diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 5cf08aea407..44cf398c407 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,7 +1,7 @@ // For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: { "name": "F#", - "image": "mcr.microsoft.com/dotnet/sdk:8.0.100-rc.1", + "image": "mcr.microsoft.com/dotnet/sdk:8.0", "features": { "ghcr.io/devcontainers/features/common-utils:2": {}, "ghcr.io/devcontainers/features/git:1": {}, diff --git a/.fantomasignore b/.fantomasignore index 4830d0af77d..d142916bd16 100644 --- a/.fantomasignore +++ b/.fantomasignore @@ -20,7 +20,6 @@ src/Compiler/Checking/AccessibilityLogic.fs src/Compiler/Checking/AttributeChecking.fs src/Compiler/Checking/AugmentWithHashCompare.fs src/Compiler/Checking/CheckBasics.fs -src/Compiler/Checking/CheckComputationExpressions.fs src/Compiler/Checking/CheckDeclarations.fs src/Compiler/Checking/CheckExpressions.fs src/Compiler/Checking/CheckFormatStrings.fs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000000..d8110ac0ace --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Format src/Compiler/Checking/CheckComputationExpressions.fs, https://github.com/dotnet/fsharp/pull/16512 +603a310cdfd9902ec1d29b399377dcc9ac56235b diff --git a/DEVGUIDE.md b/DEVGUIDE.md index e605c711dba..e5480e39ae0 100644 --- a/DEVGUIDE.md +++ b/DEVGUIDE.md @@ -275,9 +275,25 @@ Where `` corresponds to the latest Visual Studio version on your machin Use the `Debug` configuration to test your changes locally. It is the default. Do not use the `Release` configuration! Local development and testing of Visual Studio tooling is not designed for the `Release` configuration. -### Writing and running benchmarks +### Benchmarking -Existing compiler benchmarks can be found in `tests\benchmarks\`. +Existing compiler benchmarks can be found in `tests\benchmarks\`. The folder contains READMEs describing specific benchmark projects as well as guidelines for creating new benchmarks. There is also `FSharp.Benchmarks.sln` solution containing all the benchmark project and their dependencies. + +To exercise the benchmarking infrastructure locally, run: + +(Windows) +```cmd +build.cmd -configuration Release -testBenchmarks +``` + +(Linux/Mac) +```shell +./build.sh --configuration Release --testBenchmarks +``` + +This is executed in CI as well. It does the following: +- builds all the benchmarking projects +- does smoke testing for fast benchmarks (executes them once to check they don't fail in the runtime) ### Benchmarking and profiling the compiler @@ -286,151 +302,6 @@ Existing compiler benchmarks can be found in `tests\benchmarks\`. * Always build both versions of compiler/FCS from source and not use pre-built binaries from SDK (SDK binaries are crossgen'd, which can affect performance). * To run `Release` build of compiler/FCS. -### Example benchmark setup using [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) - -1. Perform a clean build of the compiler and FCS from source (as described in this document, build can be done with `-noVisualStudio` in case if FCS/FSharp.Core is being benchmarked/profiled). - -2. Create a benchmark project (in this example, the project will be created in `tests\benchmarks\FCSBenchmarks`). - - ```shell - cd tests\benchmarks\FCSBenchmarks - dotnet new console -o FcsBench --name FcsBench -lang F# - ``` - -3. Add needed packages and project references. - - ```shell - cd FcsBench - dotnet add package BenchmarkDotNet - dotnet add reference ..\..\..\src\Compiler\FSharp.Compiler.Service.fsproj - ``` - -4. Additionally, if you want to test changes to the FSharp.Core (note that the relative path can be different) - - ```shell - dotnet add reference ..\..\..\src\FSharp.Core\FSharp.Core.fsproj - ``` - - > as well as the following property have to be added to `FcsBench.fsproj`: - - ```xml - - true - - ``` - -5. Add a new benchmark for FCS/FSharp.Core by editing `Program.fs`. - - ```fsharp - open System.IO - open FSharp.Compiler.CodeAnalysis - open FSharp.Compiler.Diagnostics - open FSharp.Compiler.Text - open BenchmarkDotNet.Attributes - open BenchmarkDotNet.Running - - [] - type CompilerService() = - let mutable checkerOpt = None - let mutable sourceOpt = None - - let parsingOptions = - { - SourceFiles = [|"CheckExpressions.fs"|] - ConditionalDefines = [] - DiagnosticOptions = FSharpDiagnosticOptions.Default - LangVersionText = "default" - IsInteractive = false - LightSyntax = None - CompilingFsLib = false - IsExe = false - } - - [] - member _.Setup() = - match checkerOpt with - | None -> - checkerOpt <- Some(FSharpChecker.Create(projectCacheSize = 200)) - | _ -> () - - match sourceOpt with - | None -> - sourceOpt <- Some <| SourceText.ofString(File.ReadAllText("""C:\Users\vlza\code\fsharp\src\Compiler\Checking\CheckExpressions.fs""")) - | _ -> () - - - [] - member _.ParsingTypeCheckerFs() = - match checkerOpt, sourceOpt with - | None, _ -> failwith "no checker" - | _, None -> failwith "no source" - | Some(checker), Some(source) -> - let results = checker.ParseFile("CheckExpressions.fs", source, parsingOptions) |> Async.RunSynchronously - if results.ParseHadErrors then failwithf "parse had errors: %A" results.Diagnostics - - [] - member _.ParsingTypeCheckerFsSetup() = - match checkerOpt with - | None -> failwith "no checker" - | Some(checker) -> - checker.InvalidateAll() - checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() - checker.ParseFile("dummy.fs", SourceText.ofString "dummy", parsingOptions) |> Async.RunSynchronously |> ignore - - [] - let main _ = - BenchmarkRunner.Run() |> ignore - 0 - ``` - - > For more detailed information about available BenchmarkDotNet options, please refer to [BenchmarkDotNet Documentation](https://benchmarkdotnet.org/articles/overview.html). - -6. Build and run the benchmark. - - ```shell - dotnet build -c Release - dotnet run -c Release - ``` - -7. You can find results in `.\BenchmarkDotNet.Artifacts\results\` in the current benchmark project directory. - - ```shell - > ls .\BenchmarkDotNet.Artifacts\results\ - - Directory: C:\Users\vlza\code\fsharp\tests\benchmarks\FCSBenchmarks\FcsBench\BenchmarkDotNet.Artifacts\results - - Mode LastWriteTime Length Name - ---- ------------- ------ ---- - -a--- 4/25/2022 1:42 PM 638 Program.CompilerService-report-github.md - -a--- 4/25/2022 1:42 PM 1050 Program.CompilerService-report.csv - -a--- 4/25/2022 1:42 PM 1169 Program.CompilerService-report.html - ``` - - > *-report-github.md can be used to post benchmark results to GitHub issue/PR/discussion or RFC. - > - >*-report.csv can be used for comparison purposes. - - **Example output:** - - ``` ini - - BenchmarkDotNet=v0.13.1, OS=Windows 10.0.25102 - Intel Core i7-8750H CPU 2.20GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores - .NET SDK=6.0.200 - [Host] : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT DEBUG - Job-GDIBXX : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT - - InvocationCount=1 UnrollFactor=1 - - ``` - - | Method | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Allocated | - |--------------------- |---------:|--------:|--------:|---------:|----------:|----------:|----------:| - | ParsingTypeCheckerFs | 199.4 ms | 3.84 ms | 9.78 ms | 195.5 ms | 4000.0000 | 1000.0000 | 28 MB | - -8. Repeat for any number of changes you would like to test. -9. **Optionally:** benchmark code and results can be included as part of the PR for future reference. - ## Additional resources The primary technical guide to the core compiler code is [The F# Compiler Technical Guide](https://github.com/dotnet/fsharp/blob/main/docs/index.md). Please read and contribute to that guide. diff --git a/Directory.Build.props b/Directory.Build.props index 879bd89410f..0fe610fc267 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,6 +16,10 @@ true + + true + + true @@ -24,6 +28,8 @@ + true + true false true true diff --git a/Directory.Build.targets b/Directory.Build.targets index 5952e0ed670..079e0e68fd4 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -13,7 +13,4 @@ - - - diff --git a/FSharp.Benchmarks.sln b/FSharp.Benchmarks.sln index 04af0d6830d..e04c208232f 100644 --- a/FSharp.Benchmarks.sln +++ b/FSharp.Benchmarks.sln @@ -1,4 +1,5 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32113.165 MinimumVisualStudioVersion = 10.0.40219.1 @@ -24,10 +25,10 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Compiler.Benchmarks" EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FCSSourceFiles", "tests\benchmarks\FCSBenchmarks\FCSSourceFiles\FCSSourceFiles.fsproj", "{0E2A7B27-3AD3-4C1D-BA0D-008A1200946F}" EndProject -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fsharp.ProfilingStartpointProject", "tests\benchmarks\Fsharp.ProfilingStartpointProject\Fsharp.ProfilingStartpointProject.fsproj", "{9F27346B-2FC6-4FD5-A932-4E80F331E6D6}" -EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Test.Utilities", "tests\FSharp.Test.Utilities\FSharp.Test.Utilities.fsproj", "{0B149238-0912-493E-8877-F831AE01B942}" EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Benchmarks.Common", "tests\benchmarks\FSharp.Benchmarks.Common\FSharp.Benchmarks.Common.fsproj", "{62DED1EA-6A33-4537-8ED2-118462D0FEE5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -105,18 +106,20 @@ Global {0E2A7B27-3AD3-4C1D-BA0D-008A1200946F}.Release|Any CPU.Build.0 = Release|Any CPU {0E2A7B27-3AD3-4C1D-BA0D-008A1200946F}.ReleaseCompressed|Any CPU.ActiveCfg = Debug|Any CPU {0E2A7B27-3AD3-4C1D-BA0D-008A1200946F}.Proto|Any CPU.ActiveCfg = Debug|Any CPU - {9F27346B-2FC6-4FD5-A932-4E80F331E6D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9F27346B-2FC6-4FD5-A932-4E80F331E6D6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9F27346B-2FC6-4FD5-A932-4E80F331E6D6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9F27346B-2FC6-4FD5-A932-4E80F331E6D6}.Release|Any CPU.Build.0 = Release|Any CPU - {9F27346B-2FC6-4FD5-A932-4E80F331E6D6}.ReleaseCompressed|Any CPU.ActiveCfg = Debug|Any CPU - {9F27346B-2FC6-4FD5-A932-4E80F331E6D6}.Proto|Any CPU.ActiveCfg = Debug|Any CPU {0B149238-0912-493E-8877-F831AE01B942}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0B149238-0912-493E-8877-F831AE01B942}.Debug|Any CPU.Build.0 = Debug|Any CPU {0B149238-0912-493E-8877-F831AE01B942}.Release|Any CPU.ActiveCfg = Release|Any CPU {0B149238-0912-493E-8877-F831AE01B942}.Release|Any CPU.Build.0 = Release|Any CPU {0B149238-0912-493E-8877-F831AE01B942}.ReleaseCompressed|Any CPU.ActiveCfg = Debug|Any CPU {0B149238-0912-493E-8877-F831AE01B942}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.Proto|Any CPU.Build.0 = Debug|Any CPU + {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.Release|Any CPU.Build.0 = Release|Any CPU + {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.ReleaseCompressed|Any CPU.ActiveCfg = Release|Any CPU + {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.ReleaseCompressed|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FSharp.Compiler.Service.sln b/FSharp.Compiler.Service.sln index ca14b9f9e06..45997f8a6db 100644 --- a/FSharp.Compiler.Service.sln +++ b/FSharp.Compiler.Service.sln @@ -61,6 +61,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Compiler.Interactive EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.UnitTests", "tests\FSharp.Compiler.UnitTests\FSharp.Compiler.UnitTests.fsproj", "{0C0BDAF4-7D47-4BDA-9992-077F63D6B494}" EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Benchmarks.Common", "tests\benchmarks\FSharp.Benchmarks.Common\FSharp.Benchmarks.Common.fsproj", "{A7ACFD1F-D1B8-483A-A210-200BB6B4BD7E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -143,6 +145,10 @@ Global {0C0BDAF4-7D47-4BDA-9992-077F63D6B494}.Debug|Any CPU.Build.0 = Debug|Any CPU {0C0BDAF4-7D47-4BDA-9992-077F63D6B494}.Release|Any CPU.ActiveCfg = Release|Any CPU {0C0BDAF4-7D47-4BDA-9992-077F63D6B494}.Release|Any CPU.Build.0 = Release|Any CPU + {A7ACFD1F-D1B8-483A-A210-200BB6B4BD7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7ACFD1F-D1B8-483A-A210-200BB6B4BD7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7ACFD1F-D1B8-483A-A210-200BB6B4BD7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7ACFD1F-D1B8-483A-A210-200BB6B4BD7E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -154,6 +160,7 @@ Global {35F5F1C5-AE4F-4B5A-8D94-1AF708724FD5} = {AF321816-B4A0-41DD-9A1D-484E8A20C6F6} {C1950E28-1CB7-4DEC-BB3A-8A0443A17282} = {AF321816-B4A0-41DD-9A1D-484E8A20C6F6} {07CD957A-3C31-4F75-A735-16CE72E1BD71} = {AF321816-B4A0-41DD-9A1D-484E8A20C6F6} + {A7ACFD1F-D1B8-483A-A210-200BB6B4BD7E} = {AF321816-B4A0-41DD-9A1D-484E8A20C6F6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F9A60F3B-D894-4C8E-BA0F-C51115B25A5A} diff --git a/FSharp.sln b/FSharp.sln index c021f7026b4..5ce2e4eeb35 100644 --- a/FSharp.sln +++ b/FSharp.sln @@ -106,6 +106,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution src\Compiler\FSCompCheck.fsx = src\Compiler\FSCompCheck.fsx EndProjectSection EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Benchmarks.Common", "tests\benchmarks\FSharp.Benchmarks.Common\FSharp.Benchmarks.Common.fsproj", "{7D482560-DF6F-46A5-B50C-20ECF7C38759}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -416,6 +418,18 @@ Global {9C7523BA-7AB2-4604-A5FD-653E82C2BAD1}.Release|Any CPU.Build.0 = Release|Any CPU {9C7523BA-7AB2-4604-A5FD-653E82C2BAD1}.Release|x86.ActiveCfg = Release|Any CPU {9C7523BA-7AB2-4604-A5FD-653E82C2BAD1}.Release|x86.Build.0 = Release|Any CPU + {7D482560-DF6F-46A5-B50C-20ECF7C38759}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D482560-DF6F-46A5-B50C-20ECF7C38759}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D482560-DF6F-46A5-B50C-20ECF7C38759}.Debug|x86.ActiveCfg = Debug|Any CPU + {7D482560-DF6F-46A5-B50C-20ECF7C38759}.Debug|x86.Build.0 = Debug|Any CPU + {7D482560-DF6F-46A5-B50C-20ECF7C38759}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {7D482560-DF6F-46A5-B50C-20ECF7C38759}.Proto|Any CPU.Build.0 = Debug|Any CPU + {7D482560-DF6F-46A5-B50C-20ECF7C38759}.Proto|x86.ActiveCfg = Debug|Any CPU + {7D482560-DF6F-46A5-B50C-20ECF7C38759}.Proto|x86.Build.0 = Debug|Any CPU + {7D482560-DF6F-46A5-B50C-20ECF7C38759}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D482560-DF6F-46A5-B50C-20ECF7C38759}.Release|Any CPU.Build.0 = Release|Any CPU + {7D482560-DF6F-46A5-B50C-20ECF7C38759}.Release|x86.ActiveCfg = Release|Any CPU + {7D482560-DF6F-46A5-B50C-20ECF7C38759}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -447,6 +461,7 @@ Global {209C7D37-8C01-413C-8698-EC25F4C86976} = {B8DDA694-7939-42E3-95E5-265C2217C142} {BEC6E796-7E53-4888-AAFC-B8FD55C425DF} = {CE70D631-C5DC-417E-9CDA-B16097BEF1AC} {9C7523BA-7AB2-4604-A5FD-653E82C2BAD1} = {CE70D631-C5DC-417E-9CDA-B16097BEF1AC} + {7D482560-DF6F-46A5-B50C-20ECF7C38759} = {CE70D631-C5DC-417E-9CDA-B16097BEF1AC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BD5177C7-1380-40E7-94D2-7768E1A8B1B8} diff --git a/VisualFSharp.sln b/VisualFSharp.sln index b7b9e82b5dc..486cc03a163 100644 --- a/VisualFSharp.sln +++ b/VisualFSharp.sln @@ -189,12 +189,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FCSBenchmarks", "FCSBenchma tests\benchmarks\FCSBenchmarks\SmokeTestAllBenchmarks.ps1 = tests\benchmarks\FCSBenchmarks\SmokeTestAllBenchmarks.ps1 EndProjectSection EndProject -Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Fsharp.ProfilingStartpointProject", "tests\benchmarks\Fsharp.ProfilingStartpointProject\Fsharp.ProfilingStartpointProject.fsproj", "{FE23BB65-276A-4E41-8CC7-F7752241DEBA}" -EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Editor.Tests", "vsintegration\tests\FSharp.Editor.Tests\FSharp.Editor.Tests.fsproj", "{CBC96CC7-65AB-46EA-A82E-F6A788DABF80}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.Editor.IntegrationTests", "vsintegration\tests\FSharp.Editor.IntegrationTests\FSharp.Editor.IntegrationTests.csproj", "{E31F9B59-FCF1-4D04-8762-C7BB60285A7B}" EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Benchmarks.Common", "tests\benchmarks\FSharp.Benchmarks.Common\FSharp.Benchmarks.Common.fsproj", "{6734FC6F-B5F3-45E1-9A72-720378BB49C9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -997,18 +997,6 @@ Global {583182E1-3484-4A8F-AC06-7C0D232C0CA4}.Release|Any CPU.Build.0 = Release|Any CPU {583182E1-3484-4A8F-AC06-7C0D232C0CA4}.Release|x86.ActiveCfg = Release|Any CPU {583182E1-3484-4A8F-AC06-7C0D232C0CA4}.Release|x86.Build.0 = Release|Any CPU - {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Debug|x86.ActiveCfg = Debug|Any CPU - {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Debug|x86.Build.0 = Debug|Any CPU - {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Proto|Any CPU.ActiveCfg = Debug|Any CPU - {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Proto|Any CPU.Build.0 = Debug|Any CPU - {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Proto|x86.ActiveCfg = Debug|Any CPU - {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Proto|x86.Build.0 = Debug|Any CPU - {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Release|Any CPU.Build.0 = Release|Any CPU - {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Release|x86.ActiveCfg = Release|Any CPU - {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Release|x86.Build.0 = Release|Any CPU {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Debug|Any CPU.Build.0 = Debug|Any CPU {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -1033,6 +1021,18 @@ Global {E31F9B59-FCF1-4D04-8762-C7BB60285A7B}.Release|Any CPU.Build.0 = Release|Any CPU {E31F9B59-FCF1-4D04-8762-C7BB60285A7B}.Release|x86.ActiveCfg = Release|Any CPU {E31F9B59-FCF1-4D04-8762-C7BB60285A7B}.Release|x86.Build.0 = Release|Any CPU + {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Debug|x86.ActiveCfg = Debug|Any CPU + {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Debug|x86.Build.0 = Debug|Any CPU + {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Proto|Any CPU.Build.0 = Debug|Any CPU + {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Proto|x86.ActiveCfg = Debug|Any CPU + {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Proto|x86.Build.0 = Debug|Any CPU + {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Release|Any CPU.Build.0 = Release|Any CPU + {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Release|x86.ActiveCfg = Release|Any CPU + {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1111,9 +1111,9 @@ Global {EB015235-1E07-4CDA-9CC6-3FBCC27910D1} = {B8DDA694-7939-42E3-95E5-265C2217C142} {583182E1-3484-4A8F-AC06-7C0D232C0CA4} = {39CDF34B-FB23-49AE-AB27-0975DA379BB5} {39CDF34B-FB23-49AE-AB27-0975DA379BB5} = {DFB6ADD7-3149-43D9-AFA0-FC4A818B472B} - {FE23BB65-276A-4E41-8CC7-F7752241DEBA} = {39CDF34B-FB23-49AE-AB27-0975DA379BB5} {CBC96CC7-65AB-46EA-A82E-F6A788DABF80} = {F7876C9B-FB6A-4EFB-B058-D6967DB75FB2} {E31F9B59-FCF1-4D04-8762-C7BB60285A7B} = {F7876C9B-FB6A-4EFB-B058-D6967DB75FB2} + {6734FC6F-B5F3-45E1-9A72-720378BB49C9} = {DFB6ADD7-3149-43D9-AFA0-FC4A818B472B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {48EDBBBE-C8EE-4E3C-8B19-97184A487B37} diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 94bba597e8a..ec7f62c674b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,4 +1,4 @@ -# CI and PR triggers +# CI and PR triggers trigger: branches: include: @@ -86,11 +86,11 @@ stages: # Signed build # #-------------------------------------------------------------------------------------------------------------------# - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/release/dev17.9') }}: + - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/release/dev17.10') }}: - template: /eng/common/templates/job/onelocbuild.yml parameters: MirrorRepo: fsharp - MirrorBranch: release/dev17.9 + MirrorBranch: release/dev17.10 LclSource: lclFilesfromPackage LclPackageId: 'LCL-JUNO-PROD-FSHARP' - template: /eng/common/templates/jobs/jobs.yml @@ -110,13 +110,10 @@ stages: demands: ImageOverride -equals windows.vs2022preview.amd64 timeoutInMinutes: 300 variables: - - group: DotNet-Blob-Feed - group: DotNet-Symbol-Server-Pats - group: DotNet-DevDiv-Insertion-Workflow-Variables - name: _SignType value: Real - - name: _DotNetPublishToBlobFeed - value: true steps: - checkout: self clean: true @@ -131,9 +128,6 @@ stages: /p:MicroBuild_SigningEnabled=true /p:OverridePackageSource=https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json /p:TeamName=$(_TeamName) - /p:DotNetPublishBlobFeedKey=$(dotnetfeed-storage-access-key-1) - /p:DotNetPublishBlobFeedUrl=https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json - /p:DotNetPublishToBlobFeed=true /p:DotNetPublishUsingPipelines=$(_PublishUsingPipelines) /p:DotNetArtifactsCategory=$(_DotNetArtifactsCategory) /p:DotNetSymbolServerTokenMsdl=$(microsoft-symbol-server-pat) @@ -713,6 +707,22 @@ stages: continueOnError: true condition: always() + # Build and run fast benchmarks + - job: Benchmarks + pool: + name: $(DncEngPublicBuildPool) + demands: ImageOverride -equals $(WindowsMachineQueueName) + variables: + - name: _BuildConfig + value: Release + steps: + - checkout: self + clean: true + - script: eng\CIBuild.cmd -configuration $(_BuildConfig) -testBenchmarks + displayName: Smoke test fast benchmarks + continueOnError: true + condition: always() + # Test trimming on Windows - job: Build_And_Test_Trimming_Windows pool: @@ -806,8 +816,8 @@ stages: - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - template: eng/release/insert-into-vs.yml parameters: - componentBranchName: refs/heads/release/dev17.9 - insertTargetBranch: rel/d17.9 + componentBranchName: refs/heads/release/dev17.10 + insertTargetBranch: main insertTeamEmail: fsharpteam@microsoft.com insertTeamName: 'F#' completeInsertion: 'auto' diff --git a/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt b/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt index 9ea06757477..07ec0379282 100644 --- a/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt +++ b/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt @@ -1,6 +1,6 @@ FSharp.Build.UnitTests.dll +FSharp.Benchmarks.Common.dll FSharp.Compiler.Benchmarks.dll -Fsharp.ProfilingStartpointProject.dll FSharp.Compiler.ComponentTests.dll FSharp.Test.Utilities.dll FSharp.Compiler.Private.Scripting.UnitTests.dll diff --git a/docs/fcs/syntax-visitor.fsx b/docs/fcs/syntax-visitor.fsx deleted file mode 100644 index f0ea0316cfb..00000000000 --- a/docs/fcs/syntax-visitor.fsx +++ /dev/null @@ -1,189 +0,0 @@ -(** ---- -title: Tutorial: SyntaxVisitorBase -category: FSharp.Compiler.Service -categoryindex: 300 -index: 301 ---- -*) -(*** hide ***) -#I "../../artifacts/bin/FSharp.Compiler.Service/Debug/netstandard2.0" -(** -Compiler Services: Using the SyntaxVisitorBase -========================================= - -Syntax tree traversal is a common topic when interacting with the `FSharp.Compiler.Service`. -As established in [Tutorial: Expressions](./untypedtree.html#Walking-over-the-AST), the [ParsedInput](../reference/fsharp-compiler-syntax-parsedinput.html) can be traversed by a set of recursive functions. -It can be tedious to always construct these functions from scratch. - -As an alternative, a [SyntaxVisitorBase](../reference/fsharp-compiler-syntax-syntaxvisitorbase-1.html) can be used to traverse the syntax tree. -Consider, the following code sample: -*) - -let codeSample = """ -module Lib - -let myFunction paramOne paramTwo = - () -""" - -(** -Imagine we wish to grab the `myFunction` name from the `headPat` in the [SynBinding](../reference/fsharp-compiler-syntax-synbinding.html). -Let's introduce a helper function to construct the AST: -*) - -#r "FSharp.Compiler.Service.dll" -open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.Text -open FSharp.Compiler.Syntax - -let checker = FSharpChecker.Create() - -/// Helper to construct an ParsedInput from a code snippet. -let mkTree codeSample = - let parseFileResults = - checker.ParseFile( - "FileName.fs", - SourceText.ofString codeSample, - { FSharpParsingOptions.Default with SourceFiles = [| "FileName.fs" |] } - ) - |> Async.RunSynchronously - - parseFileResults.ParseTree - -(** -And create a visitor to traverse the tree: -*) - -let visitor = - { new SyntaxVisitorBase() with - override this.VisitPat(path, defaultTraverse, synPat) = - // First check if the pattern is what we are looking for. - match synPat with - | SynPat.LongIdent(longDotId = SynLongIdent(id = [ ident ])) -> - // Next we can check if the current path of visited nodes, matches our expectations. - // The path will contain all the ancestors of the current node. - match path with - // The parent node of `synPat` should be a `SynBinding`. - | SyntaxNode.SynBinding _ :: _ -> - // We return a `Some` option to indicate we found what we are looking for. - Some ident.idText - // If the parent is something else, we can skip it here. - | _ -> None - | _ -> None } - -let result = SyntaxTraversal.Traverse(Position.pos0, mkTree codeSample, visitor) // Some "myFunction" - -(** -Instead of traversing manually from `ParsedInput` to `SynModuleOrNamespace` to `SynModuleDecl.Let` to `SynBinding` to `SynPat`, we leverage the default navigation that happens in `SyntaxTraversal.Traverse`. -A `SyntaxVisitorBase` will shortcut all other code paths once a single `VisitXYZ` override has found anything. - -Our code sample of course only had one let binding and thus we didn't need to specify any further logic whether to differentiate between multiple bindings. -Let's consider a second example where we know the user's cursor inside an IDE is placed after `c` and we are interested in the body expression of the let binding. -*) - -let secondCodeSample = """ -module X - -let a = 0 -let b = 1 -let c = 2 -""" - -let secondVisitor = - { new SyntaxVisitorBase() with - override this.VisitBinding(path, defaultTraverse, binding) = - match binding with - | SynBinding(expr = e) -> Some e } - -let cursorPos = Position.mkPos 6 5 - -let secondResult = - SyntaxTraversal.Traverse(cursorPos, mkTree secondCodeSample, secondVisitor) // Some (Const (Int32 2, (6,8--6,9))) - -(** -Due to our passed cursor position, we did not need to write any code to exclude the expressions of the other let bindings. -`SyntaxTraversal.Traverse` will check whether the current position is inside any syntax node before drilling deeper. - -Lastly, some `VisitXYZ` overrides can contain a defaultTraverse. This helper allows you to continue the default traversal when you currently hit a node that is not of interest. -Consider `1 + 2 + 3 + 4`, this will be reflected in a nested infix application expression. -If the cursor is at the end of the entire expression, we can grab the value of `4` using the following visitor: -*) - -let thirdCodeSample = "let sum = 1 + 2 + 3 + 4" - -(* -AST will look like: - -Let - (false, - [SynBinding - (None, Normal, false, false, [], - PreXmlDoc ((1,0), Fantomas.FCS.Xml.XmlDocCollector), - SynValData - (None, SynValInfo ([], SynArgInfo ([], false, None)), None, - None), - Named (SynIdent (sum, None), false, None, (1,4--1,7)), None, - App - (NonAtomic, false, - App - (NonAtomic, true, - LongIdent - (false, - SynLongIdent - ([op_Addition], [], [Some (OriginalNotation "+")]), - None, (1,20--1,21)), - App - (NonAtomic, false, - App - (NonAtomic, true, - LongIdent - (false, - SynLongIdent - ([op_Addition], [], - [Some (OriginalNotation "+")]), None, - (1,16--1,17)), - App - (NonAtomic, false, - App - (NonAtomic, true, - LongIdent - (false, - SynLongIdent - ([op_Addition], [], - [Some (OriginalNotation "+")]), None, - (1,12--1,13)), - Const (Int32 1, (1,10--1,11)), (1,10--1,13)), - Const (Int32 2, (1,14--1,15)), (1,10--1,15)), - (1,10--1,17)), Const (Int32 3, (1,18--1,19)), - (1,10--1,19)), (1,10--1,21)), - Const (Int32 4, (1,22--1,23)), (1,10--1,23)), (1,4--1,7), - Yes (1,0--1,23), { LeadingKeyword = Let (1,0--1,3) - InlineKeyword = None - EqualsRange = Some (1,8--1,9) }) -*) - -let thirdCursorPos = Position.mkPos 1 22 - -let thirdVisitor = - { new SyntaxVisitorBase() with - override this.VisitExpr(path, traverseSynExpr, defaultTraverse, synExpr) = - match synExpr with - | SynExpr.Const (constant = SynConst.Int32 v) -> Some v - // We do want to continue to traverse when nodes like `SynExpr.App` are found. - | otherExpr -> defaultTraverse otherExpr } - -let thirdResult = - SyntaxTraversal.Traverse(cursorPos, mkTree thirdCodeSample, thirdVisitor) // Some 4 - -(** -`defaultTraverse` is especially useful when you do not know upfront what syntax tree you will be walking. -This is a common case when dealing with IDE tooling. You won't know what actual code the end-user is currently processing. - -**Note: SyntaxVisitorBase is designed to find a single value inside a tree!** -This is not an ideal solution when you are interested in all nodes of certain shape. -It will always verify if the given cursor position is still matching the range of the node. -As a fallback the first branch will be explored when you pass `Position.pos0`. -By design, it is meant to find a single result. - -*) diff --git a/docs/fcs/untypedtree-apis.fsx b/docs/fcs/untypedtree-apis.fsx new file mode 100644 index 00000000000..c713ad8acff --- /dev/null +++ b/docs/fcs/untypedtree-apis.fsx @@ -0,0 +1,553 @@ +(** +--- +title: Tutorial: AST APIs +category: FSharp.Compiler.Service +categoryindex: 300 +index: 301 +--- +*) +(*** hide ***) +#I "../../artifacts/bin/FSharp.Compiler.Service/Debug/netstandard2.0" +(** +Compiler Services: APIs for the untyped AST +========================================= + +## The ParsedInput module + +As established in [Tutorial: Expressions](./untypedtree.html#Walking-over-the-AST), the AST held in a [`ParsedInput`](../reference/fsharp-compiler-syntax-parsedinput.html) value +can be traversed by a set of recursive functions. It can be tedious and error-prone to write these functions from scratch every time, though, +so the [`ParsedInput` module](../reference/fsharp-compiler-syntax-parsedinputmodule.html) +exposes a number of functions to make common operations easier. + +For example: + +- [`ParsedInput.exists`](../reference/fsharp-compiler-syntax-parsedinputmodule.html#exists) + - May be used by tooling to determine whether the user's cursor is in a certain context, e.g., to determine whether to offer a certain tooling action. +- [`ParsedInput.fold`](../reference/fsharp-compiler-syntax-parsedinputmodule.html#fold) + - May be used when writing analyzers to collect diagnostic information for an entire source file. +- [`ParsedInput.foldWhile`](../reference/fsharp-compiler-syntax-parsedinputmodule.html#foldWhile) + - Like `fold` but supports stopping traversal early. +- [`ParsedInput.tryNode`](../reference/fsharp-compiler-syntax-parsedinputmodule.html#tryNode) + - May be used by tooling to get the last (deepest) node under the user's cursor. +- [`ParsedInput.tryPick`](../reference/fsharp-compiler-syntax-parsedinputmodule.html#tryPick) + - May be used by tooling to find the first (shallowest) matching node near the user's cursor. +- [`ParsedInput.tryPickLast`](../reference/fsharp-compiler-syntax-parsedinputmodule.html#tryPickLast) + - May be used by tooling to find the last (deepest) matching node near the user's cursor. + +## SyntaxVisitorBase & SyntaxTraversal.Traverse + +While the `ParsedInput` module functions are usually the simplest way to meet most needs, +there is also a [`SyntaxVisitorBase`](../reference/fsharp-compiler-syntax-syntaxvisitorbase-1.html)-based API that can +provide somewhat more fine-grained control over syntax traversal for a subset of use-cases at the expense of a bit more +ceremony and complexity. + +## Examples + +Let's start by introducing a helper function for constructing an AST from source code so we can run through some real examples: +*) + +#r "FSharp.Compiler.Service.dll" +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.Text +open FSharp.Compiler.Syntax + +let checker = FSharpChecker.Create() + +/// A helper for constructing a `ParsedInput` from a code snippet. +let mkTree codeSample = + let parseFileResults = + checker.ParseFile( + "FileName.fs", + SourceText.ofString codeSample, + { FSharpParsingOptions.Default with SourceFiles = [| "FileName.fs" |] } + ) + |> Async.RunSynchronously + + parseFileResults.ParseTree + +(** +### ParsedInput.exists + +Now consider the following code sample: +*) + +let brokenTypeDefn = """ +module Lib + +// Whoops, we forgot the equals sign. +type T { A: int; B: int } +""" + +(** +Let's say we have a code fix for adding an equals sign to a type definition that's missing one—like the one above. +We want to offer the fix when the user's cursor is inside of—or just after—the broken type definition. + +We can determine this by using `ParsedInput.exists` and passing in the position of the user's cursor: +*) + +// type T { A: int; B: int } +// ···········↑ +let posInMiddleOfTypeDefn = Position.mkPos 5 12 + +(** +Given that cursor position, all we need to do is find a `SynTypeDefn` node: +*) + +let isPosInTypeDefn = // true. + (posInMiddleOfTypeDefn, mkTree brokenTypeDefn) + ||> ParsedInput.exists (fun _path node -> + match node with + | SyntaxNode.SynTypeDefn _ -> true + | _ -> false) + +(** +If the position passed into `ParsedInput.exists` is not contained in any node in the given AST, +but rather is below or to the right of all nodes, `ParsedInput.exists` will fall back to exploring the nearest branch above +and/or to the left. This is useful because the user's cursor may lie beyond the range of all nodes. +*) + +// type T { A: int; B: int } +// ··························↑ +let posAfterTypeDefn = Position.mkPos 5 28 + +(** +Our function still returns `true` if the cursor is past the end of the type definition node itself: +*) + +let isPosInTypeDefn' = // Still true. + (posAfterTypeDefn, mkTree brokenTypeDefn) + ||> ParsedInput.exists (fun _path node -> + match node with + | SyntaxNode.SynTypeDefn _ -> true + | _ -> false) + +(** +### ParsedInput.fold + +`ParsedInput.fold` can be useful when writing an analyzer to collect diagnostics from entire input files. +*) + +(*** hide ***) +let getLineStr (line: int) : string = failwith "Nope." + +(** +Take this code that has unnecessary parentheses in both patterns and expressions: +*) + +let unnecessaryParentheses = """ +let (x) = (id (3)) +""" + +(** +We can gather the ranges of all unnecessary parentheses like this: +*) + +open System.Collections.Generic + +module HashSet = + let add item (set: HashSet<_>) = + ignore (set.Add item) + set + +let unnecessaryParenthesesRanges = + (HashSet Range.comparer, mkTree unnecessaryParentheses) ||> ParsedInput.fold (fun ranges path node -> + match node with + | SyntaxNode.SynExpr(SynExpr.Paren(expr = inner; rightParenRange = Some _; range = range)) when + not (SynExpr.shouldBeParenthesizedInContext getLineStr path inner) + -> + ranges |> HashSet.add range + + | SyntaxNode.SynPat(SynPat.Paren(inner, range)) when + not (SynPat.shouldBeParenthesizedInContext path inner) + -> + ranges |> HashSet.add range + + | _ -> + ranges) + +(** +### ParsedInput.tryNode + +Sometimes, we might just want to get whatever node is directly at a given position—for example, if the user's +cursor is on an argument of a function being applied, we can find the node representing the argument and use its path +to backtrack and find the function's name. +*) + +let functionApplication = """ +f x y +""" + +(** +If we have our cursor on `y`: +*) + +// f x y +// ·····↑ +let posOnY = Position.mkPos 2 5 + +(** +The syntax node representing the function `f` technically contains the cursor's position, +but `ParsedInput.tryNode` will keep diving until it finds the _deepest_ node containing the position. + +We can thus get the node representing `y` and its ancestors (the `path`) like this: +*) + +let yAndPath = // Some (SynExpr (Ident y), [SynExpr (App …); …]) + mkTree functionApplication + |> ParsedInput.tryNode posOnY + +(** +Note that, unlike `ParsedInput.exists`, `ParsedInput.tryPick`, and `ParsedInput.tryPickLast`, +`ParsedInput.tryNode` does _not_ fall back to the nearest branch above or to the left. +*) + +// f x y +// ······↑ +let posAfterY = Position.mkPos 2 8 + +(** +If we take the same code snippet but pass in a position after `y`, +we get no node: +*) + +let nope = // None. + mkTree functionApplication + |> ParsedInput.tryNode posAfterY + +(** +### ParsedInput.tryPick + +Now imagine that we have a code fix for converting a record construction expression into an anonymous record construction +expression when there is no record type in scope whose fields match. +*) + +let recordExpr = """ +let r = { A = 1; B = 2 } +""" + +(** +We can offer this fix when the user's cursor is inside of a record expression by +using `ParsedInput.tryPick` to return the surrounding record expression's range, if any. +*) + +// let r = { A = 1; B = 2 } +// ······················↑ +let posInRecordExpr = Position.mkPos 2 25 + +(** +Here, even though `ParsedInput.tryPick` will try to cleave to the given position by default, +we want to verify that the record expression node that we've come across actually contains the position, +since, like `ParsedInput.exists`, `ParsedInput.tryPick` will also fall back to the nearest branch above and/or +to the left if no node actually contains the position. In this case, we don't want to offer the code fix +if the user's cursor isn't actually inside of the record expression. +*) + +let recordExprRange = // Some (2,8--2,24). + (posInRecordExpr, mkTree recordExpr) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynExpr(SynExpr.Record(range = range)) when + Range.rangeContainsPos range posInRecordExpr + -> Some range + | _ -> None) + +(** +We might also sometimes want to make use of the `path` parameter. Take this simple function definition: +*) + +let myFunction = """ +module Lib + +let myFunction paramOne paramTwo = + () +""" + +(** +Imagine we want to grab the `myFunction` name from the `headPat` in the [`SynBinding`](../reference/fsharp-compiler-syntax-synbinding.html). + +We can write a function to match the node we're looking for—and _not_ match anything we're _not_ looking for (like the argument patterns)—by taking its path into account: +*) + +let myFunctionId = // Some "myFunction". + (Position.pos0, mkTree myFunction) + ||> ParsedInput.tryPick (fun path node -> + // Match on the node and the path (the node's ancestors) to see whether: + // 1. The node is a pattern. + // 2. The pattern is a long identifier pattern. + // 3. The pattern's parent node (the head of the path) is a binding. + match node, path with + | SyntaxNode.SynPat(SynPat.LongIdent(longDotId = SynLongIdent(id = [ ident ]))), + SyntaxNode.SynBinding _ :: _ -> + // We have found what we're looking for. + Some ident.idText + | _ -> + // If the node or its context don't match, + // we continue. + None) + +(** +Instead of traversing manually from `ParsedInput` to `SynModuleOrNamespace` to `SynModuleDecl.Let` to `SynBinding` to `SynPat`, we leverage the default navigation that happens in `ParsedInput.tryPick`. +`ParsedInput.tryPick` will short-circuit once we have indicated that we have found what we're looking for by returning `Some value`. + +Our code sample of course only had one let-binding and thus we didn't need to specify any further logic to differentiate between bindings. + +Let's consider a second example involving multiple let-bindings: +*) + +let multipleLetsInModule = """ +module X + +let a = 0 +let b = 1 +let c = 2 +""" + +(** +In this case, we know the user's cursor inside an IDE is placed after `c`, and we are interested in the body expression of the _last_ let-binding. +*) + +// … +// let c = 2 +// ·····↑ +let posInLastLet = Position.mkPos 6 5 + +(** +Thanks to the cursor position we passed in, we do not need to write any code to exclude the expressions of the sibling let-bindings. +`ParsedInput.tryPick` will check whether the current position is inside any given syntax node before drilling deeper. +*) + +let bodyOfLetContainingPos = // Some (Const (Int32 2, (6,8--6,9))). + (posInLastLet, mkTree multipleLetsInModule) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynBinding(SynBinding(expr = e)) -> Some e + | _ -> None) + +(** +As noted above, `ParsedInput.tryPick` will short-circuit at the first matching node. +`ParsedInput.tryPickLast` can be used to get the _last_ matching node that contains a given position. + +Take this example of multiple nested modules: +*) + +let nestedModules = """ +module M + +module N = + module O = + module P = begin end +""" + +(** +By using `ParsedInput.tryPick`, we'll get the name of the outermost nested module even if we pass in a position inside the innermost, +since the innermost is contained within the outermost. + +This position is inside module `P`, which is nested inside of module `O`, which is nested inside of module `N`, +which is nested inside of top-level module `M`: +*) + +// module M +// +// module N = +// module O = +// module P = begin end +// ···························↑ +let posInsideOfInnermostNestedModule = Position.mkPos 6 28 + +(** +`ParsedInput.tryPick` short-circuits on the first match, and since module `N` is the first +nested module whose range contains position (6, 28), that's the result we get. +*) + +let outermostNestedModule = // Some ["N"]. + (posInsideOfInnermostNestedModule, mkTree nestedModules) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longId))) -> + Some [for ident in longId -> ident.idText] + | _ -> None) + +(** +### ParsedInput.tryPickLast + +If however we use the same code snippet and pass the same position into `ParsedInput.tryPickLast`, +we can get the name of the _last_ (deepest or innermost) matching node: +*) + +let innermostNestedModule = // Some ["P"]. + (posInsideOfInnermostNestedModule, mkTree nestedModules) + ||> ParsedInput.tryPickLast (fun _path node -> + match node with + | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longId))) -> + Some [for ident in longId -> ident.idText] + | _ -> None) + +(** +If we want the next-to-innermost nested module, we can do likewise but make use of the `path` parameter: +*) + +let nextToInnermostNestedModule = // Some ["O"]. + (posInsideOfInnermostNestedModule, mkTree nestedModules) + ||> ParsedInput.tryPickLast (fun path node -> + match node, path with + | SyntaxNode.SynModule(SynModuleDecl.NestedModule _), + SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longId))) :: _ -> + Some [for ident in longId -> ident.idText] + | _ -> None) + +(** +### SyntaxTraversal.Traverse + +Consider again the following code sample: +*) + +let codeSample = """ +module Lib + +let myFunction paramOne paramTwo = + () +""" + +(** +Imagine we wish to grab the `myFunction` name from the `headPat` in the [SynBinding](../reference/fsharp-compiler-syntax-synbinding.html). + +We can create a visitor to traverse the tree and find the function name: +*) + +let visitor = + { new SyntaxVisitorBase() with + override this.VisitPat(path, defaultTraverse, synPat) = + // First check if the pattern is what we are looking for. + match synPat with + | SynPat.LongIdent(longDotId = SynLongIdent(id = [ ident ])) -> + // Next we can check if the current path of visited nodes, matches our expectations. + // The path will contain all the ancestors of the current node. + match path with + // The parent node of `synPat` should be a `SynBinding`. + | SyntaxNode.SynBinding _ :: _ -> + // We return a `Some` option to indicate we found what we are looking for. + Some ident.idText + // If the parent is something else, we can skip it here. + | _ -> None + | _ -> None } + +let result = SyntaxTraversal.Traverse(Position.pos0, mkTree codeSample, visitor) // Some "myFunction" + +(** +Instead of traversing manually from `ParsedInput` to `SynModuleOrNamespace` to `SynModuleDecl.Let` to `SynBinding` to `SynPat`, we leverage the default navigation that happens in `SyntaxTraversal.Traverse`. +A `SyntaxVisitorBase` will shortcut all other code paths once a single `VisitXYZ` override has found anything. + +Our code sample of course only had one let binding and thus we didn't need to specify any further logic whether to differentiate between multiple bindings. + +### SyntaxTraversal.Traverse: using position + +Let's now consider a second example where we know the user's cursor inside an IDE is placed after `c` and we are interested in the body expression of the let binding. +*) + +let secondCodeSample = """ +module X + +let a = 0 +let b = 1 +let c = 2 +""" + +let secondVisitor = + { new SyntaxVisitorBase() with + override this.VisitBinding(path, defaultTraverse, binding) = + match binding with + | SynBinding(expr = e) -> Some e } + +let cursorPos = Position.mkPos 6 5 + +let secondResult = + SyntaxTraversal.Traverse(cursorPos, mkTree secondCodeSample, secondVisitor) // Some (Const (Int32 2, (6,8--6,9))) + +(** +Due to our passed cursor position, we did not need to write any code to exclude the expressions of the other let bindings. +`SyntaxTraversal.Traverse` will check whether the current position is inside any syntax node before drilling deeper. + +### SyntaxTraversal.Traverse: using defaultTraverse + +Lastly, some `VisitXYZ` overrides can contain a defaultTraverse. This helper allows you to continue the default traversal when you currently hit a node that is not of interest. +Consider `1 + 2 + 3 + 4`, this will be reflected in a nested infix application expression. +If the cursor is at the end of the entire expression, we can grab the value of `4` using the following visitor: +*) + +let thirdCodeSample = "let sum = 1 + 2 + 3 + 4" + +(* +AST will look like: + +Let + (false, + [SynBinding + (None, Normal, false, false, [], + PreXmlDoc ((1,0), Fantomas.FCS.Xml.XmlDocCollector), + SynValData + (None, SynValInfo ([], SynArgInfo ([], false, None)), None, + None), + Named (SynIdent (sum, None), false, None, (1,4--1,7)), None, + App + (NonAtomic, false, + App + (NonAtomic, true, + LongIdent + (false, + SynLongIdent + ([op_Addition], [], [Some (OriginalNotation "+")]), + None, (1,20--1,21)), + App + (NonAtomic, false, + App + (NonAtomic, true, + LongIdent + (false, + SynLongIdent + ([op_Addition], [], + [Some (OriginalNotation "+")]), None, + (1,16--1,17)), + App + (NonAtomic, false, + App + (NonAtomic, true, + LongIdent + (false, + SynLongIdent + ([op_Addition], [], + [Some (OriginalNotation "+")]), None, + (1,12--1,13)), + Const (Int32 1, (1,10--1,11)), (1,10--1,13)), + Const (Int32 2, (1,14--1,15)), (1,10--1,15)), + (1,10--1,17)), Const (Int32 3, (1,18--1,19)), + (1,10--1,19)), (1,10--1,21)), + Const (Int32 4, (1,22--1,23)), (1,10--1,23)), (1,4--1,7), + Yes (1,0--1,23), { LeadingKeyword = Let (1,0--1,3) + InlineKeyword = None + EqualsRange = Some (1,8--1,9) }) +*) + +let thirdCursorPos = Position.mkPos 1 22 + +let thirdVisitor = + { new SyntaxVisitorBase() with + override this.VisitExpr(path, traverseSynExpr, defaultTraverse, synExpr) = + match synExpr with + | SynExpr.Const (constant = SynConst.Int32 v) -> Some v + // We do want to continue to traverse when nodes like `SynExpr.App` are found. + | otherExpr -> defaultTraverse otherExpr } + +let thirdResult = + SyntaxTraversal.Traverse(cursorPos, mkTree thirdCodeSample, thirdVisitor) // Some 4 + +(** +`defaultTraverse` is especially useful when you do not know upfront what syntax tree you will be walking. +This is a common case when dealing with IDE tooling. You won't know what actual code the end-user is currently processing. + +**Note: SyntaxVisitorBase is designed to find a single value inside a tree!** +This is not an ideal solution when you are interested in all nodes of certain shape. +It will always verify if the given cursor position is still matching the range of the node. +As a fallback the first branch will be explored when you pass `Position.pos0`. +By design, it is meant to find a single result. + +*) diff --git a/docs/release-notes/.FSharp.Compiler.Service/8.0.200.md b/docs/release-notes/.FSharp.Compiler.Service/8.0.200.md index 72037533dfd..175d90b6302 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/8.0.200.md +++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.200.md @@ -1,6 +1,5 @@ ### Fixed -* Miscellaneous fixes to parentheses analysis. ([PR #16262](https://github.com/dotnet/fsharp/pull/16262), [PR #16391](https://github.com/dotnet/fsharp/pull/16391), [PR #16370](https://github.com/dotnet/fsharp/pull/16370), [PR #16395](https://github.com/dotnet/fsharp/pull/16395), [PR #16372](https://github.com/dotnet/fsharp/pull/16372)) * Correctly handle assembly imports with public key token of 0 length. ([Issue #16359](https://github.com/dotnet/fsharp/issues/16359), [PR #16363](https://github.com/dotnet/fsharp/pull/16363)) * Range of [SynField](../reference/fsharp-compiler-syntax-synfield.html) ([PR #16357](https://github.com/dotnet/fsharp/pull/16357)) * Limit a type to 65K methods, introduce a compile-time error if any class has over approx 64K methods in generated IL. ([Issue #16398](https://github.com/dotnet/fsharp/issues/16398), [#PR 16427](https://github.com/dotnet/fsharp/pull/16427)) @@ -15,6 +14,7 @@ * Parser recovers on unfinished record declarations. ([PR #16357](https://github.com/dotnet/fsharp/pull/16357)) * `MutableKeyword` to [SynFieldTrivia](../reference/fsharp-compiler-syntaxtrivia-synfieldtrivia.html) ([PR #16357](https://github.com/dotnet/fsharp/pull/16357)) * Added support for a new parameterless constructor for `CustomOperationAttribute`, which, when applied, will use method name as keyword for custom operation in computation expression builder. ([PR #16475](https://github.com/dotnet/fsharp/pull/16475), part of implementation for [fslang-suggestions/1250](https://github.com/fsharp/fslang-suggestions/issues/1250)) +* Compiler service API for getting ranges of unnecessary parentheses. ([PR #16079](https://github.com/dotnet/fsharp/pull/16079) et seq.) ### Changed diff --git a/docs/release-notes/.FSharp.Compiler.Service/8.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/8.0.300.md new file mode 100644 index 00000000000..a0cbc71e6fa --- /dev/null +++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.300.md @@ -0,0 +1,27 @@ +### Fixed + +* Fix missing warning for recursive calls in list comprehensions. ([PR #16652](https://github.com/dotnet/fsharp/pull/16652)) +* Code generated files with > 64K methods and generated symbols crash when loaded. Use infered sequence points for debugging. ([Issue #16399](https://github.com/dotnet/fsharp/issues/16399), [#PR 16514](https://github.com/dotnet/fsharp/pull/16514)) +* `nameof Module` expressions and patterns are processed to link files in `--test:GraphBasedChecking`. ([PR #16550](https://github.com/dotnet/fsharp/pull/16550)) +* Graph Based Checking doesn't throw on invalid parsed input so it can be used for IDE scenarios ([PR #16575](https://github.com/dotnet/fsharp/pull/16575), [PR #16588](https://github.com/dotnet/fsharp/pull/16588), [PR #16643](https://github.com/dotnet/fsharp/pull/16643)) +* Keep parens for problematic exprs (`if`, `match`, etc.) in `$"{(…):N0}"`, `$"{(…),-3}"`, etc. ([PR #16578](https://github.com/dotnet/fsharp/pull/16578)) +* Fix crash in DOTNET_SYSTEM_GLOBALIZATION_INVARIANT mode [#PR 16471](https://github.com/dotnet/fsharp/pull/16471)) + + +### Added + +* The stackguard depth for ILPdbWriter.unshadowScopes can be modified via the environment variable `FSHARP_ILPdb_UnshadowScopes_StackGuardDepth`([PR #16583](https://github.com/dotnet/fsharp/pull/16583)) +* Parser recovers on complex primary constructor patterns, better tree representation for primary constructor patterns. ([PR #16425](https://github.com/dotnet/fsharp/pull/16425)) +* Name resolution: keep type vars in subsequent checks ([PR #16456](https://github.com/dotnet/fsharp/pull/16456)) +* Higher-order-function-based API for working with the untyped abstract syntax tree. ([PR #16462](https://github.com/dotnet/fsharp/pull/16462)) +* Allow returning bool instead of unit option for partial active patterns. ([Language suggestion #1041](https://github.com/fsharp/fslang-suggestions/issues/1041), [PR #16473](https://github.com/dotnet/fsharp/pull/16473)) +* Symbols: Add GenericArguments to FSharpEntity ([PR #16470](https://github.com/dotnet/fsharp/pull/16470)) + +### Changed + +* Autogenerated .Is* members for unions skipped for single-case unions. ([PR 16571](https://github.com/dotnet/fsharp/pull/16571)) +* `implicitCtorSynPats` in `SynTypeDefnSimpleRepr.General` is now `SynPat option` instead of `SynSimplePats option`. ([PR #16425](https://github.com/dotnet/fsharp/pull/16425)) +* `SyntaxVisitorBase<'T>.VisitSimplePats` now takes `SynPat` instead of `SynSimplePat list`. ([PR #16425](https://github.com/dotnet/fsharp/pull/16425)) +* Reduce allocations in compiler checking via `ValueOption` usage ([PR #16323](https://github.com/dotnet/fsharp/pull/16323), [PR #16567](https://github.com/dotnet/fsharp/pull/16567)) +* Reverted [#16348](https://github.com/dotnet/fsharp/pull/16348) `ThreadStatic` `CancellationToken` changes to improve test stability and prevent potential unwanted cancellations. ([PR #16536](https://github.com/dotnet/fsharp/pull/16536)) +* Refactored parenthesization API. ([PR #16461])(https://github.com/dotnet/fsharp/pull/16461)) diff --git a/docs/release-notes/.FSharp.Core/8.0.300.md b/docs/release-notes/.FSharp.Core/8.0.300.md new file mode 100644 index 00000000000..9acf7d07635 --- /dev/null +++ b/docs/release-notes/.FSharp.Core/8.0.300.md @@ -0,0 +1,3 @@ +### Fixed + +* Preserve original stack traces in resumable state machines generated code if available. ([PR #16568](https://github.com/dotnet/fsharp/pull/16568)) \ No newline at end of file diff --git a/docs/release-notes/.Language/preview.md b/docs/release-notes/.Language/preview.md index 0fce580b51e..832dd924a44 100644 --- a/docs/release-notes/.Language/preview.md +++ b/docs/release-notes/.Language/preview.md @@ -3,6 +3,7 @@ * Better generic unmanaged structs handling. ([Language suggestion #692](https://github.com/fsharp/fslang-suggestions/issues/692), [PR #12154](https://github.com/dotnet/fsharp/pull/12154)) * Bidirectional F#/C# interop for 'unmanaged' constraint. ([PR #12154](https://github.com/dotnet/fsharp/pull/12154)) * Make `.Is*` discriminated union properties visible. ([Language suggestion #222](https://github.com/fsharp/fslang-suggestions/issues/222), [PR #16341](https://github.com/dotnet/fsharp/pull/16341)) +* Allow returning bool instead of unit option for partial active patterns. ([Language suggestion #1041](https://github.com/fsharp/fslang-suggestions/issues/1041), [PR #16473](https://github.com/dotnet/fsharp/pull/16473)) ### Fixed diff --git a/docs/release-notes/.VisualStudio/17.10.md b/docs/release-notes/.VisualStudio/17.10.md new file mode 100644 index 00000000000..0045b1bd64b --- /dev/null +++ b/docs/release-notes/.VisualStudio/17.10.md @@ -0,0 +1,7 @@ +### Fixed + +* Show signature help mid-pipeline in more scenarios. ([PR #16462](https://github.com/dotnet/fsharp/pull/16462)) + +### Changed + +* Use refactored parenthesization API in unnecessary parentheses code fix. ([PR #16461])(https://github.com/dotnet/fsharp/pull/16461)) diff --git a/docs/release-notes/.VisualStudio/17.9.md b/docs/release-notes/.VisualStudio/17.9.md new file mode 100644 index 00000000000..47d796020d5 --- /dev/null +++ b/docs/release-notes/.VisualStudio/17.9.md @@ -0,0 +1,3 @@ +### Added + +* Analyzer & code fix for removing unnecessary parentheses. ([PR #16079](https://github.com/dotnet/fsharp/pull/16079) et seq.) diff --git a/eng/Build.ps1 b/eng/Build.ps1 index b713a86effa..fe7eb56a5b1 100644 --- a/eng/Build.ps1 +++ b/eng/Build.ps1 @@ -62,6 +62,7 @@ param ( [switch]$testAllButIntegration, [switch]$testpack, [switch]$testAOT, + [switch]$testBenchmarks, [string]$officialSkipTests = "false", [switch]$noVisualStudio, [switch]$sourceBuild, @@ -111,6 +112,7 @@ function Print-Usage() { Write-Host " -testVs Run F# editor unit tests" Write-Host " -testpack Verify built packages" Write-Host " -testAOT Run AOT/Trimming tests" + Write-Host " -testBenchmarks Build and Run Benchmark suite" Write-Host " -officialSkipTests Set to 'true' to skip running tests" Write-Host "" Write-Host "Advanced settings:" @@ -176,6 +178,7 @@ function Process-Arguments() { $script:testVs = $False $script:testpack = $False $script:testAOT = $False + $script:testBenchmarks = $False $script:verifypackageshipstatus = $True } @@ -211,6 +214,10 @@ function Process-Arguments() { $script:pack = $True; } + if ($testBenchmarks) { + $script:testBenchmarks = $True + } + foreach ($property in $properties) { if (!$property.StartsWith("/p:", "InvariantCultureIgnoreCase")) { Write-Host "Invalid argument: $property" @@ -541,12 +548,17 @@ try { } } + if ($testBenchmarks) { + BuildSolution "FSharp.Benchmarks.sln" $False + } + if ($pack) { $properties_storage = $properties $properties += "/p:GenerateSbom=false" BuildSolution "Microsoft.FSharp.Compiler.sln" $True $properties = $properties_storage } + if ($build) { VerifyAssemblyVersionsAndSymbols } @@ -662,6 +674,12 @@ try { Pop-Location } + if ($testBenchmarks) { + Push-Location "$RepoRoot\tests\benchmarks" + ./SmokeTestBenchmarks.ps1 + Pop-Location + } + # verify nupkgs have access to the source code $nupkgtestFailed = $false if ($testpack) { diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 777911db583..9010c61d06b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,11 +1,12 @@ - + https://github.com/dotnet/source-build-reference-packages - ef691e3c401949dab9986a50d8288a6e489f72bb + ffac2194c39660f03761ba81bdd6026202942184 + https://github.com/dotnet/msbuild 2cbc8b6aef648cf21c6a68a0dab7fe09a614e475 @@ -29,14 +30,19 @@ - + https://github.com/dotnet/arcade - 1d451c32dda2314c721adbf8829e1c0cd4e681ff + be88b08c41971b52ec11aec05ef31e72185d4a1f - + https://github.com/dotnet/xliff-tasks - 194f32828726c3f1f63f79f3dc09b9e99c157b11 + 73f0850939d96131c28cf6ea6ee5aacb4da0083a + + + + https://github.com/dotnet/xliff-tasks + 73f0850939d96131c28cf6ea6ee5aacb4da0083a diff --git a/eng/Versions.props b/eng/Versions.props index 598487cde55..d62e0635a85 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -16,7 +16,7 @@ 8 0 - 200 + 300 0 @@ -58,7 +58,7 @@ 17 - 9 + 10 $(VSMajorVersion).0 $(VSMajorVersion).$(VSMinorVersion).0 $(VSAssemblyVersionPrefix).0 @@ -91,7 +91,7 @@ 6.0.0 4.5.0 - 4.6.0-2.23126.2 + 4.6.0-3.23329.3 17.7.25-preview 17.7.35338-preview.1 17.7.58-pre @@ -168,7 +168,7 @@ 1.0.0 1.1.33 - 0.13.2 + 0.13.10 2.16.5 4.3.0.0 1.0.31 @@ -192,7 +192,6 @@ 2.4.2 5.10.3 2.2.0 - 1.0.0-beta.23426.1 1.0.0-prerelease.23614.4 1.0.0-prerelease.23614.4 diff --git a/eng/build-utils.ps1 b/eng/build-utils.ps1 index d5375c29732..a0c5299cb95 100644 --- a/eng/build-utils.ps1 +++ b/eng/build-utils.ps1 @@ -179,7 +179,7 @@ function Get-PackageDir([string]$name, [string]$version = "") { } function Run-MSBuild([string]$projectFilePath, [string]$buildArgs = "", [string]$logFileName = "", [switch]$parallel = $true, [switch]$summary = $true, [switch]$warnAsError = $true, [string]$configuration = $script:configuration, [string]$verbosity = $script:verbosity) { - # Because we override the C#/VB toolset to build against our LKG package, it is important + # Because we override the C#/VB toolset to build against our LKG (Last Known Good) package, it is important # that we do not reuse MSBuild nodes from other jobs/builds on the machine. Otherwise, # we'll run into issues such as https://github.com/dotnet/roslyn/issues/6211. # MSBuildAdditionalCommandLineArgs= diff --git a/eng/build.sh b/eng/build.sh index 3b992d6bfab..b8915397d25 100755 --- a/eng/build.sh +++ b/eng/build.sh @@ -25,6 +25,7 @@ usage() echo "Test actions:" echo " --testcoreclr Run unit tests on .NET Core (short: --test, -t)" echo " --testCompilerComponentTests Run FSharp.Compiler.ComponentTests on .NET Core" + echo " --testBenchmarks Build and Run Benchmark suite" echo "" echo "Advanced settings:" echo " --ci Building in CI" @@ -56,6 +57,7 @@ pack=false publish=false test_core_clr=false test_compilercomponent_tests=false +test_benchmarks=false configuration="Debug" verbosity='minimal' binary_log=false @@ -126,6 +128,9 @@ while [[ $# > 0 ]]; do --testcompilercomponenttests) test_compilercomponent_tests=true ;; + --testbenchmarks) + test_benchmarks=true + ;; --ci) ci=true ;; @@ -330,4 +335,10 @@ if [[ "$test_compilercomponent_tests" == true ]]; then TestUsingNUnit --testproject "$repo_root/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj" --targetframework $coreclrtestframework --notestfilter fi +if [[ "$test_benchmarks" == true ]]; then + pushd "$repo_root/tests/benchmarks" + ./SmokeTestBenchmarks.sh + popd +fi + ExitWithExitCode 0 diff --git a/eng/common/darc-init.ps1 b/eng/common/darc-init.ps1 index 435e7641341..8fda30bdce2 100644 --- a/eng/common/darc-init.ps1 +++ b/eng/common/darc-init.ps1 @@ -1,6 +1,6 @@ param ( $darcVersion = $null, - $versionEndpoint = 'https://maestro-prod.westus2.cloudapp.azure.com/api/assets/darc-version?api-version=2019-01-16', + $versionEndpoint = 'https://maestro.dot.net/api/assets/darc-version?api-version=2019-01-16', $verbosity = 'minimal', $toolpath = $null ) diff --git a/eng/common/darc-init.sh b/eng/common/darc-init.sh index 84c1d0cc2e7..c305ae6bd77 100755 --- a/eng/common/darc-init.sh +++ b/eng/common/darc-init.sh @@ -2,7 +2,7 @@ source="${BASH_SOURCE[0]}" darcVersion='' -versionEndpoint='https://maestro-prod.westus2.cloudapp.azure.com/api/assets/darc-version?api-version=2019-01-16' +versionEndpoint='https://maestro.dot.net/api/assets/darc-version?api-version=2019-01-16' verbosity='minimal' while [[ $# > 0 ]]; do diff --git a/eng/common/post-build/add-build-to-channel.ps1 b/eng/common/post-build/add-build-to-channel.ps1 index de2d957922a..49938f0c89f 100644 --- a/eng/common/post-build/add-build-to-channel.ps1 +++ b/eng/common/post-build/add-build-to-channel.ps1 @@ -2,7 +2,7 @@ param( [Parameter(Mandatory=$true)][int] $BuildId, [Parameter(Mandatory=$true)][int] $ChannelId, [Parameter(Mandatory=$true)][string] $MaestroApiAccessToken, - [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro-prod.westus2.cloudapp.azure.com', + [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro.dot.net', [Parameter(Mandatory=$false)][string] $MaestroApiVersion = '2019-01-16' ) diff --git a/eng/common/post-build/publish-using-darc.ps1 b/eng/common/post-build/publish-using-darc.ps1 index 8508397d776..1e779fec4dd 100644 --- a/eng/common/post-build/publish-using-darc.ps1 +++ b/eng/common/post-build/publish-using-darc.ps1 @@ -3,7 +3,7 @@ param( [Parameter(Mandatory=$true)][int] $PublishingInfraVersion, [Parameter(Mandatory=$true)][string] $AzdoToken, [Parameter(Mandatory=$true)][string] $MaestroToken, - [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro-prod.westus2.cloudapp.azure.com', + [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro.dot.net', [Parameter(Mandatory=$true)][string] $WaitPublishingFinish, [Parameter(Mandatory=$false)][string] $ArtifactsPublishingAdditionalParameters, [Parameter(Mandatory=$false)][string] $SymbolPublishingAdditionalParameters diff --git a/eng/common/post-build/trigger-subscriptions.ps1 b/eng/common/post-build/trigger-subscriptions.ps1 index 55dea518ac5..ac9a95778fc 100644 --- a/eng/common/post-build/trigger-subscriptions.ps1 +++ b/eng/common/post-build/trigger-subscriptions.ps1 @@ -2,7 +2,7 @@ param( [Parameter(Mandatory=$true)][string] $SourceRepo, [Parameter(Mandatory=$true)][int] $ChannelId, [Parameter(Mandatory=$true)][string] $MaestroApiAccessToken, - [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro-prod.westus2.cloudapp.azure.com', + [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro.dot.net', [Parameter(Mandatory=$false)][string] $MaestroApiVersion = '2019-01-16' ) diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 index 6c4ac6fec1a..73828dd30d3 100644 --- a/eng/common/sdk-task.ps1 +++ b/eng/common/sdk-task.ps1 @@ -64,7 +64,7 @@ try { $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty } if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.6.0-2" -MemberType NoteProperty + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.8.1-2" -MemberType NoteProperty } if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index e20ee3a983c..e24ca2f46f9 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -136,7 +136,7 @@ jobs: condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}: - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 - ${{ if and(ne(parameters.artifacts.download, 'false'), ne(parameters.artifacts.download, '')) }}: - task: DownloadPipelineArtifact@2 diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml index 42017109f37..fa5446c093d 100644 --- a/eng/common/templates/job/publish-build-assets.yml +++ b/eng/common/templates/job/publish-build-assets.yml @@ -72,7 +72,7 @@ jobs: condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 - task: PowerShell@2 displayName: Publish Build Assets @@ -81,7 +81,7 @@ jobs: arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests' /p:BuildAssetRegistryToken=$(MaestroAccessToken) - /p:MaestroApiEndpoint=https://maestro-prod.westus2.cloudapp.azure.com + /p:MaestroApiEndpoint=https://maestro.dot.net /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }} /p:OfficialBuildId=$(Build.BuildNumber) condition: ${{ parameters.condition }} diff --git a/eng/common/templates/post-build/common-variables.yml b/eng/common/templates/post-build/common-variables.yml index c24193acfc9..173914f2364 100644 --- a/eng/common/templates/post-build/common-variables.yml +++ b/eng/common/templates/post-build/common-variables.yml @@ -7,7 +7,7 @@ variables: # Default Maestro++ API Endpoint and API Version - name: MaestroApiEndPoint - value: "https://maestro-prod.westus2.cloudapp.azure.com" + value: "https://maestro.dot.net" - name: MaestroApiAccessToken value: $(MaestroAccessToken) - name: MaestroApiVersion diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml index ef720f9d781..3f74abf7ce0 100644 --- a/eng/common/templates/post-build/post-build.yml +++ b/eng/common/templates/post-build/post-build.yml @@ -169,7 +169,7 @@ stages: # This is necessary whenever we want to publish/restore to an AzDO private feed # Since sdk-task.ps1 tries to restore packages we need to do this authentication here # otherwise it'll complain about accessing a private feed. - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 displayName: 'Authenticate to AzDO Feeds' # Signing validation will optionally work with the buildmanifest file which is downloaded from @@ -266,7 +266,7 @@ stages: BARBuildId: ${{ parameters.BARBuildId }} PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 - task: PowerShell@2 displayName: Publish Using Darc diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index aa74ab4a81e..eb188cfda41 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -379,13 +379,13 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = } # Minimum VS version to require. - $vsMinVersionReqdStr = '17.6' + $vsMinVersionReqdStr = '17.7' $vsMinVersionReqd = [Version]::new($vsMinVersionReqdStr) # If the version of msbuild is going to be xcopied, # use this version. Version matches a package here: - # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/RoslynTools.MSBuild/versions/17.6.0-2 - $defaultXCopyMSBuildVersion = '17.6.0-2' + # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/RoslynTools.MSBuild/versions/17.8.1-2 + $defaultXCopyMSBuildVersion = '17.8.1-2' if (!$vsRequirements) { if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { @@ -601,7 +601,15 @@ function InitializeBuildTool() { ExitWithExitCode 1 } $dotnetPath = Join-Path $dotnetRoot (GetExecutableFileName 'dotnet') - $buildTool = @{ Path = $dotnetPath; Command = 'msbuild'; Tool = 'dotnet'; Framework = 'net8.0' } + + # Use override if it exists - commonly set by source-build + if ($null -eq $env:_OverrideArcadeInitializeBuildToolFramework) { + $initializeBuildToolFramework="net8.0" + } else { + $initializeBuildToolFramework=$env:_OverrideArcadeInitializeBuildToolFramework + } + + $buildTool = @{ Path = $dotnetPath; Command = 'msbuild'; Tool = 'dotnet'; Framework = $initializeBuildToolFramework } } elseif ($msbuildEngine -eq "vs") { try { $msbuildPath = InitializeVisualStudioMSBuild -install:$restore diff --git a/eng/common/tools.sh b/eng/common/tools.sh index e8d47894334..3392e3a9992 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -341,7 +341,12 @@ function InitializeBuildTool { # return values _InitializeBuildTool="$_InitializeDotNetCli/dotnet" _InitializeBuildToolCommand="msbuild" - _InitializeBuildToolFramework="net8.0" + # use override if it exists - commonly set by source-build + if [[ "${_OverrideArcadeInitializeBuildToolFramework:-x}" == "x" ]]; then + _InitializeBuildToolFramework="net8.0" + else + _InitializeBuildToolFramework="${_OverrideArcadeInitializeBuildToolFramework}" + fi } # Set RestoreNoCache as a workaround for https://github.com/NuGet/Home/issues/3116 diff --git a/eng/restore-internal-tools.yml b/eng/restore-internal-tools.yml index f71d0710e44..01cd835c5b7 100644 --- a/eng/restore-internal-tools.yml +++ b/eng/restore-internal-tools.yml @@ -1,5 +1,5 @@ steps: - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 inputs: nuGetServiceConnections: 'devdiv/dotnet-core-internal-tooling' forceReinstallCredentialProvider: true @@ -10,4 +10,4 @@ steps: /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/RestoreInternal.binlog /v:normal displayName: Restore internal tools - condition: and(succeeded(), ne(variables['_skipRestoreInternalTools'], 'true')) \ No newline at end of file + condition: and(succeeded(), ne(variables['_skipRestoreInternalTools'], 'true')) diff --git a/global.json b/global.json index fa76ef7aa20..57b76f86929 100644 --- a/global.json +++ b/global.json @@ -1,10 +1,10 @@ { "sdk": { - "version": "8.0.100", + "version": "8.0.101", "allowPrerelease": true }, "tools": { - "dotnet": "8.0.100", + "dotnet": "8.0.101", "vs": { "version": "17.8", "components": [ @@ -17,7 +17,7 @@ "perl": "5.38.0.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23463.1", + "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24081.5", "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.23255.2" } } diff --git a/src/Compiler/AbstractIL/ilwritepdb.fs b/src/Compiler/AbstractIL/ilwritepdb.fs index 24082eea2b3..fd5ffad27ac 100644 --- a/src/Compiler/AbstractIL/ilwritepdb.fs +++ b/src/Compiler/AbstractIL/ilwritepdb.fs @@ -16,6 +16,7 @@ open Internal.Utilities open FSharp.Compiler.AbstractIL.IL open FSharp.Compiler.AbstractIL.Support open Internal.Utilities.Library +open Internal.Utilities.Library.Extras open FSharp.Compiler.DiagnosticsLogger open FSharp.Compiler.IO open FSharp.Compiler.Text.Range @@ -1028,6 +1029,11 @@ let rec pushShadowedLocals (stackGuard: StackGuard) (localsToPush: PdbLocalVar[] // adding the text " (shadowed)" to the names of those with name conflicts. let unshadowScopes rootScope = // Avoid stack overflow when writing linearly nested scopes - let stackGuard = StackGuard(100, "ILPdbWriter.unshadowScopes") + let UnshadowScopesStackGuardDepth = + GetEnvInteger "FSHARP_ILPdb_UnshadowScopes_StackGuardDepth" 100 + + let stackGuard = + StackGuard(UnshadowScopesStackGuardDepth, "ILPdbWriter.unshadowScopes") + let result, _ = pushShadowedLocals stackGuard [||] rootScope result diff --git a/src/Compiler/Checking/CheckComputationExpressions.fs b/src/Compiler/Checking/CheckComputationExpressions.fs index f1900f7a166..f0727c3fc5d 100644 --- a/src/Compiler/Checking/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/CheckComputationExpressions.fs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -/// The typechecker. Left-to-right constrained type checking +/// The typechecker. Left-to-right constrained type checking /// with generalization at appropriate points. module internal FSharp.Compiler.CheckComputationExpressions @@ -28,65 +28,91 @@ open FSharp.Compiler.TypedTreeOps type cenv = TcFileState /// Used to flag if this is the first or a sebsequent translation pass through a computation expression -type CompExprTranslationPass = Initial | Subsequent +type CompExprTranslationPass = + | Initial + | Subsequent /// Used to flag if computation expression custom operations are allowed in a given context -type CustomOperationsMode = Allowed | Denied +type CustomOperationsMode = + | Allowed + | Denied -let TryFindIntrinsicOrExtensionMethInfo collectionSettings (cenv: cenv) (env: TcEnv) m ad nm ty = +let TryFindIntrinsicOrExtensionMethInfo collectionSettings (cenv: cenv) (env: TcEnv) m ad nm ty = AllMethInfosOfTypeInScope collectionSettings cenv.infoReader env.NameEnv (Some nm) ad IgnoreOverrides m ty /// Ignores an attribute let IgnoreAttribute _ = None -let (|ExprAsPat|_|) (f: SynExpr) = - match f with - | SingleIdent v1 | SynExprParen(SingleIdent v1, _, _, _) -> Some (mkSynPatVar None v1) - | SynExprParen(SynExpr.Tuple (false, elems, commas, _), _, _, _) -> - let elems = elems |> List.map (|SingleIdent|_|) - if elems |> List.forall (fun x -> x.IsSome) then - Some (SynPat.Tuple(false, (elems |> List.map (fun x -> mkSynPatVar None x.Value)), commas, f.Range)) +[] +let (|ExprAsPat|_|) (f: SynExpr) = + match f with + | SingleIdent v1 + | SynExprParen(SingleIdent v1, _, _, _) -> ValueSome(mkSynPatVar None v1) + | SynExprParen(SynExpr.Tuple(false, elems, commas, _), _, _, _) -> + let elems = elems |> List.map (|SingleIdent|_|) + + if elems |> List.forall (fun x -> x.IsSome) then + ValueSome(SynPat.Tuple(false, (elems |> List.map (fun x -> mkSynPatVar None x.Value)), commas, f.Range)) else - None - | _ -> None + ValueNone + | _ -> ValueNone -// For join clauses that join on nullable, we syntactically insert the creation of nullable values on the appropriate side of the condition, +// For join clauses that join on nullable, we syntactically insert the creation of nullable values on the appropriate side of the condition, // then pull the syntax apart again -let (|JoinRelation|_|) cenv env (expr: SynExpr) = +[] +let (|JoinRelation|_|) cenv env (expr: SynExpr) = let m = expr.Range let ad = env.eAccessRights let isOpName opName vref s = - (s = opName) && - match ResolveExprLongIdent cenv.tcSink cenv.nameResolver m ad env.eNameResEnv TypeNameResolutionInfo.Default [ident(opName, m)] None with - | Result (_, Item.Value vref2, []) -> valRefEq cenv.g vref vref2 - | _ -> false + (s = opName) + && match + ResolveExprLongIdent + cenv.tcSink + cenv.nameResolver + m + ad + env.eNameResEnv + TypeNameResolutionInfo.Default + [ ident (opName, m) ] + None + with + | Result(_, Item.Value vref2, []) -> valRefEq cenv.g vref vref2 + | _ -> false + + match expr with + | BinOpExpr(opId, a, b) when isOpName opNameEquals cenv.g.equals_operator_vref opId.idText -> ValueSome(a, b) - match expr with - | BinOpExpr(opId, a, b) when isOpName opNameEquals cenv.g.equals_operator_vref opId.idText -> Some (a, b) + | BinOpExpr(opId, a, b) when isOpName opNameEqualsNullable cenv.g.equals_nullable_operator_vref opId.idText -> - | BinOpExpr(opId, a, b) when isOpName opNameEqualsNullable cenv.g.equals_nullable_operator_vref opId.idText -> + let a = + SynExpr.App(ExprAtomicFlag.Atomic, false, mkSynLidGet a.Range [ MangledGlobalName; "System" ] "Nullable", a, a.Range) - let a = SynExpr.App (ExprAtomicFlag.Atomic, false, mkSynLidGet a.Range [MangledGlobalName;"System"] "Nullable", a, a.Range) - Some (a, b) + ValueSome(a, b) - | BinOpExpr(opId, a, b) when isOpName opNameNullableEquals cenv.g.nullable_equals_operator_vref opId.idText -> + | BinOpExpr(opId, a, b) when isOpName opNameNullableEquals cenv.g.nullable_equals_operator_vref opId.idText -> - let b = SynExpr.App (ExprAtomicFlag.Atomic, false, mkSynLidGet b.Range [MangledGlobalName;"System"] "Nullable", b, b.Range) - Some (a, b) + let b = + SynExpr.App(ExprAtomicFlag.Atomic, false, mkSynLidGet b.Range [ MangledGlobalName; "System" ] "Nullable", b, b.Range) - | BinOpExpr(opId, a, b) when isOpName opNameNullableEqualsNullable cenv.g.nullable_equals_nullable_operator_vref opId.idText -> + ValueSome(a, b) - Some (a, b) + | BinOpExpr(opId, a, b) when isOpName opNameNullableEqualsNullable cenv.g.nullable_equals_nullable_operator_vref opId.idText -> - | _ -> None + ValueSome(a, b) + + | _ -> ValueNone let elimFastIntegerForLoop (spFor, spTo, id, start: SynExpr, dir, finish: SynExpr, innerExpr, m: range) = let mOp = (unionRanges start.Range finish.Range).MakeSynthetic() - let pseudoEnumExpr = - if dir then mkSynInfix mOp start ".." finish - else mkSynTrifix mOp ".. .." start (SynExpr.Const (SynConst.Int32 -1, mOp)) finish - SynExpr.ForEach (spFor, spTo, SeqExprOnly false, true, mkSynPatVar None id, pseudoEnumExpr, innerExpr, m) + + let pseudoEnumExpr = + if dir then + mkSynInfix mOp start ".." finish + else + mkSynTrifix mOp ".. .." start (SynExpr.Const(SynConst.Int32 -1, mOp)) finish + + SynExpr.ForEach(spFor, spTo, SeqExprOnly false, true, mkSynPatVar None id, pseudoEnumExpr, innerExpr, m) /// Check if a computation or sequence expression is syntactically free of 'yield' (though not yield!) let YieldFree (cenv: cenv) expr = @@ -95,30 +121,27 @@ let YieldFree (cenv: cenv) expr = // Implement yield free logic for F# Language including the LanguageFeature.ImplicitYield let rec YieldFree expr = match expr with - | SynExpr.Sequential (expr1=expr1; expr2=expr2) -> - YieldFree expr1 && YieldFree expr2 + | SynExpr.Sequential(expr1 = expr1; expr2 = expr2) -> YieldFree expr1 && YieldFree expr2 - | SynExpr.IfThenElse (thenExpr=thenExpr; elseExpr=elseExprOpt) -> - YieldFree thenExpr && Option.forall YieldFree elseExprOpt + | SynExpr.IfThenElse(thenExpr = thenExpr; elseExpr = elseExprOpt) -> YieldFree thenExpr && Option.forall YieldFree elseExprOpt - | SynExpr.TryWith (tryExpr=body; withCases=clauses) -> - YieldFree body && clauses |> List.forall (fun (SynMatchClause(resultExpr = res)) -> YieldFree res) + | SynExpr.TryWith(tryExpr = body; withCases = clauses) -> + YieldFree body + && clauses |> List.forall (fun (SynMatchClause(resultExpr = res)) -> YieldFree res) - | SynExpr.Match (clauses=clauses) | SynExpr.MatchBang (clauses=clauses) -> - clauses |> List.forall (fun (SynMatchClause(resultExpr = res)) -> YieldFree res) + | SynExpr.Match(clauses = clauses) + | SynExpr.MatchBang(clauses = clauses) -> clauses |> List.forall (fun (SynMatchClause(resultExpr = res)) -> YieldFree res) - | SynExpr.For (doBody=body) - | SynExpr.TryFinally (tryExpr=body) - | SynExpr.LetOrUse (body=body) - | SynExpr.While (doExpr=body) - | SynExpr.WhileBang (doExpr=body) - | SynExpr.ForEach (bodyExpr=body) -> - YieldFree body + | SynExpr.For(doBody = body) + | SynExpr.TryFinally(tryExpr = body) + | SynExpr.LetOrUse(body = body) + | SynExpr.While(doExpr = body) + | SynExpr.WhileBang(doExpr = body) + | SynExpr.ForEach(bodyExpr = body) -> YieldFree body - | SynExpr.LetOrUseBang(body=body) -> - YieldFree body + | SynExpr.LetOrUseBang(body = body) -> YieldFree body - | SynExpr.YieldOrReturn(flags=(true, _)) -> false + | SynExpr.YieldOrReturn(flags = (true, _)) -> false | _ -> true @@ -127,25 +150,23 @@ let YieldFree (cenv: cenv) expr = // Implement yield free logic for F# Language without the LanguageFeature.ImplicitYield let rec YieldFree expr = match expr with - | SynExpr.Sequential (expr1=expr1; expr2=expr2) -> - YieldFree expr1 && YieldFree expr2 + | SynExpr.Sequential(expr1 = expr1; expr2 = expr2) -> YieldFree expr1 && YieldFree expr2 - | SynExpr.IfThenElse (thenExpr=thenExpr; elseExpr=elseExprOpt) -> - YieldFree thenExpr && Option.forall YieldFree elseExprOpt + | SynExpr.IfThenElse(thenExpr = thenExpr; elseExpr = elseExprOpt) -> YieldFree thenExpr && Option.forall YieldFree elseExprOpt - | SynExpr.TryWith (tryExpr=e1; withCases=clauses) -> - YieldFree e1 && clauses |> List.forall (fun (SynMatchClause(resultExpr = res)) -> YieldFree res) + | SynExpr.TryWith(tryExpr = e1; withCases = clauses) -> + YieldFree e1 + && clauses |> List.forall (fun (SynMatchClause(resultExpr = res)) -> YieldFree res) - | SynExpr.Match (clauses=clauses) | SynExpr.MatchBang (clauses=clauses) -> - clauses |> List.forall (fun (SynMatchClause(resultExpr = res)) -> YieldFree res) + | SynExpr.Match(clauses = clauses) + | SynExpr.MatchBang(clauses = clauses) -> clauses |> List.forall (fun (SynMatchClause(resultExpr = res)) -> YieldFree res) - | SynExpr.For (doBody=body) - | SynExpr.TryFinally (tryExpr=body) - | SynExpr.LetOrUse (body=body) - | SynExpr.While (doExpr=body) - | SynExpr.WhileBang (doExpr=body) - | SynExpr.ForEach (bodyExpr=body) -> - YieldFree body + | SynExpr.For(doBody = body) + | SynExpr.TryFinally(tryExpr = body) + | SynExpr.LetOrUse(body = body) + | SynExpr.While(doExpr = body) + | SynExpr.WhileBang(doExpr = body) + | SynExpr.ForEach(bodyExpr = body) -> YieldFree body | SynExpr.LetOrUseBang _ | SynExpr.YieldOrReturnFrom _ @@ -157,44 +178,43 @@ let YieldFree (cenv: cenv) expr = YieldFree expr - /// Determine if a syntactic expression inside 'seq { ... }' or '[...]' counts as a "simple sequence /// of semicolon separated values". For example [1;2;3]. /// 'acceptDeprecated' is true for the '[ ... ]' case, where we allow the syntax '[ if g then t else e ]' but ask it to be parenthesized -/// -let (|SimpleSemicolonSequence|_|) cenv acceptDeprecated cexpr = +[] +let (|SimpleSemicolonSequence|_|) cenv acceptDeprecated cexpr = - let IsSimpleSemicolonSequenceElement expr = + let IsSimpleSemicolonSequenceElement expr = match expr with | SynExpr.IfThenElse _ when acceptDeprecated && YieldFree cenv expr -> true | SynExpr.IfThenElse _ - | SynExpr.TryWith _ - | SynExpr.Match _ - | SynExpr.For _ - | SynExpr.ForEach _ - | SynExpr.TryFinally _ - | SynExpr.YieldOrReturnFrom _ - | SynExpr.YieldOrReturn _ - | SynExpr.LetOrUse _ - | SynExpr.Do _ - | SynExpr.MatchBang _ - | SynExpr.LetOrUseBang _ + | SynExpr.TryWith _ + | SynExpr.Match _ + | SynExpr.For _ + | SynExpr.ForEach _ + | SynExpr.TryFinally _ + | SynExpr.YieldOrReturnFrom _ + | SynExpr.YieldOrReturn _ + | SynExpr.LetOrUse _ + | SynExpr.Do _ + | SynExpr.MatchBang _ + | SynExpr.LetOrUseBang _ | SynExpr.While _ | SynExpr.WhileBang _ -> false | _ -> true - let rec TryGetSimpleSemicolonSequenceOfComprehension expr acc = - match expr with - | SynExpr.Sequential (_, true, e1, e2, _) -> - if IsSimpleSemicolonSequenceElement e1 then + let rec TryGetSimpleSemicolonSequenceOfComprehension expr acc = + match expr with + | SynExpr.Sequential(_, true, e1, e2, _) -> + if IsSimpleSemicolonSequenceElement e1 then TryGetSimpleSemicolonSequenceOfComprehension e2 (e1 :: acc) else - None - | _ -> - if IsSimpleSemicolonSequenceElement expr then - Some(List.rev (expr :: acc)) - else - None + ValueNone + | _ -> + if IsSimpleSemicolonSequenceElement expr then + ValueSome(List.rev (expr :: acc)) + else + ValueNone TryGetSimpleSemicolonSequenceOfComprehension cexpr [] @@ -202,88 +222,106 @@ let RecordNameAndTypeResolutions cenv env tpenv expr = // This function is motivated by cases like // query { for ... join(for x in f(). } // where there is incomplete code in a query, and we are current just dropping a piece of the AST on the floor (above, the bit inside the 'join'). - // + // // The problem with dropping the AST on the floor is that we get no captured resolutions, which means no Intellisense/QuickInfo/ParamHelp. // // We check this AST-fragment, to get resolutions captured. // // This may have effects from typechecking, producing side-effects on the typecheck environment. - suppressErrorReporting (fun () -> + suppressErrorReporting (fun () -> try - ignore(TcExprOfUnknownType cenv env tpenv expr) + ignore (TcExprOfUnknownType cenv env tpenv expr) with _ -> ()) /// Used for all computation expressions except sequence expressions -let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhole, interpExpr: Expr, builderTy, comp: SynExpr) = +let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhole, interpExpr: Expr, builderTy, comp: SynExpr) = let overallTy = overallTy.Commit - + let g = cenv.g let ad = env.eAccessRights let mkSynDelay2 (e: SynExpr) = mkSynDelay (e.Range.MakeSynthetic()) e - + let builderValName = CompilerGeneratedName "builder" let mBuilderVal = interpExpr.Range - + // Give bespoke error messages for the FSharp.Core "query" builder - let isQuery = - match stripDebugPoints interpExpr with + let isQuery = + match stripDebugPoints interpExpr with // An unparameterized custom builder, e.g., `query`, `async`. - | Expr.Val (vref, _, m) + | Expr.Val(vref, _, m) // A parameterized custom builder, e.g., `builder<…>`, `builder ()`. - | Expr.App (funcExpr = Expr.Val (vref, _, m)) -> - let item = Item.CustomBuilder (vref.DisplayName, vref) + | Expr.App(funcExpr = Expr.Val(vref, _, m)) -> + let item = Item.CustomBuilder(vref.DisplayName, vref) CallNameResolutionSink cenv.tcSink (m, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, env.eAccessRights) - valRefEq cenv.g vref cenv.g.query_value_vref + valRefEq cenv.g vref cenv.g.query_value_vref | _ -> false /// Make a builder.Method(...) call - let mkSynCall nm (m: range) args = + let mkSynCall nm (m: range) args = let m = m.MakeSynthetic() // Mark as synthetic so the language service won't pick it up. - let args = - match args with - | [] -> SynExpr.Const (SynConst.Unit, m) - | [arg] -> SynExpr.Paren (SynExpr.Paren (arg, range0, None, m), range0, None, m) - | args -> SynExpr.Paren (SynExpr.Tuple (false, args, [], m), range0, None, m) - + + let args = + match args with + | [] -> SynExpr.Const(SynConst.Unit, m) + | [ arg ] -> SynExpr.Paren(SynExpr.Paren(arg, range0, None, m), range0, None, m) + | args -> SynExpr.Paren(SynExpr.Tuple(false, args, [], m), range0, None, m) + let builderVal = mkSynIdGet m builderValName - mkSynApp1 (SynExpr.DotGet (builderVal, range0, SynLongIdent([mkSynId m nm], [], [None]), m)) args m + mkSynApp1 (SynExpr.DotGet(builderVal, range0, SynLongIdent([ mkSynId m nm ], [], [ None ]), m)) args m - let hasMethInfo nm = TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBuilderVal ad nm builderTy |> isNil |> not + let hasMethInfo nm = + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBuilderVal ad nm builderTy + |> isNil + |> not - let sourceMethInfo = TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBuilderVal ad "Source" builderTy + let sourceMethInfo = + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBuilderVal ad "Source" builderTy // Optionally wrap sources of "let!", "yield!", "use!" in "query.Source" - let mkSourceExpr callExpr = - match sourceMethInfo with + let mkSourceExpr callExpr = + match sourceMethInfo with | [] -> callExpr - | _ -> mkSynCall "Source" callExpr.Range [callExpr] + | _ -> mkSynCall "Source" callExpr.Range [ callExpr ] - let mkSourceExprConditional isFromSource callExpr = + let mkSourceExprConditional isFromSource callExpr = if isFromSource then mkSourceExpr callExpr else callExpr /// Decide if the builder is an auto-quote builder let isAutoQuote = hasMethInfo "Quote" let customOperationMethods = - AllMethInfosOfTypeInScope ResultCollectionSettings.AllResults cenv.infoReader env.NameEnv None ad IgnoreOverrides mBuilderVal builderTy + AllMethInfosOfTypeInScope + ResultCollectionSettings.AllResults + cenv.infoReader + env.NameEnv + None + ad + IgnoreOverrides + mBuilderVal + builderTy |> List.choose (fun methInfo -> - if not (IsMethInfoAccessible cenv.amap mBuilderVal ad methInfo) then None else + if not (IsMethInfoAccessible cenv.amap mBuilderVal ad methInfo) then + None + else let nameSearch = - TryBindMethInfoAttribute cenv.g mBuilderVal cenv.g.attrib_CustomOperationAttribute methInfo + TryBindMethInfoAttribute + cenv.g + mBuilderVal + cenv.g.attrib_CustomOperationAttribute + methInfo IgnoreAttribute // We do not respect this attribute for IL methods (fun attr -> // NOTE: right now, we support of custom operations with spaces in them ([]) // In the parameterless CustomOperationAttribute - we use the method name, and also allow it to be ````-quoted (member _.``foo bar`` _ = ...) match attr with // Empty string and parameterless constructor - we use the method name - | Attrib(_, _, [ AttribStringArg "" ], _, _, _, _) // Empty string as parameter - | Attrib(_, _, [ ], _, _, _, _) -> // No parameters, same as empty string for compat reasons. + | Attrib(unnamedArgs = [ AttribStringArg "" ]) // Empty string as parameter + | Attrib(unnamedArgs = []) -> // No parameters, same as empty string for compat reasons. Some methInfo.LogicalName // Use the specified name - | Attrib(_, _, [ AttribStringArg msg ], _, _, _, _) -> - Some msg + | Attrib(unnamedArgs = [ AttribStringArg msg ]) -> Some msg | _ -> None) IgnoreAttribute // We do not respect this attribute for provided methods @@ -291,34 +329,55 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol | None -> None | Some nm -> let joinConditionWord = - TryBindMethInfoAttribute cenv.g mBuilderVal cenv.g.attrib_CustomOperationAttribute methInfo + TryBindMethInfoAttribute + cenv.g + mBuilderVal + cenv.g.attrib_CustomOperationAttribute + methInfo IgnoreAttribute // We do not respect this attribute for IL methods - (function Attrib(_, _, _, ExtractAttribNamedArg "JoinConditionWord" (AttribStringArg s), _, _, _) -> Some s | _ -> None) + (function + | Attrib(propVal = ExtractAttribNamedArg "JoinConditionWord" (AttribStringArg s)) -> Some s + | _ -> None) IgnoreAttribute // We do not respect this attribute for provided methods - let flagSearch (propName: string) = - TryBindMethInfoAttribute cenv.g mBuilderVal cenv.g.attrib_CustomOperationAttribute methInfo + let flagSearch (propName: string) = + TryBindMethInfoAttribute + cenv.g + mBuilderVal + cenv.g.attrib_CustomOperationAttribute + methInfo IgnoreAttribute // We do not respect this attribute for IL methods - (function Attrib(_, _, _, ExtractAttribNamedArg propName (AttribBoolArg b), _, _, _) -> Some b | _ -> None) + (function + | Attrib(propVal = ExtractAttribNamedArg propName (AttribBoolArg b)) -> Some b + | _ -> None) IgnoreAttribute // We do not respect this attribute for provided methods - let maintainsVarSpaceUsingBind = defaultArg (flagSearch "MaintainsVariableSpaceUsingBind") false + let maintainsVarSpaceUsingBind = + defaultArg (flagSearch "MaintainsVariableSpaceUsingBind") false + let maintainsVarSpace = defaultArg (flagSearch "MaintainsVariableSpace") false let allowInto = defaultArg (flagSearch "AllowIntoPattern") false let isLikeZip = defaultArg (flagSearch "IsLikeZip") false let isLikeJoin = defaultArg (flagSearch "IsLikeJoin") false let isLikeGroupJoin = defaultArg (flagSearch "IsLikeGroupJoin") false - Some (nm, maintainsVarSpaceUsingBind, maintainsVarSpace, allowInto, isLikeZip, isLikeJoin, isLikeGroupJoin, joinConditionWord, methInfo)) - - let customOperationMethodsIndexedByKeyword = + Some( + nm, + maintainsVarSpaceUsingBind, + maintainsVarSpace, + allowInto, + isLikeZip, + isLikeJoin, + isLikeGroupJoin, + joinConditionWord, + methInfo + )) + + let customOperationMethodsIndexedByKeyword = if cenv.g.langVersion.SupportsFeature LanguageFeature.OverloadsForCustomOperations then customOperationMethods |> Seq.groupBy (fun (nm, _, _, _, _, _, _, _, _) -> nm) - |> Seq.map (fun (nm, group) -> - (nm, - group - |> Seq.toList)) + |> Seq.map (fun (nm, group) -> (nm, group |> Seq.toList)) else customOperationMethods |> Seq.groupBy (fun (nm, _, _, _, _, _, _, _, _) -> nm) @@ -326,14 +385,11 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol |> dict // Check for duplicates by method name (keywords and method names must be 1:1) - let customOperationMethodsIndexedByMethodName = + let customOperationMethodsIndexedByMethodName = if cenv.g.langVersion.SupportsFeature LanguageFeature.OverloadsForCustomOperations then customOperationMethods |> Seq.groupBy (fun (_, _, _, _, _, _, _, _, methInfo) -> methInfo.LogicalName) - |> Seq.map (fun (nm, group) -> - (nm, - group - |> Seq.toList)) + |> Seq.map (fun (nm, group) -> (nm, group |> Seq.toList)) else customOperationMethods |> Seq.groupBy (fun (_, _, _, _, _, _, _, _, methInfo) -> methInfo.LogicalName) @@ -344,1189 +400,2003 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol let tryGetDataForCustomOperation (nm: Ident) = let isOpDataCountAllowed opDatas = match opDatas with - | [_] -> true + | [ _ ] -> true | _ :: _ -> cenv.g.langVersion.SupportsFeature LanguageFeature.OverloadsForCustomOperations | _ -> false - match customOperationMethodsIndexedByKeyword.TryGetValue nm.idText with - | true, opDatas when isOpDataCountAllowed opDatas -> + match customOperationMethodsIndexedByKeyword.TryGetValue nm.idText with + | true, opDatas when isOpDataCountAllowed opDatas -> for opData in opDatas do - let opName, maintainsVarSpaceUsingBind, maintainsVarSpace, _allowInto, isLikeZip, isLikeJoin, isLikeGroupJoin, _joinConditionWord, methInfo = opData - if (maintainsVarSpaceUsingBind && maintainsVarSpace) || (isLikeZip && isLikeJoin) || (isLikeZip && isLikeGroupJoin) || (isLikeJoin && isLikeGroupJoin) then - errorR(Error(FSComp.SR.tcCustomOperationInvalid opName, nm.idRange)) + let (opName, + maintainsVarSpaceUsingBind, + maintainsVarSpace, + _allowInto, + isLikeZip, + isLikeJoin, + isLikeGroupJoin, + _joinConditionWord, + methInfo) = + opData + + if + (maintainsVarSpaceUsingBind && maintainsVarSpace) + || (isLikeZip && isLikeJoin) + || (isLikeZip && isLikeGroupJoin) + || (isLikeJoin && isLikeGroupJoin) + then + errorR (Error(FSComp.SR.tcCustomOperationInvalid opName, nm.idRange)) + if not (cenv.g.langVersion.SupportsFeature LanguageFeature.OverloadsForCustomOperations) then - match customOperationMethodsIndexedByMethodName.TryGetValue methInfo.LogicalName with - | true, [_] -> () - | _ -> errorR(Error(FSComp.SR.tcCustomOperationMayNotBeOverloaded nm.idText, nm.idRange)) + match customOperationMethodsIndexedByMethodName.TryGetValue methInfo.LogicalName with + | true, [ _ ] -> () + | _ -> errorR (Error(FSComp.SR.tcCustomOperationMayNotBeOverloaded nm.idText, nm.idRange)) + Some opDatas - | true, opData :: _ -> errorR(Error(FSComp.SR.tcCustomOperationMayNotBeOverloaded nm.idText, nm.idRange)); Some [opData] + | true, opData :: _ -> + errorR (Error(FSComp.SR.tcCustomOperationMayNotBeOverloaded nm.idText, nm.idRange)) + Some [ opData ] | _ -> None /// Decide if the identifier represents a use of a custom query operator - let hasCustomOperations () = if isNil customOperationMethods then CustomOperationsMode.Denied else CustomOperationsMode.Allowed + let hasCustomOperations () = + if isNil customOperationMethods then + CustomOperationsMode.Denied + else + CustomOperationsMode.Allowed - let isCustomOperation nm = tryGetDataForCustomOperation nm |> Option.isSome + let isCustomOperation nm = + tryGetDataForCustomOperation nm |> Option.isSome - let customOperationCheckValidity m f opDatas = + let customOperationCheckValidity m f opDatas = let vs = opDatas |> List.map f let v0 = vs[0] - let opName, _maintainsVarSpaceUsingBind, _maintainsVarSpace, _allowInto, _isLikeZip, _isLikeJoin, _isLikeGroupJoin, _joinConditionWord, _methInfo = opDatas[0] - if not (List.allEqual vs) then - errorR(Error(FSComp.SR.tcCustomOperationInvalid opName, m)) + + let (opName, + _maintainsVarSpaceUsingBind, + _maintainsVarSpace, + _allowInto, + _isLikeZip, + _isLikeJoin, + _isLikeGroupJoin, + _joinConditionWord, + _methInfo) = + opDatas[0] + + if not (List.allEqual vs) then + errorR (Error(FSComp.SR.tcCustomOperationInvalid opName, m)) + v0 // Check for the MaintainsVariableSpace on custom operation - let customOperationMaintainsVarSpace (nm: Ident) = - match tryGetDataForCustomOperation nm with + let customOperationMaintainsVarSpace (nm: Ident) = + match tryGetDataForCustomOperation nm with | None -> false | Some opDatas -> - opDatas |> customOperationCheckValidity nm.idRange (fun (_nm, _maintainsVarSpaceUsingBind, maintainsVarSpace, _allowInto, _isLikeZip, _isLikeJoin, _isLikeGroupJoin, _joinConditionWord, _methInfo) -> maintainsVarSpace) - - let customOperationMaintainsVarSpaceUsingBind (nm: Ident) = - match tryGetDataForCustomOperation nm with + opDatas + |> customOperationCheckValidity + nm.idRange + (fun + (_nm, + _maintainsVarSpaceUsingBind, + maintainsVarSpace, + _allowInto, + _isLikeZip, + _isLikeJoin, + _isLikeGroupJoin, + _joinConditionWord, + _methInfo) -> maintainsVarSpace) + + let customOperationMaintainsVarSpaceUsingBind (nm: Ident) = + match tryGetDataForCustomOperation nm with | None -> false | Some opDatas -> - opDatas |> customOperationCheckValidity nm.idRange (fun (_nm, maintainsVarSpaceUsingBind, _maintainsVarSpace, _allowInto, _isLikeZip, _isLikeJoin, _isLikeGroupJoin, _joinConditionWord, _methInfo) -> maintainsVarSpaceUsingBind) - - let customOperationIsLikeZip (nm: Ident) = - match tryGetDataForCustomOperation nm with + opDatas + |> customOperationCheckValidity + nm.idRange + (fun + (_nm, + maintainsVarSpaceUsingBind, + _maintainsVarSpace, + _allowInto, + _isLikeZip, + _isLikeJoin, + _isLikeGroupJoin, + _joinConditionWord, + _methInfo) -> maintainsVarSpaceUsingBind) + + let customOperationIsLikeZip (nm: Ident) = + match tryGetDataForCustomOperation nm with | None -> false | Some opDatas -> - opDatas |> customOperationCheckValidity nm.idRange (fun (_nm, _maintainsVarSpaceUsingBind, _maintainsVarSpace, _allowInto, isLikeZip, _isLikeJoin, _isLikeGroupJoin, _joinConditionWord, _methInfo) -> isLikeZip) - - let customOperationIsLikeJoin (nm: Ident) = - match tryGetDataForCustomOperation nm with + opDatas + |> customOperationCheckValidity + nm.idRange + (fun + (_nm, + _maintainsVarSpaceUsingBind, + _maintainsVarSpace, + _allowInto, + isLikeZip, + _isLikeJoin, + _isLikeGroupJoin, + _joinConditionWord, + _methInfo) -> isLikeZip) + + let customOperationIsLikeJoin (nm: Ident) = + match tryGetDataForCustomOperation nm with | None -> false | Some opDatas -> - opDatas |> customOperationCheckValidity nm.idRange (fun (_nm, _maintainsVarSpaceUsingBind, _maintainsVarSpace, _allowInto, _isLikeZip, isLikeJoin, _isLikeGroupJoin, _joinConditionWord, _methInfo) -> isLikeJoin) - - let customOperationIsLikeGroupJoin (nm: Ident) = - match tryGetDataForCustomOperation nm with + opDatas + |> customOperationCheckValidity + nm.idRange + (fun + (_nm, + _maintainsVarSpaceUsingBind, + _maintainsVarSpace, + _allowInto, + _isLikeZip, + isLikeJoin, + _isLikeGroupJoin, + _joinConditionWord, + _methInfo) -> isLikeJoin) + + let customOperationIsLikeGroupJoin (nm: Ident) = + match tryGetDataForCustomOperation nm with | None -> false | Some opDatas -> - opDatas |> customOperationCheckValidity nm.idRange (fun (_nm, _maintainsVarSpaceUsingBind, _maintainsVarSpace, _allowInto, _isLikeZip, _isLikeJoin, isLikeGroupJoin, _joinConditionWord, _methInfo) -> isLikeGroupJoin) - - let customOperationJoinConditionWord (nm: Ident) = - match tryGetDataForCustomOperation nm with + opDatas + |> customOperationCheckValidity + nm.idRange + (fun + (_nm, + _maintainsVarSpaceUsingBind, + _maintainsVarSpace, + _allowInto, + _isLikeZip, + _isLikeJoin, + isLikeGroupJoin, + _joinConditionWord, + _methInfo) -> isLikeGroupJoin) + + let customOperationJoinConditionWord (nm: Ident) = + match tryGetDataForCustomOperation nm with | Some opDatas -> - opDatas |> customOperationCheckValidity nm.idRange (fun (_nm, _maintainsVarSpaceUsingBind, _maintainsVarSpace, _allowInto, _isLikeZip, _isLikeJoin, _isLikeGroupJoin, joinConditionWord, _methInfo) -> joinConditionWord) - |> function None -> "on" | Some v -> v - | _ -> "on" - - let customOperationAllowsInto (nm: Ident) = - match tryGetDataForCustomOperation nm with + opDatas + |> customOperationCheckValidity + nm.idRange + (fun + (_nm, + _maintainsVarSpaceUsingBind, + _maintainsVarSpace, + _allowInto, + _isLikeZip, + _isLikeJoin, + _isLikeGroupJoin, + joinConditionWord, + _methInfo) -> joinConditionWord) + |> function + | None -> "on" + | Some v -> v + | _ -> "on" + + let customOperationAllowsInto (nm: Ident) = + match tryGetDataForCustomOperation nm with | None -> false | Some opDatas -> - opDatas |> customOperationCheckValidity nm.idRange (fun (_nm, _maintainsVarSpaceUsingBind, _maintainsVarSpace, allowInto, _isLikeZip, _isLikeJoin, _isLikeGroupJoin, _joinConditionWord, _methInfo) -> allowInto) - - let customOpUsageText nm = + opDatas + |> customOperationCheckValidity + nm.idRange + (fun + (_nm, + _maintainsVarSpaceUsingBind, + _maintainsVarSpace, + allowInto, + _isLikeZip, + _isLikeJoin, + _isLikeGroupJoin, + _joinConditionWord, + _methInfo) -> allowInto) + + let customOpUsageText nm = match tryGetDataForCustomOperation nm with - | Some ((_nm, _maintainsVarSpaceUsingBind, _maintainsVarSpace, _allowInto, isLikeZip, isLikeJoin, isLikeGroupJoin, _joinConditionWord, _methInfo) :: _) -> + | Some((_nm, + _maintainsVarSpaceUsingBind, + _maintainsVarSpace, + _allowInto, + isLikeZip, + isLikeJoin, + isLikeGroupJoin, + _joinConditionWord, + _methInfo) :: _) -> if isLikeGroupJoin then - Some (FSComp.SR.customOperationTextLikeGroupJoin(nm.idText, customOperationJoinConditionWord nm, customOperationJoinConditionWord nm)) + Some( + FSComp.SR.customOperationTextLikeGroupJoin ( + nm.idText, + customOperationJoinConditionWord nm, + customOperationJoinConditionWord nm + ) + ) elif isLikeJoin then - Some (FSComp.SR.customOperationTextLikeJoin(nm.idText, customOperationJoinConditionWord nm, customOperationJoinConditionWord nm)) + Some( + FSComp.SR.customOperationTextLikeJoin ( + nm.idText, + customOperationJoinConditionWord nm, + customOperationJoinConditionWord nm + ) + ) elif isLikeZip then - Some (FSComp.SR.customOperationTextLikeZip(nm.idText)) + Some(FSComp.SR.customOperationTextLikeZip (nm.idText)) else None - | _ -> None + | _ -> None /// Inside the 'query { ... }' use a modified name environment that contains fake 'CustomOperation' entries /// for all custom operations. This adds them to the completion lists and prevents them being used as values inside /// the query. - let env = - if List.isEmpty customOperationMethods then env else - { env with - eNameResEnv = - (env.eNameResEnv, customOperationMethods) - ||> Seq.fold (fun nenv (nm, _, _, _, _, _, _, _, methInfo) -> - AddFakeNameToNameEnv nm nenv (Item.CustomOperation (nm, (fun () -> customOpUsageText (ident (nm, mBuilderVal))), Some methInfo))) } + let env = + if List.isEmpty customOperationMethods then + env + else + { env with + eNameResEnv = + (env.eNameResEnv, customOperationMethods) + ||> Seq.fold (fun nenv (nm, _, _, _, _, _, _, _, methInfo) -> + AddFakeNameToNameEnv + nm + nenv + (Item.CustomOperation(nm, (fun () -> customOpUsageText (ident (nm, mBuilderVal))), Some methInfo))) + } // Environment is needed for completions CallEnvSink cenv.tcSink (comp.Range, env.NameEnv, ad) - let tryGetArgAttribsForCustomOperator (nm: Ident) = - match tryGetDataForCustomOperation nm with - | Some argInfos -> - argInfos - |> List.map (fun (_nm, __maintainsVarSpaceUsingBind, _maintainsVarSpace, _allowInto, _isLikeZip, _isLikeJoin, _isLikeGroupJoin, _joinConditionWord, methInfo) -> - match methInfo.GetParamAttribs(cenv.amap, mWhole) with - | [curriedArgInfo] -> Some curriedArgInfo // one for the actual argument group - | _ -> None) + let tryGetArgAttribsForCustomOperator (nm: Ident) = + match tryGetDataForCustomOperation nm with + | Some argInfos -> + argInfos + |> List.map + (fun + (_nm, + __maintainsVarSpaceUsingBind, + _maintainsVarSpace, + _allowInto, + _isLikeZip, + _isLikeJoin, + _isLikeGroupJoin, + _joinConditionWord, + methInfo) -> + match methInfo.GetParamAttribs(cenv.amap, mWhole) with + | [ curriedArgInfo ] -> Some curriedArgInfo // one for the actual argument group + | _ -> None) |> Some | _ -> None - let tryGetArgInfosForCustomOperator (nm: Ident) = - match tryGetDataForCustomOperation nm with - | Some argInfos -> - argInfos - |> List.map (fun (_nm, __maintainsVarSpaceUsingBind, _maintainsVarSpace, _allowInto, _isLikeZip, _isLikeJoin, _isLikeGroupJoin, _joinConditionWord, methInfo) -> - match methInfo with - | FSMeth(_, _, vref, _) -> - match ArgInfosOfMember cenv.g vref with - | [curriedArgInfo] -> Some curriedArgInfo - | _ -> None - | _ -> None) + let tryGetArgInfosForCustomOperator (nm: Ident) = + match tryGetDataForCustomOperation nm with + | Some argInfos -> + argInfos + |> List.map + (fun + (_nm, + __maintainsVarSpaceUsingBind, + _maintainsVarSpace, + _allowInto, + _isLikeZip, + _isLikeJoin, + _isLikeGroupJoin, + _joinConditionWord, + methInfo) -> + match methInfo with + | FSMeth(_, _, vref, _) -> + match ArgInfosOfMember cenv.g vref with + | [ curriedArgInfo ] -> Some curriedArgInfo + | _ -> None + | _ -> None) |> Some | _ -> None - let tryExpectedArgCountForCustomOperator (nm: Ident) = - match tryGetArgAttribsForCustomOperator nm with + let tryExpectedArgCountForCustomOperator (nm: Ident) = + match tryGetArgAttribsForCustomOperator nm with | None -> None - | Some argInfosForOverloads -> - let nums = argInfosForOverloads |> List.map (function None -> -1 | Some argInfos -> List.length argInfos) + | Some argInfosForOverloads -> + let nums = + argInfosForOverloads + |> List.map (function + | None -> -1 + | Some argInfos -> List.length argInfos) // Prior to 'OverloadsForCustomOperations' we count exact arguments. // // With 'OverloadsForCustomOperations' we don't compute an exact expected argument count // if any arguments are optional, out or ParamArray. - let isSpecial = + let isSpecial = if cenv.g.langVersion.SupportsFeature LanguageFeature.OverloadsForCustomOperations then - argInfosForOverloads |> List.exists (fun info -> - match info with + argInfosForOverloads + |> List.exists (fun info -> + match info with | None -> false - | Some args -> - args |> List.exists (fun (ParamAttribs(isParamArrayArg, _isInArg, isOutArg, optArgInfo, _callerInfo, _reflArgInfo)) -> isParamArrayArg || isOutArg || optArgInfo.IsOptional)) + | Some args -> + args + |> List.exists + (fun (ParamAttribs(isParamArrayArg, _isInArg, isOutArg, optArgInfo, _callerInfo, _reflArgInfo)) -> + isParamArrayArg || isOutArg || optArgInfo.IsOptional)) else false - if not isSpecial && nums |> List.forall (fun v -> v >= 0 && v = nums[0]) then - Some (max (nums[0] - 1) 0) // drop the computation context argument + if not isSpecial && nums |> List.forall (fun v -> v >= 0 && v = nums[0]) then + Some(max (nums[0] - 1) 0) // drop the computation context argument else None // Check for the [] attribute on an argument position - let isCustomOperationProjectionParameter i (nm: Ident) = + let isCustomOperationProjectionParameter i (nm: Ident) = match tryGetArgInfosForCustomOperator nm with | None -> false | Some argInfosForOverloads -> - let vs = - argInfosForOverloads |> List.map (function + let vs = + argInfosForOverloads + |> List.map (function | None -> false - | Some argInfos -> - i < argInfos.Length && - let _, argInfo = List.item i argInfos - HasFSharpAttribute cenv.g cenv.g.attrib_ProjectionParameterAttribute argInfo.Attribs) - if List.allEqual vs then vs[0] - else + | Some argInfos -> + i < argInfos.Length + && let _, argInfo = List.item i argInfos in + HasFSharpAttribute cenv.g cenv.g.attrib_ProjectionParameterAttribute argInfo.Attribs) + + if List.allEqual vs then + vs[0] + else let opDatas = (tryGetDataForCustomOperation nm).Value let opName, _, _, _, _, _, _, _j, _ = opDatas[0] - errorR(Error(FSComp.SR.tcCustomOperationInvalid opName, nm.idRange)) + errorR (Error(FSComp.SR.tcCustomOperationInvalid opName, nm.idRange)) false - let (|ForEachThen|_|) synExpr = - match synExpr with - | SynExpr.ForEach (_spFor, _spIn, SeqExprOnly false, isFromSource, pat1, expr1, SynExpr.Sequential (_, true, clause, rest, _), _) -> - Some (isFromSource, pat1, expr1, clause, rest) + let (|ForEachThen|_|) synExpr = + match synExpr with + | SynExpr.ForEach(_spFor, _spIn, SeqExprOnly false, isFromSource, pat1, expr1, SynExpr.Sequential(_, true, clause, rest, _), _) -> + Some(isFromSource, pat1, expr1, clause, rest) | _ -> None - let (|CustomOpId|_|) predicate synExpr = - match synExpr with + let (|CustomOpId|_|) predicate synExpr = + match synExpr with | SingleIdent nm when isCustomOperation nm && predicate nm -> Some nm | _ -> None // e1 in e2 ('in' is parsed as 'JOIN_IN') - let (|InExpr|_|) synExpr = - match synExpr with - | SynExpr.JoinIn (e1, _, e2, mApp) -> Some (e1, e2, mApp) + let (|InExpr|_|) synExpr = + match synExpr with + | SynExpr.JoinIn(e1, _, e2, mApp) -> Some(e1, e2, mApp) | _ -> None // e1 on e2 (note: 'on' is the 'JoinConditionWord') - let (|OnExpr|_|) nm synExpr = - match tryGetDataForCustomOperation nm with + let (|OnExpr|_|) nm synExpr = + match tryGetDataForCustomOperation nm with | None -> None - | Some _ -> - match synExpr with - | SynExpr.App (_, _, SynExpr.App (_, _, e1, SingleIdent opName, _), e2, _) when opName.idText = customOperationJoinConditionWord nm -> - let item = Item.CustomOperation (opName.idText, (fun () -> None), None) + | Some _ -> + match synExpr with + | SynExpr.App(funcExpr = SynExpr.App(funcExpr = e1; argExpr = SingleIdent opName); argExpr = e2) when + opName.idText = customOperationJoinConditionWord nm + -> + let item = Item.CustomOperation(opName.idText, (fun () -> None), None) CallNameResolutionSink cenv.tcSink (opName.idRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, env.AccessRights) - Some (e1, e2) + Some(e1, e2) | _ -> None // e1 into e2 - let (|IntoSuffix|_|) (e: SynExpr) = - match e with - | SynExpr.App (_, _, SynExpr.App (_, _, x, SingleIdent nm2, _), ExprAsPat intoPat, _) when nm2.idText = CustomOperations.Into -> - Some (x, nm2.idRange, intoPat) - | _ -> - None + let (|IntoSuffix|_|) (e: SynExpr) = + match e with + | SynExpr.App(funcExpr = SynExpr.App(funcExpr = x; argExpr = SingleIdent nm2); argExpr = ExprAsPat intoPat) when + nm2.idText = CustomOperations.Into + -> + Some(x, nm2.idRange, intoPat) + | _ -> None - let arbPat (m: range) = mkSynPatVar None (mkSynId (m.MakeSynthetic()) "_missingVar") + let arbPat (m: range) = + mkSynPatVar None (mkSynId (m.MakeSynthetic()) "_missingVar") - let MatchIntoSuffixOrRecover alreadyGivenError (nm: Ident) synExpr = - match synExpr with - | IntoSuffix (x, intoWordRange, intoPat) -> + let MatchIntoSuffixOrRecover alreadyGivenError (nm: Ident) synExpr = + match synExpr with + | IntoSuffix(x, intoWordRange, intoPat) -> // record the "into" as a custom operation for colorization - let item = Item.CustomOperation ("into", (fun () -> None), None) + let item = Item.CustomOperation("into", (fun () -> None), None) CallNameResolutionSink cenv.tcSink (intoWordRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, env.eAccessRights) (x, intoPat, alreadyGivenError) - | _ -> - if not alreadyGivenError then - errorR(Error(FSComp.SR.tcOperatorIncorrectSyntax(nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) + | _ -> + if not alreadyGivenError then + errorR (Error(FSComp.SR.tcOperatorIncorrectSyntax (nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) + (synExpr, arbPat synExpr.Range, true) - let MatchOnExprOrRecover alreadyGivenError nm (onExpr: SynExpr) = - match onExpr with - | OnExpr nm (innerSource, SynExprParen(keySelectors, _, _, _)) -> - (innerSource, keySelectors) - | _ -> - if not alreadyGivenError then - suppressErrorReporting (fun () -> TcExprOfUnknownType cenv env tpenv onExpr) |> ignore - errorR(Error(FSComp.SR.tcOperatorIncorrectSyntax(nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) - (arbExpr("_innerSource", onExpr.Range), mkSynBifix onExpr.Range "=" (arbExpr("_keySelectors", onExpr.Range)) (arbExpr("_keySelector2", onExpr.Range))) - - let JoinOrGroupJoinOp detector synExpr = - match synExpr with - | SynExpr.App (_, _, CustomOpId detector nm, ExprAsPat innerSourcePat, mJoinCore) -> - Some(nm, innerSourcePat, mJoinCore, false) + let MatchOnExprOrRecover alreadyGivenError nm (onExpr: SynExpr) = + match onExpr with + | OnExpr nm (innerSource, SynExprParen(keySelectors, _, _, _)) -> (innerSource, keySelectors) + | _ -> + if not alreadyGivenError then + suppressErrorReporting (fun () -> TcExprOfUnknownType cenv env tpenv onExpr) + |> ignore + + errorR (Error(FSComp.SR.tcOperatorIncorrectSyntax (nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) + + (arbExpr ("_innerSource", onExpr.Range), + mkSynBifix onExpr.Range "=" (arbExpr ("_keySelectors", onExpr.Range)) (arbExpr ("_keySelector2", onExpr.Range))) + + let JoinOrGroupJoinOp detector synExpr = + match synExpr with + | SynExpr.App(_, _, CustomOpId detector nm, ExprAsPat innerSourcePat, mJoinCore) -> Some(nm, innerSourcePat, mJoinCore, false) // join with bad pattern (gives error on "join" and continues) - | SynExpr.App (_, _, CustomOpId detector nm, _innerSourcePatExpr, mJoinCore) -> - errorR(Error(FSComp.SR.tcBinaryOperatorRequiresVariable(nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) + | SynExpr.App(_, _, CustomOpId detector nm, _innerSourcePatExpr, mJoinCore) -> + errorR (Error(FSComp.SR.tcBinaryOperatorRequiresVariable (nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) Some(nm, arbPat mJoinCore, mJoinCore, true) // join (without anything after - gives error on "join" and continues) - | CustomOpId detector nm -> - errorR(Error(FSComp.SR.tcBinaryOperatorRequiresVariable(nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) + | CustomOpId detector nm -> + errorR (Error(FSComp.SR.tcBinaryOperatorRequiresVariable (nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) Some(nm, arbPat synExpr.Range, synExpr.Range, true) - | _ -> - None - // JoinOrGroupJoinOp customOperationIsLikeJoin + | _ -> None + // JoinOrGroupJoinOp customOperationIsLikeJoin - let (|JoinOp|_|) synExpr = JoinOrGroupJoinOp customOperationIsLikeJoin synExpr + let (|JoinOp|_|) synExpr = + JoinOrGroupJoinOp customOperationIsLikeJoin synExpr - let (|GroupJoinOp|_|) synExpr = JoinOrGroupJoinOp customOperationIsLikeGroupJoin synExpr + let (|GroupJoinOp|_|) synExpr = + JoinOrGroupJoinOp customOperationIsLikeGroupJoin synExpr - let arbKeySelectors m = mkSynBifix m "=" (arbExpr("_keySelectors", m)) (arbExpr("_keySelector2", m)) + let arbKeySelectors m = + mkSynBifix m "=" (arbExpr ("_keySelectors", m)) (arbExpr ("_keySelector2", m)) - let (|JoinExpr|_|) synExpr = - match synExpr with - | InExpr (JoinOp(nm, innerSourcePat, _, alreadyGivenError), onExpr, mJoinCore) -> + let (|JoinExpr|_|) synExpr = + match synExpr with + | InExpr(JoinOp(nm, innerSourcePat, _, alreadyGivenError), onExpr, mJoinCore) -> let innerSource, keySelectors = MatchOnExprOrRecover alreadyGivenError nm onExpr Some(nm, innerSourcePat, innerSource, keySelectors, mJoinCore) - | JoinOp (nm, innerSourcePat, mJoinCore, alreadyGivenError) -> - if alreadyGivenError then - errorR(Error(FSComp.SR.tcOperatorRequiresIn(nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) - Some (nm, innerSourcePat, arbExpr("_innerSource", synExpr.Range), arbKeySelectors synExpr.Range, mJoinCore) + | JoinOp(nm, innerSourcePat, mJoinCore, alreadyGivenError) -> + if alreadyGivenError then + errorR (Error(FSComp.SR.tcOperatorRequiresIn (nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) + + Some(nm, innerSourcePat, arbExpr ("_innerSource", synExpr.Range), arbKeySelectors synExpr.Range, mJoinCore) | _ -> None - let (|GroupJoinExpr|_|) synExpr = - match synExpr with - | InExpr (GroupJoinOp (nm, innerSourcePat, _, alreadyGivenError), intoExpr, mGroupJoinCore) -> - let onExpr, intoPat, alreadyGivenError = MatchIntoSuffixOrRecover alreadyGivenError nm intoExpr - let innerSource, keySelectors = MatchOnExprOrRecover alreadyGivenError nm onExpr - Some (nm, innerSourcePat, innerSource, keySelectors, intoPat, mGroupJoinCore) - | GroupJoinOp (nm, innerSourcePat, mGroupJoinCore, alreadyGivenError) -> - if alreadyGivenError then - errorR(Error(FSComp.SR.tcOperatorRequiresIn(nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) - Some (nm, innerSourcePat, arbExpr("_innerSource", synExpr.Range), arbKeySelectors synExpr.Range, arbPat synExpr.Range, mGroupJoinCore) - | _ -> - None + let (|GroupJoinExpr|_|) synExpr = + match synExpr with + | InExpr(GroupJoinOp(nm, innerSourcePat, _, alreadyGivenError), intoExpr, mGroupJoinCore) -> + let onExpr, intoPat, alreadyGivenError = + MatchIntoSuffixOrRecover alreadyGivenError nm intoExpr + let innerSource, keySelectors = MatchOnExprOrRecover alreadyGivenError nm onExpr + Some(nm, innerSourcePat, innerSource, keySelectors, intoPat, mGroupJoinCore) + | GroupJoinOp(nm, innerSourcePat, mGroupJoinCore, alreadyGivenError) -> + if alreadyGivenError then + errorR (Error(FSComp.SR.tcOperatorRequiresIn (nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) + + Some( + nm, + innerSourcePat, + arbExpr ("_innerSource", synExpr.Range), + arbKeySelectors synExpr.Range, + arbPat synExpr.Range, + mGroupJoinCore + ) + | _ -> None - let (|JoinOrGroupJoinOrZipClause|_|) synExpr = + let (|JoinOrGroupJoinOrZipClause|_|) synExpr = - match synExpr with + match synExpr with // join innerSourcePat in innerSource on (keySelector1 = keySelector2) - | JoinExpr (nm, innerSourcePat, innerSource, keySelectors, mJoinCore) -> + | JoinExpr(nm, innerSourcePat, innerSource, keySelectors, mJoinCore) -> Some(nm, innerSourcePat, innerSource, Some keySelectors, None, mJoinCore) // groupJoin innerSourcePat in innerSource on (keySelector1 = keySelector2) into intoPat - | GroupJoinExpr (nm, innerSourcePat, innerSource, keySelectors, intoPat, mGroupJoinCore) -> + | GroupJoinExpr(nm, innerSourcePat, innerSource, keySelectors, intoPat, mGroupJoinCore) -> Some(nm, innerSourcePat, innerSource, Some keySelectors, Some intoPat, mGroupJoinCore) - // zip intoPat in secondSource - | InExpr (SynExpr.App (_, _, CustomOpId customOperationIsLikeZip nm, ExprAsPat secondSourcePat, _), secondSource, mZipCore) -> + // zip intoPat in secondSource + | InExpr(SynExpr.App(_, _, CustomOpId customOperationIsLikeZip nm, ExprAsPat secondSourcePat, _), secondSource, mZipCore) -> Some(nm, secondSourcePat, secondSource, None, None, mZipCore) // zip (without secondSource or in - gives error) - | CustomOpId customOperationIsLikeZip nm -> - errorR(Error(FSComp.SR.tcOperatorIncorrectSyntax(nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) - Some(nm, arbPat synExpr.Range, arbExpr("_secondSource", synExpr.Range), None, None, synExpr.Range) + | CustomOpId customOperationIsLikeZip nm -> + errorR (Error(FSComp.SR.tcOperatorIncorrectSyntax (nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) + Some(nm, arbPat synExpr.Range, arbExpr ("_secondSource", synExpr.Range), None, None, synExpr.Range) // zip secondSource (without in - gives error) - | SynExpr.App (_, _, CustomOpId customOperationIsLikeZip nm, ExprAsPat secondSourcePat, mZipCore) -> - errorR(Error(FSComp.SR.tcOperatorIncorrectSyntax(nm.idText, Option.get (customOpUsageText nm)), mZipCore)) - Some(nm, secondSourcePat, arbExpr("_innerSource", synExpr.Range), None, None, mZipCore) + | SynExpr.App(_, _, CustomOpId customOperationIsLikeZip nm, ExprAsPat secondSourcePat, mZipCore) -> + errorR (Error(FSComp.SR.tcOperatorIncorrectSyntax (nm.idText, Option.get (customOpUsageText nm)), mZipCore)) + Some(nm, secondSourcePat, arbExpr ("_innerSource", synExpr.Range), None, None, mZipCore) - | _ -> - None + | _ -> None - let (|ForEachThenJoinOrGroupJoinOrZipClause|_|) strict synExpr = - match synExpr with - | ForEachThen (isFromSource, firstSourcePat, firstSource, JoinOrGroupJoinOrZipClause(nm, secondSourcePat, secondSource, keySelectorsOpt, pat3opt, mOpCore), innerComp) - when - (let _firstSourceSimplePats, later1 = - use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - SimplePatsOfPat cenv.synArgNameGenerator firstSourcePat - Option.isNone later1) -> - Some (isFromSource, firstSourcePat, firstSource, nm, secondSourcePat, secondSource, keySelectorsOpt, pat3opt, mOpCore, innerComp) - - | JoinOrGroupJoinOrZipClause(nm, pat2, expr2, expr3, pat3opt, mOpCore) when strict -> - errorR(Error(FSComp.SR.tcBinaryOperatorRequiresBody(nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) - Some (true, arbPat synExpr.Range, arbExpr("_outerSource", synExpr.Range), nm, pat2, expr2, expr3, pat3opt, mOpCore, arbExpr("_innerComp", synExpr.Range)) - - | _ -> - None + let (|ForEachThenJoinOrGroupJoinOrZipClause|_|) strict synExpr = + match synExpr with + | ForEachThen(isFromSource, + firstSourcePat, + firstSource, + JoinOrGroupJoinOrZipClause(nm, secondSourcePat, secondSource, keySelectorsOpt, pat3opt, mOpCore), + innerComp) when + (let _firstSourceSimplePats, later1 = + use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink + SimplePatsOfPat cenv.synArgNameGenerator firstSourcePat + + Option.isNone later1) + -> + Some(isFromSource, firstSourcePat, firstSource, nm, secondSourcePat, secondSource, keySelectorsOpt, pat3opt, mOpCore, innerComp) + + | JoinOrGroupJoinOrZipClause(nm, pat2, expr2, expr3, pat3opt, mOpCore) when strict -> + errorR (Error(FSComp.SR.tcBinaryOperatorRequiresBody (nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) + + Some( + true, + arbPat synExpr.Range, + arbExpr ("_outerSource", synExpr.Range), + nm, + pat2, + expr2, + expr3, + pat3opt, + mOpCore, + arbExpr ("_innerComp", synExpr.Range) + ) + + | _ -> None - let (|StripApps|) e = - let rec strip e = - match e with - | SynExpr.FromParseError (SynExpr.App (_, _, f, arg, _), _) - | SynExpr.App (_, _, f, arg, _) -> - let g, acc = strip f - g, (arg :: acc) + let (|StripApps|) e = + let rec strip e = + match e with + | SynExpr.FromParseError(SynExpr.App(funcExpr = f; argExpr = arg), _) + | SynExpr.App(funcExpr = f; argExpr = arg) -> + let g, acc = strip f + g, (arg :: acc) | _ -> e, [] + let g, acc = strip e g, List.rev acc - let (|OptionalIntoSuffix|) e = - match e with - | IntoSuffix (body, intoWordRange, intoInfo) -> (body, Some (intoWordRange, intoInfo)) + let (|OptionalIntoSuffix|) e = + match e with + | IntoSuffix(body, intoWordRange, intoInfo) -> (body, Some(intoWordRange, intoInfo)) | body -> (body, None) - let (|CustomOperationClause|_|) e = - match e with - | OptionalIntoSuffix(StripApps(SingleIdent nm, _) as core, intoOpt) when isCustomOperation nm -> + let (|CustomOperationClause|_|) e = + match e with + | OptionalIntoSuffix(StripApps(SingleIdent nm, _) as core, intoOpt) when isCustomOperation nm -> // Now we know we have a custom operation, commit the name resolution - let intoInfoOpt = - match intoOpt with - | Some (intoWordRange, intoInfo) -> - let item = Item.CustomOperation ("into", (fun () -> None), None) - CallNameResolutionSink cenv.tcSink (intoWordRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, env.eAccessRights) + let intoInfoOpt = + match intoOpt with + | Some(intoWordRange, intoInfo) -> + let item = Item.CustomOperation("into", (fun () -> None), None) + + CallNameResolutionSink + cenv.tcSink + (intoWordRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, env.eAccessRights) + Some intoInfo | None -> None - Some (nm, Option.get (tryGetDataForCustomOperation nm), core, core.Range, intoInfoOpt) + Some(nm, Option.get (tryGetDataForCustomOperation nm), core, core.Range, intoInfoOpt) | _ -> None - let mkSynLambda p e m = SynExpr.Lambda (false, false, p, e, None, m, SynExprLambdaTrivia.Zero) + let mkSynLambda p e m = + SynExpr.Lambda(false, false, p, e, None, m, SynExprLambdaTrivia.Zero) - let mkExprForVarSpace m (patvs: Val list) = - match patvs with - | [] -> SynExpr.Const (SynConst.Unit, m) - | [v] -> SynExpr.Ident v.Id - | vs -> SynExpr.Tuple (false, (vs |> List.map (fun v -> SynExpr.Ident(v.Id))), [], m) + let mkExprForVarSpace m (patvs: Val list) = + match patvs with + | [] -> SynExpr.Const(SynConst.Unit, m) + | [ v ] -> SynExpr.Ident v.Id + | vs -> SynExpr.Tuple(false, (vs |> List.map (fun v -> SynExpr.Ident(v.Id))), [], m) - let mkSimplePatForVarSpace m (patvs: Val list) = - let spats = - match patvs with + let mkSimplePatForVarSpace m (patvs: Val list) = + let spats = + match patvs with | [] -> [] - | [v] -> [mkSynSimplePatVar false v.Id] + | [ v ] -> [ mkSynSimplePatVar false v.Id ] | vs -> vs |> List.map (fun v -> mkSynSimplePatVar false v.Id) - SynSimplePats.SimplePats (spats, [], m) - let mkPatForVarSpace m (patvs: Val list) = - match patvs with - | [] -> SynPat.Const (SynConst.Unit, m) - | [v] -> mkSynPatVar None v.Id + SynSimplePats.SimplePats(spats, [], m) + + let mkPatForVarSpace m (patvs: Val list) = + match patvs with + | [] -> SynPat.Const(SynConst.Unit, m) + | [ v ] -> mkSynPatVar None v.Id | vs -> SynPat.Tuple(false, (vs |> List.map (fun x -> mkSynPatVar None x.Id)), [], m) - let (|OptionalSequential|) e = - match e with - | SynExpr.Sequential (_sp, true, dataComp1, dataComp2, _) -> (dataComp1, Some dataComp2) + let (|OptionalSequential|) e = + match e with + | SynExpr.Sequential(_sp, true, dataComp1, dataComp2, _) -> (dataComp1, Some dataComp2) | _ -> (e, None) // "cexpr; cexpr" is treated as builder.Combine(cexpr1, cexpr1) // This is not pretty - we have to decide which range markers we use for the calls to Combine and Delay // NOTE: we should probably suppress these sequence points altogether - let rangeForCombine innerComp1 = + let rangeForCombine innerComp1 = let m = - match innerComp1 with - | SynExpr.IfThenElse (trivia={ IfToThenRange = mIfToThen }) -> mIfToThen - | SynExpr.Match (matchDebugPoint=DebugPointAtBinding.Yes mMatch) -> mMatch - | SynExpr.TryWith (trivia={ TryKeyword = mTry }) -> mTry - | SynExpr.TryFinally (trivia={ TryKeyword = mTry }) -> mTry - | SynExpr.For (forDebugPoint=DebugPointAtFor.Yes mBind) -> mBind - | SynExpr.ForEach (forDebugPoint=DebugPointAtFor.Yes mBind) -> mBind - | SynExpr.While (whileDebugPoint=DebugPointAtWhile.Yes mWhile) -> mWhile + match innerComp1 with + | SynExpr.IfThenElse(trivia = { IfToThenRange = mIfToThen }) -> mIfToThen + | SynExpr.Match(matchDebugPoint = DebugPointAtBinding.Yes mMatch) -> mMatch + | SynExpr.TryWith(trivia = { TryKeyword = mTry }) -> mTry + | SynExpr.TryFinally(trivia = { TryKeyword = mTry }) -> mTry + | SynExpr.For(forDebugPoint = DebugPointAtFor.Yes mBind) -> mBind + | SynExpr.ForEach(forDebugPoint = DebugPointAtFor.Yes mBind) -> mBind + | SynExpr.While(whileDebugPoint = DebugPointAtWhile.Yes mWhile) -> mWhile | _ -> innerComp1.Range m.NoteSourceConstruct(NotedSourceConstruct.Combine) // Check for 'where x > y', 'select x, y' and other mis-applications of infix operators, give a good error message, and return a flag - let checkForBinaryApp comp = - match comp with - | StripApps(SingleIdent nm, [StripApps(SingleIdent nm2, args); arg2]) when - IsLogicalInfixOpName nm.idText && - (match tryExpectedArgCountForCustomOperator nm2 with Some n -> n > 0 | _ -> false) && - not (List.isEmpty args) -> - let estimatedRangeOfIntendedLeftAndRightArguments = unionRanges (List.last args).Range arg2.Range - errorR(Error(FSComp.SR.tcUnrecognizedQueryBinaryOperator(), estimatedRangeOfIntendedLeftAndRightArguments)) + let checkForBinaryApp comp = + match comp with + | StripApps(SingleIdent nm, [ StripApps(SingleIdent nm2, args); arg2 ]) when + IsLogicalInfixOpName nm.idText + && (match tryExpectedArgCountForCustomOperator nm2 with + | Some n -> n > 0 + | _ -> false) + && not (List.isEmpty args) + -> + let estimatedRangeOfIntendedLeftAndRightArguments = + unionRanges (List.last args).Range arg2.Range + + errorR (Error(FSComp.SR.tcUnrecognizedQueryBinaryOperator (), estimatedRangeOfIntendedLeftAndRightArguments)) true - | SynExpr.Tuple (false, StripApps(SingleIdent nm2, args) :: _, _, m) when - (match tryExpectedArgCountForCustomOperator nm2 with Some n -> n > 0 | _ -> false) && - not (List.isEmpty args) -> - let estimatedRangeOfIntendedLeftAndRightArguments = unionRanges (List.last args).Range m.EndRange - errorR(Error(FSComp.SR.tcUnrecognizedQueryBinaryOperator(), estimatedRangeOfIntendedLeftAndRightArguments)) + | SynExpr.Tuple(false, StripApps(SingleIdent nm2, args) :: _, _, m) when + (match tryExpectedArgCountForCustomOperator nm2 with + | Some n -> n > 0 + | _ -> false) + && not (List.isEmpty args) + -> + let estimatedRangeOfIntendedLeftAndRightArguments = + unionRanges (List.last args).Range m.EndRange + + errorR (Error(FSComp.SR.tcUnrecognizedQueryBinaryOperator (), estimatedRangeOfIntendedLeftAndRightArguments)) true - | _ -> - false - - let addVarsToVarSpace (varSpace: LazyWithContext) f = - LazyWithContext.Create - ((fun m -> - let (patvs: Val list, env) = varSpace.Force m - let vs, envinner = f m env - let patvs = List.append patvs (vs |> List.filter (fun v -> not (patvs |> List.exists (fun v2 -> v.LogicalName = v2.LogicalName)))) - patvs, envinner), - id) + | _ -> false + + let addVarsToVarSpace (varSpace: LazyWithContext) f = + LazyWithContext.Create( + (fun m -> + let (patvs: Val list, env) = varSpace.Force m + let vs, envinner = f m env + + let patvs = + List.append + patvs + (vs + |> List.filter (fun v -> not (patvs |> List.exists (fun v2 -> v.LogicalName = v2.LogicalName)))) + + patvs, envinner), + id + ) // Flag that a debug point should get emitted prior to both the evaluation of 'rhsExpr' and the call to Using let addBindDebugPoint spBind e = match spBind with - | DebugPointAtBinding.Yes m -> - SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes m, false, e) + | DebugPointAtBinding.Yes m -> SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes m, false, e) | _ -> e - let emptyVarSpace = LazyWithContext.NotLazy ([], env) + let emptyVarSpace = LazyWithContext.NotLazy([], env) // If there are no 'yield' in the computation expression, and the builder supports 'Yield', // then allow the type-directed rule interpreting non-unit-typed expressions in statement // positions as 'yield'. 'yield!' may be present in the computation expression. let enableImplicitYield = cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield - && (hasMethInfo "Yield" && hasMethInfo "Combine" && hasMethInfo "Delay" && YieldFree cenv comp) - - // q - a flag indicating if custom operators are allowed. They are not allowed inside try/with, try/finally, if/then/else etc. - // varSpace - a lazy data structure indicating the variables bound so far in the overall computation - // comp - the computation expression being analyzed - // translatedCtxt - represents the translation of the context in which the computation expression 'comp' occurs, up to a - // hole to be filled by (part of) the results of translating 'comp'. - let rec tryTrans firstTry q varSpace comp translatedCtxt = + && (hasMethInfo "Yield" + && hasMethInfo "Combine" + && hasMethInfo "Delay" + && YieldFree cenv comp) + + /// + /// Try translate the syntax sugar + /// + /// + /// a flag indicating if custom operators are allowed. They are not allowed inside try/with, try/finally, if/then/else etc. + /// a lazy data structure indicating the variables bound so far in the overall computation + /// the computation expression being analyzed + /// represents the translation of the context in which the computation expression 'comp' occurs, + /// up to a hole to be filled by (part of) the results of translating 'comp'. + let rec tryTrans + (firstTry: CompExprTranslationPass) + (q: CustomOperationsMode) + (varSpace: LazyWithContext<(Val list * TcEnv), range>) + (comp: SynExpr) + (translatedCtxt: SynExpr -> SynExpr) + : SynExpr option = // Guard the stack for deeply nested computation expressions - cenv.stackGuard.Guard <| fun () -> - - match comp with - - // for firstSourcePat in firstSource do - // join secondSourcePat in expr2 on (expr3 = expr4) - // ... - // --> - // join expr1 expr2 (fun firstSourcePat -> expr3) (fun secondSourcePat -> expr4) (fun firstSourcePat secondSourcePat -> ...) - - // for firstSourcePat in firstSource do - // groupJoin secondSourcePat in expr2 on (expr3 = expr4) into groupPat - // ... - // --> - // groupJoin expr1 expr2 (fun firstSourcePat -> expr3) (fun secondSourcePat -> expr4) (fun firstSourcePat groupPat -> ...) - - // for firstSourcePat in firstSource do - // zip secondSource into secondSourcePat - // ... - // --> - // zip expr1 expr2 (fun pat1 pat3 -> ...) - | ForEachThenJoinOrGroupJoinOrZipClause true (isFromSource, firstSourcePat, firstSource, nm, secondSourcePat, secondSource, keySelectorsOpt, secondResultPatOpt, mOpCore, innerComp) -> - - if q = CustomOperationsMode.Denied then error(Error(FSComp.SR.tcCustomOperationMayNotBeUsedHere(), nm.idRange)) - let firstSource = mkSourceExprConditional isFromSource firstSource - let secondSource = mkSourceExpr secondSource - - // Add the variables to the variable space, on demand - let varSpaceWithFirstVars = - addVarsToVarSpace varSpace (fun _mCustomOp env -> - use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType g) env tpenv firstSourcePat None - vspecs, envinner) - - let varSpaceWithSecondVars = - addVarsToVarSpace varSpaceWithFirstVars (fun _mCustomOp env -> - use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType g) env tpenv secondSourcePat None - vspecs, envinner) - - let varSpaceWithGroupJoinVars = - match secondResultPatOpt with - | Some pat3 -> - addVarsToVarSpace varSpaceWithFirstVars (fun _mCustomOp env -> + cenv.stackGuard.Guard + <| fun () -> + + match comp with + + // for firstSourcePat in firstSource do + // join secondSourcePat in expr2 on (expr3 = expr4) + // ... + // --> + // join expr1 expr2 (fun firstSourcePat -> expr3) (fun secondSourcePat -> expr4) (fun firstSourcePat secondSourcePat -> ...) + + // for firstSourcePat in firstSource do + // groupJoin secondSourcePat in expr2 on (expr3 = expr4) into groupPat + // ... + // --> + // groupJoin expr1 expr2 (fun firstSourcePat -> expr3) (fun secondSourcePat -> expr4) (fun firstSourcePat groupPat -> ...) + + // for firstSourcePat in firstSource do + // zip secondSource into secondSourcePat + // ... + // --> + // zip expr1 expr2 (fun pat1 pat3 -> ...) + | ForEachThenJoinOrGroupJoinOrZipClause true (isFromSource, + firstSourcePat, + firstSource, + nm, + secondSourcePat, + secondSource, + keySelectorsOpt, + secondResultPatOpt, + mOpCore, + innerComp) -> + + if q = CustomOperationsMode.Denied then + error (Error(FSComp.SR.tcCustomOperationMayNotBeUsedHere (), nm.idRange)) + + let firstSource = mkSourceExprConditional isFromSource firstSource + let secondSource = mkSourceExpr secondSource + + // Add the variables to the variable space, on demand + let varSpaceWithFirstVars = + addVarsToVarSpace varSpace (fun _mCustomOp env -> use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType g) env tpenv pat3 None - vspecs, envinner) - | None -> varSpace - let firstSourceSimplePats, later1 = SimplePatsOfPat cenv.synArgNameGenerator firstSourcePat - let secondSourceSimplePats, later2 = SimplePatsOfPat cenv.synArgNameGenerator secondSourcePat + let _, _, vspecs, envinner, _ = + TcMatchPattern cenv (NewInferenceType g) env tpenv firstSourcePat None - if Option.isSome later1 then errorR (Error (FSComp.SR.tcJoinMustUseSimplePattern(nm.idText), firstSourcePat.Range)) - if Option.isSome later2 then errorR (Error (FSComp.SR.tcJoinMustUseSimplePattern(nm.idText), secondSourcePat.Range)) + vspecs, envinner) - // check 'join' or 'groupJoin' or 'zip' is permitted for this builder - match tryGetDataForCustomOperation nm with - | None -> error(Error(FSComp.SR.tcMissingCustomOperation(nm.idText), nm.idRange)) - | Some opDatas -> - let opName, _, _, _, _, _, _, _, methInfo = opDatas[0] + let varSpaceWithSecondVars = + addVarsToVarSpace varSpaceWithFirstVars (fun _mCustomOp env -> + use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - // Record the resolution of the custom operation for posterity - let item = Item.CustomOperation (opName, (fun () -> customOpUsageText nm), Some methInfo) + let _, _, vspecs, envinner, _ = + TcMatchPattern cenv (NewInferenceType g) env tpenv secondSourcePat None - // FUTURE: consider whether we can do better than emptyTyparInst here, in order to display instantiations - // of type variables in the quick info provided in the IDE. - CallNameResolutionSink cenv.tcSink (nm.idRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, env.eAccessRights) + vspecs, envinner) - let mkJoinExpr keySelector1 keySelector2 innerPat e = - let mSynthetic = mOpCore.MakeSynthetic() - mkSynCall methInfo.DisplayName mOpCore - [ firstSource - secondSource - mkSynLambda firstSourceSimplePats keySelector1 mSynthetic - mkSynLambda secondSourceSimplePats keySelector2 mSynthetic - mkSynLambda firstSourceSimplePats (mkSynLambda innerPat e mSynthetic) mSynthetic ] - - let mkZipExpr e = - let mSynthetic = mOpCore.MakeSynthetic() - mkSynCall methInfo.DisplayName mOpCore - [ firstSource - secondSource - mkSynLambda firstSourceSimplePats (mkSynLambda secondSourceSimplePats e mSynthetic) mSynthetic ] - - // wraps given expression into sequence with result produced by arbExpr so result will look like: - // l; SynExpr.ArbitraryAfterError (...) - // this allows to handle cases like 'on (a > b)' // '>' is not permitted as correct join relation - // after wrapping a and b can still be typechecked (so we'll have correct completion inside 'on' part) - // but presence of SynExpr.ArbitraryAfterError allows to avoid errors about incompatible types in cases like - // query { - // for a in [1] do - // join b in [""] on (a > b) - // } - // if we typecheck raw 'a' and 'b' then we'll end up with 2 errors: - // 1. incorrect join relation - // 2. incompatible types: int and string - // with SynExpr.ArbitraryAfterError we have only first one - let wrapInArbErrSequence l caption = - SynExpr.Sequential (DebugPointAtSequential.SuppressNeither, true, l, (arbExpr(caption, l.Range.EndRange)), l.Range) - - let mkOverallExprGivenVarSpaceExpr, varSpaceInner = - - let isNullableOp opId = - match ConvertValLogicalNameToDisplayNameCore opId with - | "?=" | "=?" | "?=?" -> true - | _ -> false - - match secondResultPatOpt, keySelectorsOpt with - // groupJoin - | Some secondResultPat, Some relExpr when customOperationIsLikeGroupJoin nm -> - let secondResultSimplePats, later3 = SimplePatsOfPat cenv.synArgNameGenerator secondResultPat - if Option.isSome later3 then errorR (Error (FSComp.SR.tcJoinMustUseSimplePattern(nm.idText), secondResultPat.Range)) - match relExpr with - | JoinRelation cenv env (keySelector1, keySelector2) -> - mkJoinExpr keySelector1 keySelector2 secondResultSimplePats, varSpaceWithGroupJoinVars - | BinOpExpr (opId, l, r) -> - if isNullableOp opId.idText then - // When we cannot resolve NullableOps, recommend the relevant namespace to be added - errorR(Error(FSComp.SR.cannotResolveNullableOperators(ConvertValLogicalNameToDisplayNameCore opId.idText), relExpr.Range)) - else - errorR(Error(FSComp.SR.tcInvalidRelationInJoin(nm.idText), relExpr.Range)) - let l = wrapInArbErrSequence l "_keySelector1" - let r = wrapInArbErrSequence r "_keySelector2" - // this is not correct JoinRelation but it is still binary operation - // we've already reported error now we can use operands of binary operation as join components - mkJoinExpr l r secondResultSimplePats, varSpaceWithGroupJoinVars - | _ -> - errorR(Error(FSComp.SR.tcInvalidRelationInJoin(nm.idText), relExpr.Range)) - // since the shape of relExpr doesn't match our expectations (JoinRelation) - // then we assume that this is l.h.s. of the join relation - // so typechecker will treat relExpr as body of outerKeySelector lambda parameter in GroupJoin method - mkJoinExpr relExpr (arbExpr("_keySelector2", relExpr.Range)) secondResultSimplePats, varSpaceWithGroupJoinVars - - | None, Some relExpr when customOperationIsLikeJoin nm -> - match relExpr with - | JoinRelation cenv env (keySelector1, keySelector2) -> - mkJoinExpr keySelector1 keySelector2 secondSourceSimplePats, varSpaceWithSecondVars - | BinOpExpr (opId, l, r) -> - if isNullableOp opId.idText then - // When we cannot resolve NullableOps, recommend the relevant namespace to be added - errorR(Error(FSComp.SR.cannotResolveNullableOperators(ConvertValLogicalNameToDisplayNameCore opId.idText), relExpr.Range)) - else - errorR(Error(FSComp.SR.tcInvalidRelationInJoin(nm.idText), relExpr.Range)) - // this is not correct JoinRelation but it is still binary operation - // we've already reported error now we can use operands of binary operation as join components - let l = wrapInArbErrSequence l "_keySelector1" - let r = wrapInArbErrSequence r "_keySelector2" - mkJoinExpr l r secondSourceSimplePats, varSpaceWithGroupJoinVars - | _ -> - errorR(Error(FSComp.SR.tcInvalidRelationInJoin(nm.idText), relExpr.Range)) - // since the shape of relExpr doesn't match our expectations (JoinRelation) - // then we assume that this is l.h.s. of the join relation - // so typechecker will treat relExpr as body of outerKeySelector lambda parameter in Join method - mkJoinExpr relExpr (arbExpr("_keySelector2", relExpr.Range)) secondSourceSimplePats, varSpaceWithGroupJoinVars - - | None, None when customOperationIsLikeZip nm -> - mkZipExpr, varSpaceWithSecondVars - - | _ -> - assert false - failwith "unreachable" - - - // Case from C# spec: A query expression with a join clause with an into followed by something other than a select clause - // Case from C# spec: A query expression with a join clause without an into followed by something other than a select clause - let valsInner, _env = varSpaceInner.Force mOpCore - let varSpaceExpr = mkExprForVarSpace mOpCore valsInner - let varSpacePat = mkPatForVarSpace mOpCore valsInner - let joinExpr = mkOverallExprGivenVarSpaceExpr varSpaceExpr - let consumingExpr = SynExpr.ForEach (DebugPointAtFor.No, DebugPointAtInOrTo.No, SeqExprOnly false, false, varSpacePat, joinExpr, innerComp, mOpCore) - Some (trans CompExprTranslationPass.Initial q varSpaceInner consumingExpr translatedCtxt) - - | SynExpr.ForEach (spFor, spIn, SeqExprOnly _seqExprOnly, isFromSource, pat, sourceExpr, innerComp, _mEntireForEach) -> - let sourceExpr = - match RewriteRangeExpr sourceExpr with - | Some e -> e - | None -> sourceExpr - let wrappedSourceExpr = mkSourceExprConditional isFromSource sourceExpr + let varSpaceWithGroupJoinVars = + match secondResultPatOpt with + | Some pat3 -> + addVarsToVarSpace varSpaceWithFirstVars (fun _mCustomOp env -> + use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let mFor = - match spFor with - | DebugPointAtFor.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.For) - | DebugPointAtFor.No -> pat.Range + let _, _, vspecs, envinner, _ = + TcMatchPattern cenv (NewInferenceType g) env tpenv pat3 None - // For computation expressions, 'in' or 'to' is hit on each MoveNext. - // To support this a named debug point for the "in" keyword is available to inlined code. - match spIn with - | DebugPointAtInOrTo.Yes mIn -> - cenv.namedDebugPointsForInlinedCode[{Range=mFor; Name="ForLoop.InOrToKeyword"}] <- mIn - | _ -> () + vspecs, envinner) + | None -> varSpace + + let firstSourceSimplePats, later1 = + SimplePatsOfPat cenv.synArgNameGenerator firstSourcePat + + let secondSourceSimplePats, later2 = + SimplePatsOfPat cenv.synArgNameGenerator secondSourcePat + + if Option.isSome later1 then + errorR (Error(FSComp.SR.tcJoinMustUseSimplePattern (nm.idText), firstSourcePat.Range)) + + if Option.isSome later2 then + errorR (Error(FSComp.SR.tcJoinMustUseSimplePattern (nm.idText), secondSourcePat.Range)) + + // check 'join' or 'groupJoin' or 'zip' is permitted for this builder + match tryGetDataForCustomOperation nm with + | None -> error (Error(FSComp.SR.tcMissingCustomOperation (nm.idText), nm.idRange)) + | Some opDatas -> + let opName, _, _, _, _, _, _, _, methInfo = opDatas[0] + + // Record the resolution of the custom operation for posterity + let item = + Item.CustomOperation(opName, (fun () -> customOpUsageText nm), Some methInfo) + + // FUTURE: consider whether we can do better than emptyTyparInst here, in order to display instantiations + // of type variables in the quick info provided in the IDE. + CallNameResolutionSink cenv.tcSink (nm.idRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, env.eAccessRights) + + let mkJoinExpr keySelector1 keySelector2 innerPat e = + let mSynthetic = mOpCore.MakeSynthetic() + + mkSynCall + methInfo.DisplayName + mOpCore + [ + firstSource + secondSource + mkSynLambda firstSourceSimplePats keySelector1 mSynthetic + mkSynLambda secondSourceSimplePats keySelector2 mSynthetic + mkSynLambda firstSourceSimplePats (mkSynLambda innerPat e mSynthetic) mSynthetic + ] + + let mkZipExpr e = + let mSynthetic = mOpCore.MakeSynthetic() + + mkSynCall + methInfo.DisplayName + mOpCore + [ + firstSource + secondSource + mkSynLambda firstSourceSimplePats (mkSynLambda secondSourceSimplePats e mSynthetic) mSynthetic + ] + + // wraps given expression into sequence with result produced by arbExpr so result will look like: + // l; SynExpr.ArbitraryAfterError (...) + // this allows to handle cases like 'on (a > b)' // '>' is not permitted as correct join relation + // after wrapping a and b can still be typechecked (so we'll have correct completion inside 'on' part) + // but presence of SynExpr.ArbitraryAfterError allows to avoid errors about incompatible types in cases like + // query { + // for a in [1] do + // join b in [""] on (a > b) + // } + // if we typecheck raw 'a' and 'b' then we'll end up with 2 errors: + // 1. incorrect join relation + // 2. incompatible types: int and string + // with SynExpr.ArbitraryAfterError we have only first one + let wrapInArbErrSequence l caption = + SynExpr.Sequential(DebugPointAtSequential.SuppressNeither, true, l, (arbExpr (caption, l.Range.EndRange)), l.Range) + + let mkOverallExprGivenVarSpaceExpr, varSpaceInner = + + let isNullableOp opId = + match ConvertValLogicalNameToDisplayNameCore opId with + | "?=" + | "=?" + | "?=?" -> true + | _ -> false + + match secondResultPatOpt, keySelectorsOpt with + // groupJoin + | Some secondResultPat, Some relExpr when customOperationIsLikeGroupJoin nm -> + let secondResultSimplePats, later3 = + SimplePatsOfPat cenv.synArgNameGenerator secondResultPat + + if Option.isSome later3 then + errorR (Error(FSComp.SR.tcJoinMustUseSimplePattern (nm.idText), secondResultPat.Range)) + + match relExpr with + | JoinRelation cenv env (keySelector1, keySelector2) -> + mkJoinExpr keySelector1 keySelector2 secondResultSimplePats, varSpaceWithGroupJoinVars + | BinOpExpr(opId, l, r) -> + if isNullableOp opId.idText then + // When we cannot resolve NullableOps, recommend the relevant namespace to be added + errorR ( + Error( + FSComp.SR.cannotResolveNullableOperators (ConvertValLogicalNameToDisplayNameCore opId.idText), + relExpr.Range + ) + ) + else + errorR (Error(FSComp.SR.tcInvalidRelationInJoin (nm.idText), relExpr.Range)) + + let l = wrapInArbErrSequence l "_keySelector1" + let r = wrapInArbErrSequence r "_keySelector2" + // this is not correct JoinRelation but it is still binary operation + // we've already reported error now we can use operands of binary operation as join components + mkJoinExpr l r secondResultSimplePats, varSpaceWithGroupJoinVars + | _ -> + errorR (Error(FSComp.SR.tcInvalidRelationInJoin (nm.idText), relExpr.Range)) + // since the shape of relExpr doesn't match our expectations (JoinRelation) + // then we assume that this is l.h.s. of the join relation + // so typechecker will treat relExpr as body of outerKeySelector lambda parameter in GroupJoin method + mkJoinExpr relExpr (arbExpr ("_keySelector2", relExpr.Range)) secondResultSimplePats, + varSpaceWithGroupJoinVars + + | None, Some relExpr when customOperationIsLikeJoin nm -> + match relExpr with + | JoinRelation cenv env (keySelector1, keySelector2) -> + mkJoinExpr keySelector1 keySelector2 secondSourceSimplePats, varSpaceWithSecondVars + | BinOpExpr(opId, l, r) -> + if isNullableOp opId.idText then + // When we cannot resolve NullableOps, recommend the relevant namespace to be added + errorR ( + Error( + FSComp.SR.cannotResolveNullableOperators (ConvertValLogicalNameToDisplayNameCore opId.idText), + relExpr.Range + ) + ) + else + errorR (Error(FSComp.SR.tcInvalidRelationInJoin (nm.idText), relExpr.Range)) + // this is not correct JoinRelation but it is still binary operation + // we've already reported error now we can use operands of binary operation as join components + let l = wrapInArbErrSequence l "_keySelector1" + let r = wrapInArbErrSequence r "_keySelector2" + mkJoinExpr l r secondSourceSimplePats, varSpaceWithGroupJoinVars + | _ -> + errorR (Error(FSComp.SR.tcInvalidRelationInJoin (nm.idText), relExpr.Range)) + // since the shape of relExpr doesn't match our expectations (JoinRelation) + // then we assume that this is l.h.s. of the join relation + // so typechecker will treat relExpr as body of outerKeySelector lambda parameter in Join method + mkJoinExpr relExpr (arbExpr ("_keySelector2", relExpr.Range)) secondSourceSimplePats, + varSpaceWithGroupJoinVars + + | None, None when customOperationIsLikeZip nm -> mkZipExpr, varSpaceWithSecondVars + + | _ -> + assert false + failwith "unreachable" + + // Case from C# spec: A query expression with a join clause with an into followed by something other than a select clause + // Case from C# spec: A query expression with a join clause without an into followed by something other than a select clause + let valsInner, _env = varSpaceInner.Force mOpCore + let varSpaceExpr = mkExprForVarSpace mOpCore valsInner + let varSpacePat = mkPatForVarSpace mOpCore valsInner + let joinExpr = mkOverallExprGivenVarSpaceExpr varSpaceExpr + + let consumingExpr = + SynExpr.ForEach( + DebugPointAtFor.No, + DebugPointAtInOrTo.No, + SeqExprOnly false, + false, + varSpacePat, + joinExpr, + innerComp, + mOpCore + ) + + Some(trans CompExprTranslationPass.Initial q varSpaceInner consumingExpr translatedCtxt) + + | SynExpr.ForEach(spFor, spIn, SeqExprOnly _seqExprOnly, isFromSource, pat, sourceExpr, innerComp, _mEntireForEach) -> + let sourceExpr = + match RewriteRangeExpr sourceExpr with + | Some e -> e + | None -> sourceExpr + + let wrappedSourceExpr = mkSourceExprConditional isFromSource sourceExpr + + let mFor = + match spFor with + | DebugPointAtFor.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.For) + | DebugPointAtFor.No -> pat.Range + + // For computation expressions, 'in' or 'to' is hit on each MoveNext. + // To support this a named debug point for the "in" keyword is available to inlined code. + match spIn with + | DebugPointAtInOrTo.Yes mIn -> + cenv.namedDebugPointsForInlinedCode[{ + Range = mFor + Name = "ForLoop.InOrToKeyword" + }] <- mIn + | _ -> () + + let mPat = pat.Range + + if + isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mFor ad "For" builderTy) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("For"), mFor)) - let mPat = pat.Range + // Add the variables to the query variable space, on demand + let varSpace = + addVarsToVarSpace varSpace (fun _mCustomOp env -> + use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mFor ad "For" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("For"), mFor)) + let _, _, vspecs, envinner, _ = + TcMatchPattern cenv (NewInferenceType g) env tpenv pat None - // Add the variables to the query variable space, on demand - let varSpace = - addVarsToVarSpace varSpace (fun _mCustomOp env -> - use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType g) env tpenv pat None - vspecs, envinner) + vspecs, envinner) - Some (trans CompExprTranslationPass.Initial q varSpace innerComp - (fun innerCompR -> + Some( + trans CompExprTranslationPass.Initial q varSpace innerComp (fun innerCompR -> - let forCall = - mkSynCall "For" mFor [wrappedSourceExpr; SynExpr.MatchLambda (false, mPat, [SynMatchClause(pat, None, innerCompR, mPat, DebugPointAtTarget.Yes, SynMatchClauseTrivia.Zero)], DebugPointAtBinding.NoneAtInvisible, mFor) ] + let forCall = + mkSynCall + "For" + mFor + [ + wrappedSourceExpr + SynExpr.MatchLambda( + false, + mPat, + [ + SynMatchClause(pat, None, innerCompR, mPat, DebugPointAtTarget.Yes, SynMatchClauseTrivia.Zero) + ], + DebugPointAtBinding.NoneAtInvisible, + mFor + ) + ] let forCall = match spFor with - | DebugPointAtFor.Yes _ -> - SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mFor, false, forCall) + | DebugPointAtFor.Yes _ -> SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mFor, false, forCall) | DebugPointAtFor.No -> forCall - translatedCtxt forCall)) - - | SynExpr.For (forDebugPoint=spFor; toDebugPoint=spTo; ident=id; identBody=start; direction=dir; toBody=finish; doBody=innerComp; range=m) -> - let mFor = match spFor with DebugPointAtFor.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.For) | _ -> m - - if isQuery then errorR(Error(FSComp.SR.tcNoIntegerForLoopInQuery(), mFor)) - - let reduced = elimFastIntegerForLoop (spFor, spTo, id, start, dir, finish, innerComp, m) - Some (trans CompExprTranslationPass.Initial q varSpace reduced translatedCtxt ) - - | SynExpr.While (spWhile, guardExpr, innerComp, _) -> - let mGuard = guardExpr.Range - let mWhile = match spWhile with DebugPointAtWhile.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.While) | _ -> mGuard - - if isQuery then error(Error(FSComp.SR.tcNoWhileInQuery(), mWhile)) + translatedCtxt forCall) + ) + + | SynExpr.For( + forDebugPoint = spFor + toDebugPoint = spTo + ident = id + identBody = start + direction = dir + toBody = finish + doBody = innerComp + range = m) -> + let mFor = + match spFor with + | DebugPointAtFor.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.For) + | _ -> m + + if isQuery then + errorR (Error(FSComp.SR.tcNoIntegerForLoopInQuery (), mFor)) + + let reduced = + elimFastIntegerForLoop (spFor, spTo, id, start, dir, finish, innerComp, m) + + Some(trans CompExprTranslationPass.Initial q varSpace reduced translatedCtxt) + + | SynExpr.While(spWhile, guardExpr, innerComp, _) -> + let mGuard = guardExpr.Range + + let mWhile = + match spWhile with + | DebugPointAtWhile.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.While) + | _ -> mGuard + + if isQuery then + error (Error(FSComp.SR.tcNoWhileInQuery (), mWhile)) + + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mWhile ad "While" builderTy + ) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("While"), mWhile)) + + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mWhile ad "Delay" builderTy + ) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("Delay"), mWhile)) + + // 'while' is hit just before each time the guard is called + let guardExpr = + match spWhile with + | DebugPointAtWhile.Yes _ -> SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mWhile, false, guardExpr) + | DebugPointAtWhile.No -> guardExpr + + Some( + trans CompExprTranslationPass.Initial q varSpace innerComp (fun holeFill -> + translatedCtxt ( + mkSynCall + "While" + mWhile + [ + mkSynDelay2 guardExpr + mkSynCall "Delay" mWhile [ mkSynDelay innerComp.Range holeFill ] + ] + )) + ) + + | SynExpr.WhileBang(spWhile, guardExpr, innerComp, mOrig) -> + let mGuard = guardExpr.Range + + let mWhile = + match spWhile with + | DebugPointAtWhile.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.While) + | _ -> mGuard + + let mGuard = mGuard.MakeSynthetic() + + // 'while!' is hit just before each time the guard is called + let guardExpr = + match spWhile with + | DebugPointAtWhile.Yes _ -> SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mWhile, false, guardExpr) + | DebugPointAtWhile.No -> guardExpr + + let rewrittenWhileExpr = + let idFirst = mkSynId mGuard (CompilerGeneratedName "first") + let patFirst = mkSynPatVar None idFirst + + let body = + let idCond = mkSynId mGuard (CompilerGeneratedName "cond") + let patCond = mkSynPatVar None idCond + + let condBinding = + mkSynBinding + (Xml.PreXmlDoc.Empty, patCond) + (None, + false, + true, + mGuard, + DebugPointAtBinding.NoneAtSticky, + None, + SynExpr.Ident idFirst, + mGuard, + [], + [], + None, + SynBindingTrivia.Zero) + + let setCondExpr = SynExpr.Set(SynExpr.Ident idCond, SynExpr.Ident idFirst, mGuard) + + let bindCondExpr = + SynExpr.LetOrUseBang( + DebugPointAtBinding.NoneAtSticky, + false, + true, + patFirst, + guardExpr, + [], + setCondExpr, + mGuard, + SynExprLetOrUseBangTrivia.Zero + ) + + let whileExpr = + SynExpr.While( + DebugPointAtWhile.No, + SynExpr.Ident idCond, + SynExpr.Sequential(DebugPointAtSequential.SuppressBoth, true, innerComp, bindCondExpr, mWhile), + mOrig + ) + + SynExpr.LetOrUse(false, false, [ condBinding ], whileExpr, mGuard, SynExprLetOrUseTrivia.Zero) + + SynExpr.LetOrUseBang( + DebugPointAtBinding.NoneAtSticky, + false, + true, + patFirst, + guardExpr, + [], + body, + mGuard, + SynExprLetOrUseBangTrivia.Zero + ) + + tryTrans CompExprTranslationPass.Initial q varSpace rewrittenWhileExpr translatedCtxt + + | SynExpr.TryFinally(innerComp, unwindExpr, _mTryToLast, spTry, spFinally, trivia) -> + + let mTry = + match spTry with + | DebugPointAtTry.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Try) + | _ -> trivia.TryKeyword + + let mFinally = + match spFinally with + | DebugPointAtFinally.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Finally) + | _ -> trivia.FinallyKeyword + + // Put down a debug point for the 'finally' + let unwindExpr2 = + match spFinally with + | DebugPointAtFinally.Yes _ -> SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mFinally, true, unwindExpr) + | DebugPointAtFinally.No -> unwindExpr + + if isQuery then + error (Error(FSComp.SR.tcNoTryFinallyInQuery (), mTry)) + + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mTry ad "TryFinally" builderTy + ) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("TryFinally"), mTry)) + + if + isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mTry ad "Delay" builderTy) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("Delay"), mTry)) + + let innerExpr = transNoQueryOps innerComp + + let innerExpr = + match spTry with + | DebugPointAtTry.Yes _ -> SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mTry, true, innerExpr) + | _ -> innerExpr + + Some( + translatedCtxt ( + mkSynCall + "TryFinally" + mTry + [ + mkSynCall "Delay" mTry [ mkSynDelay innerComp.Range innerExpr ] + mkSynDelay2 unwindExpr2 + ] + ) + ) + + | SynExpr.Paren(range = m) -> error (Error(FSComp.SR.tcConstructIsAmbiguousInComputationExpression (), m)) + + // In some cases the node produced by `mkSynCall "Zero" m []` may be discarded in the case + // of implicit yields - for example "list { 1; 2 }" when each expression checks as an implicit yield. + // If it is not discarded, the syntax node will later be checked and the existence/non-existence of the Zero method + // will be checked/reported appropriately (though the error message won't mention computation expressions + // like our other error messages for missing methods). + | SynExpr.ImplicitZero m -> + if + (not enableImplicitYield) + && isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Zero" builderTy) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("Zero"), m)) + + Some(translatedCtxt (mkSynCall "Zero" m [])) + + | OptionalSequential(JoinOrGroupJoinOrZipClause(_, _, _, _, _, mClause), _) when firstTry = CompExprTranslationPass.Initial -> + + // 'join' clauses preceded by 'let' and other constructs get processed by repackaging with a 'for' loop. + let patvs, _env = varSpace.Force comp.Range + let varSpaceExpr = mkExprForVarSpace mClause patvs + let varSpacePat = mkPatForVarSpace mClause patvs + + let dataCompPrior = + translatedCtxt (transNoQueryOps (SynExpr.YieldOrReturn((true, false), varSpaceExpr, mClause))) + + // Rebind using for ... + let rebind = + SynExpr.ForEach( + DebugPointAtFor.No, + DebugPointAtInOrTo.No, + SeqExprOnly false, + false, + varSpacePat, + dataCompPrior, + comp, + comp.Range + ) + + // Retry with the 'for' loop packaging. Set firstTry=false just in case 'join' processing fails + tryTrans CompExprTranslationPass.Subsequent q varSpace rebind id + + | OptionalSequential(CustomOperationClause(nm, _, opExpr, mClause, _), _) -> + + if q = CustomOperationsMode.Denied then + error (Error(FSComp.SR.tcCustomOperationMayNotBeUsedHere (), opExpr.Range)) + + let patvs, _env = varSpace.Force comp.Range + let varSpaceExpr = mkExprForVarSpace mClause patvs + + let dataCompPriorToOp = + let isYield = not (customOperationMaintainsVarSpaceUsingBind nm) + translatedCtxt (transNoQueryOps (SynExpr.YieldOrReturn((isYield, false), varSpaceExpr, mClause))) + + // Now run the consumeCustomOpClauses + Some(consumeCustomOpClauses q varSpace dataCompPriorToOp comp false mClause) + + | SynExpr.Sequential(sp, true, innerComp1, innerComp2, m) -> + + // Check for 'where x > y' and other mis-applications of infix operators. If detected, give a good error message, and just ignore innerComp1 + if isQuery && checkForBinaryApp innerComp1 then + Some(trans CompExprTranslationPass.Initial q varSpace innerComp2 translatedCtxt) - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mWhile ad "While" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("While"), mWhile)) - - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mWhile ad "Delay" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("Delay"), mWhile)) - - // 'while' is hit just before each time the guard is called - let guardExpr = - match spWhile with - | DebugPointAtWhile.Yes _ -> - SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mWhile, false, guardExpr) - | DebugPointAtWhile.No -> guardExpr - - Some(trans CompExprTranslationPass.Initial q varSpace innerComp (fun holeFill -> - translatedCtxt - (mkSynCall "While" mWhile - [ mkSynDelay2 guardExpr; - mkSynCall "Delay" mWhile [mkSynDelay innerComp.Range holeFill]])) ) - - | SynExpr.WhileBang (spWhile, guardExpr, innerComp, mOrig) -> - let mGuard = guardExpr.Range - let mWhile = match spWhile with DebugPointAtWhile.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.While) | _ -> mGuard - let mGuard = mGuard.MakeSynthetic() - - // 'while!' is hit just before each time the guard is called - let guardExpr = - match spWhile with - | DebugPointAtWhile.Yes _ -> - SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mWhile, false, guardExpr) - | DebugPointAtWhile.No -> guardExpr - - let rewrittenWhileExpr = - let idFirst = mkSynId mGuard (CompilerGeneratedName "first") - let patFirst = mkSynPatVar None idFirst - - let body = - let idCond = mkSynId mGuard (CompilerGeneratedName "cond") - let patCond = mkSynPatVar None idCond - let condBinding = mkSynBinding (Xml.PreXmlDoc.Empty, patCond) (None, false, true, mGuard, DebugPointAtBinding.NoneAtSticky, None, SynExpr.Ident idFirst, mGuard, [], [], None, SynBindingTrivia.Zero) - let setCondExpr = SynExpr.Set (SynExpr.Ident idCond, SynExpr.Ident idFirst, mGuard) - let bindCondExpr = SynExpr.LetOrUseBang (DebugPointAtBinding.NoneAtSticky, false, true, patFirst, guardExpr, [], setCondExpr, mGuard, SynExprLetOrUseBangTrivia.Zero) - - let whileExpr = SynExpr.While (DebugPointAtWhile.No, SynExpr.Ident idCond, SynExpr.Sequential (DebugPointAtSequential.SuppressBoth, true, innerComp, bindCondExpr, mWhile), mOrig) - SynExpr.LetOrUse (false, false, [ condBinding ], whileExpr, mGuard, SynExprLetOrUseTrivia.Zero) - - SynExpr.LetOrUseBang (DebugPointAtBinding.NoneAtSticky, false, true, patFirst, guardExpr, [], body, mGuard, SynExprLetOrUseBangTrivia.Zero) - - tryTrans CompExprTranslationPass.Initial q varSpace rewrittenWhileExpr translatedCtxt - - | SynExpr.TryFinally (innerComp, unwindExpr, _mTryToLast, spTry, spFinally, trivia) -> - - let mTry = match spTry with DebugPointAtTry.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Try) | _ -> trivia.TryKeyword - let mFinally = match spFinally with DebugPointAtFinally.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Finally) | _ -> trivia.FinallyKeyword - - // Put down a debug point for the 'finally' - let unwindExpr2 = - match spFinally with - | DebugPointAtFinally.Yes _ -> - SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mFinally, true, unwindExpr) - | DebugPointAtFinally.No -> unwindExpr - - if isQuery then error(Error(FSComp.SR.tcNoTryFinallyInQuery(), mTry)) - - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mTry ad "TryFinally" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("TryFinally"), mTry)) - - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mTry ad "Delay" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("Delay"), mTry)) - - let innerExpr = transNoQueryOps innerComp - - let innerExpr = - match spTry with - | DebugPointAtTry.Yes _ -> - SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mTry, true, innerExpr) - | _ -> innerExpr - - Some (translatedCtxt - (mkSynCall "TryFinally" mTry [ - mkSynCall "Delay" mTry [mkSynDelay innerComp.Range innerExpr] - mkSynDelay2 unwindExpr2])) - - | SynExpr.Paren (_, _, _, m) -> - error(Error(FSComp.SR.tcConstructIsAmbiguousInComputationExpression(), m)) - - // In some cases the node produced by `mkSynCall "Zero" m []` may be discarded in the case - // of implicit yields - for example "list { 1; 2 }" when each expression checks as an implicit yield. - // If it is not discarded, the syntax node will later be checked and the existence/non-existence of the Zero method - // will be checked/reported appropriately (though the error message won't mention computation expressions - // like our other error messages for missing methods). - | SynExpr.ImplicitZero m -> - if (not enableImplicitYield) && - isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Zero" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Zero"), m)) - Some (translatedCtxt (mkSynCall "Zero" m [])) - - | OptionalSequential (JoinOrGroupJoinOrZipClause (_, _, _, _, _, mClause), _) - when firstTry = CompExprTranslationPass.Initial -> - - // 'join' clauses preceded by 'let' and other constructs get processed by repackaging with a 'for' loop. - let patvs, _env = varSpace.Force comp.Range - let varSpaceExpr = mkExprForVarSpace mClause patvs - let varSpacePat = mkPatForVarSpace mClause patvs - - let dataCompPrior = - translatedCtxt (transNoQueryOps (SynExpr.YieldOrReturn ((true, false), varSpaceExpr, mClause))) - - // Rebind using for ... - let rebind = - SynExpr.ForEach (DebugPointAtFor.No, DebugPointAtInOrTo.No, SeqExprOnly false, false, varSpacePat, dataCompPrior, comp, comp.Range) - - // Retry with the 'for' loop packaging. Set firstTry=false just in case 'join' processing fails - tryTrans CompExprTranslationPass.Subsequent q varSpace rebind id + else + if isQuery && not (innerComp1.IsArbExprAndThusAlreadyReportedError) then + match innerComp1 with + | SynExpr.JoinIn _ -> () // an error will be reported later when we process innerComp1 as a sequential + | _ -> errorR (Error(FSComp.SR.tcUnrecognizedQueryOperator (), innerComp1.RangeOfFirstPortion)) + + match tryTrans CompExprTranslationPass.Initial CustomOperationsMode.Denied varSpace innerComp1 id with + | Some c -> + // "cexpr; cexpr" is treated as builder.Combine(cexpr1, cexpr1) + let m1 = rangeForCombine innerComp1 + + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo + ResultCollectionSettings.AtMostOneResult + cenv + env + m + ad + "Combine" + builderTy + ) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("Combine"), m)) + + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Delay" builderTy + ) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("Delay"), m)) + + let combineCall = + mkSynCall + "Combine" + m1 + [ + c + mkSynCall "Delay" m1 [ mkSynDelay innerComp2.Range (transNoQueryOps innerComp2) ] + ] + + Some(translatedCtxt combineCall) + + | None -> + // "do! expr; cexpr" is treated as { let! () = expr in cexpr } + match innerComp1 with + | SynExpr.DoBang(rhsExpr, m) -> + let sp = + match sp with + | DebugPointAtSequential.SuppressExpr -> DebugPointAtBinding.NoneAtDo + | DebugPointAtSequential.SuppressBoth -> DebugPointAtBinding.NoneAtDo + | DebugPointAtSequential.SuppressStmt -> DebugPointAtBinding.Yes m + | DebugPointAtSequential.SuppressNeither -> DebugPointAtBinding.Yes m + + Some( + trans + CompExprTranslationPass.Initial + q + varSpace + (SynExpr.LetOrUseBang( + sp, + false, + true, + SynPat.Const(SynConst.Unit, rhsExpr.Range), + rhsExpr, + [], + innerComp2, + m, + SynExprLetOrUseBangTrivia.Zero + )) + translatedCtxt + ) + + // "expr; cexpr" is treated as sequential execution + | _ -> + Some( + trans CompExprTranslationPass.Initial q varSpace innerComp2 (fun holeFill -> + let fillExpr = + if enableImplicitYield then + // When implicit yields are enabled, then if the 'innerComp1' checks as type + // 'unit' we interpret the expression as a sequential, and when it doesn't + // have type 'unit' we interpret it as a 'Yield + Combine'. + let combineExpr = + let m1 = rangeForCombine innerComp1 + let implicitYieldExpr = mkSynCall "Yield" comp.Range [ innerComp1 ] + + mkSynCall + "Combine" + m1 + [ + implicitYieldExpr + mkSynCall "Delay" m1 [ mkSynDelay holeFill.Range holeFill ] + ] + + SynExpr.SequentialOrImplicitYield(sp, innerComp1, holeFill, combineExpr, m) + else + SynExpr.Sequential(sp, true, innerComp1, holeFill, m) + + translatedCtxt fillExpr) + ) + + | SynExpr.IfThenElse(guardExpr, thenComp, elseCompOpt, spIfToThen, isRecovery, mIfToEndOfElseBranch, trivia) -> + match elseCompOpt with + | Some elseComp -> + if isQuery then + error (Error(FSComp.SR.tcIfThenElseMayNotBeUsedWithinQueries (), trivia.IfToThenRange)) + + Some( + translatedCtxt ( + SynExpr.IfThenElse( + guardExpr, + transNoQueryOps thenComp, + Some(transNoQueryOps elseComp), + spIfToThen, + isRecovery, + mIfToEndOfElseBranch, + trivia + ) + ) + ) + | None -> + let elseComp = + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo + ResultCollectionSettings.AtMostOneResult + cenv + env + trivia.IfToThenRange + ad + "Zero" + builderTy + ) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("Zero"), trivia.IfToThenRange)) + + mkSynCall "Zero" trivia.IfToThenRange [] + + Some( + trans CompExprTranslationPass.Initial q varSpace thenComp (fun holeFill -> + translatedCtxt ( + SynExpr.IfThenElse( + guardExpr, + holeFill, + Some elseComp, + spIfToThen, + isRecovery, + mIfToEndOfElseBranch, + trivia + ) + )) + ) + + // 'let binds in expr' + | SynExpr.LetOrUse(isRec, false, binds, innerComp, m, trivia) -> + + // For 'query' check immediately + if isQuery then + match (List.map (BindingNormalization.NormalizeBinding ValOrMemberBinding cenv env) binds) with + | [ NormalizedBinding(_, SynBindingKind.Normal, false, false, _, _, _, _, _, _, _, _) ] when not isRec -> () + | normalizedBindings -> + let failAt m = + error (Error(FSComp.SR.tcNonSimpleLetBindingInQuery (), m)) + + match normalizedBindings with + | NormalizedBinding(mBinding = mBinding) :: _ -> failAt mBinding + | _ -> failAt m - | OptionalSequential (CustomOperationClause (nm, _, opExpr, mClause, _), _) -> + // Add the variables to the query variable space, on demand + let varSpace = + addVarsToVarSpace varSpace (fun mQueryOp env -> + // Normalize the bindings before detecting the bound variables + match (List.map (BindingNormalization.NormalizeBinding ValOrMemberBinding cenv env) binds) with + | [ NormalizedBinding(kind = SynBindingKind.Normal; mustInline = false; isMutable = false; pat = pat) ] -> + // successful case + use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - if q = CustomOperationsMode.Denied then error(Error(FSComp.SR.tcCustomOperationMayNotBeUsedHere(), opExpr.Range)) + let _, _, vspecs, envinner, _ = + TcMatchPattern cenv (NewInferenceType g) env tpenv pat None + + vspecs, envinner + | _ -> + // error case + error (Error(FSComp.SR.tcCustomOperationMayNotBeUsedInConjunctionWithNonSimpleLetBindings (), mQueryOp))) + + Some( + trans CompExprTranslationPass.Initial q varSpace innerComp (fun holeFill -> + translatedCtxt (SynExpr.LetOrUse(isRec, false, binds, holeFill, m, trivia))) + ) + + // 'use x = expr in expr' + | SynExpr.LetOrUse( + isUse = true + bindings = [ SynBinding(kind = SynBindingKind.Normal; headPat = pat; expr = rhsExpr; debugPoint = spBind) ] + body = innerComp) -> + let mBind = + match spBind with + | DebugPointAtBinding.Yes m -> m + | _ -> rhsExpr.Range + + if isQuery then + error (Error(FSComp.SR.tcUseMayNotBeUsedInQueries (), mBind)) + + let innerCompRange = innerComp.Range + + let consumeExpr = + SynExpr.MatchLambda( + false, + innerCompRange, + [ + SynMatchClause( + pat, + None, + transNoQueryOps innerComp, + innerCompRange, + DebugPointAtTarget.Yes, + SynMatchClauseTrivia.Zero + ) + ], + DebugPointAtBinding.NoneAtInvisible, + innerCompRange + ) + + if + isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBind ad "Using" builderTy) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("Using"), mBind)) + + Some( + translatedCtxt (mkSynCall "Using" mBind [ rhsExpr; consumeExpr ]) + |> addBindDebugPoint spBind + ) + + // 'let! pat = expr in expr' + // --> build.Bind(e1, (fun _argN -> match _argN with pat -> expr)) + // or + // --> build.BindReturn(e1, (fun _argN -> match _argN with pat -> expr-without-return)) + | SynExpr.LetOrUseBang( + bindDebugPoint = spBind + isUse = false + isFromSource = isFromSource + pat = pat + rhs = rhsExpr + andBangs = [] + body = innerComp) -> + + let mBind = + match spBind with + | DebugPointAtBinding.Yes m -> m + | _ -> rhsExpr.Range + + if isQuery then + error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), mBind)) - let patvs, _env = varSpace.Force comp.Range - let varSpaceExpr = mkExprForVarSpace mClause patvs - - let dataCompPriorToOp = - let isYield = not (customOperationMaintainsVarSpaceUsingBind nm) - translatedCtxt (transNoQueryOps (SynExpr.YieldOrReturn ((isYield, false), varSpaceExpr, mClause))) - - // Now run the consumeCustomOpClauses - Some (consumeCustomOpClauses q varSpace dataCompPriorToOp comp false mClause) - - | SynExpr.Sequential (sp, true, innerComp1, innerComp2, m) -> - - // Check for 'where x > y' and other mis-applications of infix operators. If detected, give a good error message, and just ignore innerComp1 - if isQuery && checkForBinaryApp innerComp1 then - Some (trans CompExprTranslationPass.Initial q varSpace innerComp2 translatedCtxt) - - else - - if isQuery && not(innerComp1.IsArbExprAndThusAlreadyReportedError) then - match innerComp1 with - | SynExpr.JoinIn _ -> () // an error will be reported later when we process innerComp1 as a sequential - | _ -> errorR(Error(FSComp.SR.tcUnrecognizedQueryOperator(), innerComp1.RangeOfFirstPortion)) - - match tryTrans CompExprTranslationPass.Initial CustomOperationsMode.Denied varSpace innerComp1 id with - | Some c -> - // "cexpr; cexpr" is treated as builder.Combine(cexpr1, cexpr1) - let m1 = rangeForCombine innerComp1 - - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Combine" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("Combine"), m)) - - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Delay" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("Delay"), m)) - - let combineCall = mkSynCall "Combine" m1 [c; mkSynCall "Delay" m1 [mkSynDelay innerComp2.Range (transNoQueryOps innerComp2)]] - - Some (translatedCtxt combineCall) - - | None -> - // "do! expr; cexpr" is treated as { let! () = expr in cexpr } - match innerComp1 with - | SynExpr.DoBang (rhsExpr, m) -> - let sp = - match sp with - | DebugPointAtSequential.SuppressExpr -> DebugPointAtBinding.NoneAtDo - | DebugPointAtSequential.SuppressBoth -> DebugPointAtBinding.NoneAtDo - | DebugPointAtSequential.SuppressStmt -> DebugPointAtBinding.Yes m - | DebugPointAtSequential.SuppressNeither -> DebugPointAtBinding.Yes m - Some(trans CompExprTranslationPass.Initial q varSpace (SynExpr.LetOrUseBang (sp, false, true, SynPat.Const(SynConst.Unit, rhsExpr.Range), rhsExpr, [], innerComp2, m, SynExprLetOrUseBangTrivia.Zero)) translatedCtxt) - - // "expr; cexpr" is treated as sequential execution - | _ -> - Some (trans CompExprTranslationPass.Initial q varSpace innerComp2 (fun holeFill -> - let fillExpr = - if enableImplicitYield then - // When implicit yields are enabled, then if the 'innerComp1' checks as type - // 'unit' we interpret the expression as a sequential, and when it doesn't - // have type 'unit' we interpret it as a 'Yield + Combine'. - let combineExpr = - let m1 = rangeForCombine innerComp1 - let implicitYieldExpr = mkSynCall "Yield" comp.Range [innerComp1] - mkSynCall "Combine" m1 [implicitYieldExpr; mkSynCall "Delay" m1 [mkSynDelay holeFill.Range holeFill]] - SynExpr.SequentialOrImplicitYield(sp, innerComp1, holeFill, combineExpr, m) - else - SynExpr.Sequential(sp, true, innerComp1, holeFill, m) - translatedCtxt fillExpr)) - - | SynExpr.IfThenElse (guardExpr, thenComp, elseCompOpt, spIfToThen, isRecovery, mIfToEndOfElseBranch, trivia) -> - match elseCompOpt with - | Some elseComp -> - if isQuery then error(Error(FSComp.SR.tcIfThenElseMayNotBeUsedWithinQueries(), trivia.IfToThenRange)) - Some (translatedCtxt (SynExpr.IfThenElse (guardExpr, transNoQueryOps thenComp, Some(transNoQueryOps elseComp), spIfToThen, isRecovery, mIfToEndOfElseBranch, trivia))) - | None -> - let elseComp = - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env trivia.IfToThenRange ad "Zero" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("Zero"), trivia.IfToThenRange)) - mkSynCall "Zero" trivia.IfToThenRange [] - Some (trans CompExprTranslationPass.Initial q varSpace thenComp (fun holeFill -> translatedCtxt (SynExpr.IfThenElse (guardExpr, holeFill, Some elseComp, spIfToThen, isRecovery, mIfToEndOfElseBranch, trivia)))) - - // 'let binds in expr' - | SynExpr.LetOrUse (isRec, false, binds, innerComp, m, trivia) -> - - // For 'query' check immediately - if isQuery then - match (List.map (BindingNormalization.NormalizeBinding ValOrMemberBinding cenv env) binds) with - | [NormalizedBinding(_, SynBindingKind.Normal, false, false, _, _, _, _, _, _, _, _)] when not isRec -> - () - | normalizedBindings -> - let failAt m = error(Error(FSComp.SR.tcNonSimpleLetBindingInQuery(), m)) - match normalizedBindings with - | NormalizedBinding(_, _, _, _, _, _, _, _, _, _, mBinding, _) :: _ -> failAt mBinding - | _ -> failAt m - - // Add the variables to the query variable space, on demand - let varSpace = - addVarsToVarSpace varSpace (fun mQueryOp env -> - // Normalize the bindings before detecting the bound variables - match (List.map (BindingNormalization.NormalizeBinding ValOrMemberBinding cenv env) binds) with - | [NormalizedBinding(_vis, SynBindingKind.Normal, false, false, _, _, _, _, pat, _, _, _)] -> - // successful case + // Add the variables to the query variable space, on demand + let varSpace = + addVarsToVarSpace varSpace (fun _mCustomOp env -> use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType g) env tpenv pat None - vspecs, envinner - | _ -> - // error case - error(Error(FSComp.SR.tcCustomOperationMayNotBeUsedInConjunctionWithNonSimpleLetBindings(), mQueryOp))) - Some (trans CompExprTranslationPass.Initial q varSpace innerComp (fun holeFill -> translatedCtxt (SynExpr.LetOrUse (isRec, false, binds, holeFill, m, trivia)))) + let _, _, vspecs, envinner, _ = + TcMatchPattern cenv (NewInferenceType g) env tpenv pat None - // 'use x = expr in expr' - | SynExpr.LetOrUse (isUse=true; bindings=[SynBinding (kind=SynBindingKind.Normal; headPat=pat; expr=rhsExpr; debugPoint=spBind)]; body=innerComp) -> - let mBind = match spBind with DebugPointAtBinding.Yes m -> m | _ -> rhsExpr.Range - if isQuery then error(Error(FSComp.SR.tcUseMayNotBeUsedInQueries(), mBind)) - let innerCompRange = innerComp.Range - let consumeExpr = SynExpr.MatchLambda(false, innerCompRange, [SynMatchClause(pat, None, transNoQueryOps innerComp, innerCompRange, DebugPointAtTarget.Yes, SynMatchClauseTrivia.Zero)], DebugPointAtBinding.NoneAtInvisible, innerCompRange) - - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBind ad "Using" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("Using"), mBind)) - - Some (translatedCtxt (mkSynCall "Using" mBind [rhsExpr; consumeExpr ]) |> addBindDebugPoint spBind) - - // 'let! pat = expr in expr' - // --> build.Bind(e1, (fun _argN -> match _argN with pat -> expr)) - // or - // --> build.BindReturn(e1, (fun _argN -> match _argN with pat -> expr-without-return)) - | SynExpr.LetOrUseBang (bindDebugPoint=spBind; isUse=false; isFromSource=isFromSource; pat=pat; rhs=rhsExpr; andBangs=[]; body=innerComp) -> - - let mBind = match spBind with DebugPointAtBinding.Yes m -> m | _ -> rhsExpr.Range - if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), mBind)) - - // Add the variables to the query variable space, on demand - let varSpace = - addVarsToVarSpace varSpace (fun _mCustomOp env -> - use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType g) env tpenv pat None vspecs, envinner) - let rhsExpr = mkSourceExprConditional isFromSource rhsExpr - Some (transBind q varSpace mBind (addBindDebugPoint spBind) "Bind" [rhsExpr] pat innerComp translatedCtxt) - - // 'use! pat = e1 in e2' --> build.Bind(e1, (function _argN -> match _argN with pat -> build.Using(x, (fun _argN -> match _argN with pat -> e2)))) - | SynExpr.LetOrUseBang (bindDebugPoint=spBind; isUse=true; isFromSource=isFromSource; pat=SynPat.Named (ident=SynIdent(id,_); isThisVal=false) as pat; rhs=rhsExpr; andBangs=[]; body=innerComp) - | SynExpr.LetOrUseBang (bindDebugPoint=spBind; isUse=true; isFromSource=isFromSource; pat=SynPat.LongIdent (longDotId=SynLongIdent([id], _, _)) as pat; rhs=rhsExpr; andBangs=[]; body=innerComp) -> - - let mBind = match spBind with DebugPointAtBinding.Yes m -> m | _ -> rhsExpr.Range - if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), mBind)) - - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBind ad "Using" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("Using"), mBind)) - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBind ad "Bind" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("Bind"), mBind)) - - let bindExpr = - let consumeExpr = SynExpr.MatchLambda(false, mBind, [SynMatchClause(pat, None, transNoQueryOps innerComp, innerComp.Range, DebugPointAtTarget.Yes, SynMatchClauseTrivia.Zero)], DebugPointAtBinding.NoneAtInvisible, mBind) - let consumeExpr = mkSynCall "Using" mBind [SynExpr.Ident id; consumeExpr ] - let consumeExpr = SynExpr.MatchLambda(false, mBind, [SynMatchClause(pat, None, consumeExpr, id.idRange, DebugPointAtTarget.No, SynMatchClauseTrivia.Zero)], DebugPointAtBinding.NoneAtInvisible, mBind) let rhsExpr = mkSourceExprConditional isFromSource rhsExpr - mkSynCall "Bind" mBind [rhsExpr; consumeExpr] - |> addBindDebugPoint spBind + Some(transBind q varSpace mBind (addBindDebugPoint spBind) "Bind" [ rhsExpr ] pat innerComp translatedCtxt) + + // 'use! pat = e1 in e2' --> build.Bind(e1, (function _argN -> match _argN with pat -> build.Using(x, (fun _argN -> match _argN with pat -> e2)))) + | SynExpr.LetOrUseBang( + bindDebugPoint = spBind + isUse = true + isFromSource = isFromSource + pat = SynPat.Named(ident = SynIdent(id, _); isThisVal = false) as pat + rhs = rhsExpr + andBangs = [] + body = innerComp) + | SynExpr.LetOrUseBang( + bindDebugPoint = spBind + isUse = true + isFromSource = isFromSource + pat = SynPat.LongIdent(longDotId = SynLongIdent(id = [ id ])) as pat + rhs = rhsExpr + andBangs = [] + body = innerComp) -> + + let mBind = + match spBind with + | DebugPointAtBinding.Yes m -> m + | _ -> rhsExpr.Range + + if isQuery then + error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), mBind)) + + if + isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBind ad "Using" builderTy) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("Using"), mBind)) + + if + isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBind ad "Bind" builderTy) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("Bind"), mBind)) + + let bindExpr = + let consumeExpr = + SynExpr.MatchLambda( + false, + mBind, + [ + SynMatchClause( + pat, + None, + transNoQueryOps innerComp, + innerComp.Range, + DebugPointAtTarget.Yes, + SynMatchClauseTrivia.Zero + ) + ], + DebugPointAtBinding.NoneAtInvisible, + mBind + ) + + let consumeExpr = mkSynCall "Using" mBind [ SynExpr.Ident id; consumeExpr ] + + let consumeExpr = + SynExpr.MatchLambda( + false, + mBind, + [ + SynMatchClause(pat, None, consumeExpr, id.idRange, DebugPointAtTarget.No, SynMatchClauseTrivia.Zero) + ], + DebugPointAtBinding.NoneAtInvisible, + mBind + ) + + let rhsExpr = mkSourceExprConditional isFromSource rhsExpr + mkSynCall "Bind" mBind [ rhsExpr; consumeExpr ] |> addBindDebugPoint spBind + + Some(translatedCtxt bindExpr) + + // 'use! pat = e1 ... in e2' where 'pat' is not a simple name --> error + | SynExpr.LetOrUseBang(isUse = true; pat = pat; andBangs = andBangs) -> + if isNil andBangs then + error (Error(FSComp.SR.tcInvalidUseBangBinding (), pat.Range)) + else + error (Error(FSComp.SR.tcInvalidUseBangBindingNoAndBangs (), comp.Range)) + + // 'let! pat1 = expr1 and! pat2 = expr2 in ...' --> + // build.BindN(expr1, expr2, ...) + // or + // build.BindNReturn(expr1, expr2, ...) + // or + // build.Bind(build.MergeSources(expr1, expr2), ...) + | SynExpr.LetOrUseBang( + bindDebugPoint = spBind + isUse = false + isFromSource = isFromSource + pat = letPat + rhs = letRhsExpr + andBangs = andBangBindings + body = innerComp + range = letBindRange) -> + if not (cenv.g.langVersion.SupportsFeature LanguageFeature.AndBang) then + error (Error(FSComp.SR.tcAndBangNotSupported (), comp.Range)) + + if isQuery then + error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), letBindRange)) + + let mBind = + match spBind with + | DebugPointAtBinding.Yes m -> m + | _ -> letRhsExpr.Range + + let sources = + (letRhsExpr + :: [ for SynExprAndBang(body = andExpr) in andBangBindings -> andExpr ]) + |> List.map (mkSourceExprConditional isFromSource) + + let pats = + letPat :: [ for SynExprAndBang(pat = andPat) in andBangBindings -> andPat ] + + let sourcesRange = sources |> List.map (fun e -> e.Range) |> List.reduce unionRanges + + let numSources = sources.Length + let bindReturnNName = "Bind" + string numSources + "Return" + let bindNName = "Bind" + string numSources + + // Check if this is a Bind2Return etc. + let hasBindReturnN = + not ( + isNil ( + TryFindIntrinsicOrExtensionMethInfo + ResultCollectionSettings.AtMostOneResult + cenv + env + mBind + ad + bindReturnNName + builderTy + ) + ) + + if hasBindReturnN && Option.isSome (convertSimpleReturnToExpr varSpace innerComp) then + let consumePat = SynPat.Tuple(false, pats, [], letPat.Range) - Some(translatedCtxt bindExpr) + // Add the variables to the query variable space, on demand + let varSpace = + addVarsToVarSpace varSpace (fun _mCustomOp env -> + use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - // 'use! pat = e1 ... in e2' where 'pat' is not a simple name --> error - | SynExpr.LetOrUseBang (isUse=true; pat=pat; andBangs=andBangs) -> - if isNil andBangs then - error(Error(FSComp.SR.tcInvalidUseBangBinding(), pat.Range)) - else - error(Error(FSComp.SR.tcInvalidUseBangBindingNoAndBangs(), comp.Range)) - - // 'let! pat1 = expr1 and! pat2 = expr2 in ...' --> - // build.BindN(expr1, expr2, ...) - // or - // build.BindNReturn(expr1, expr2, ...) - // or - // build.Bind(build.MergeSources(expr1, expr2), ...) - | SynExpr.LetOrUseBang(bindDebugPoint=spBind; isUse=false; isFromSource=isFromSource; pat=letPat; rhs=letRhsExpr; andBangs=andBangBindings; body=innerComp; range=letBindRange) -> - if not (cenv.g.langVersion.SupportsFeature LanguageFeature.AndBang) then - error(Error(FSComp.SR.tcAndBangNotSupported(), comp.Range)) - - if isQuery then - error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), letBindRange)) - - let mBind = match spBind with DebugPointAtBinding.Yes m -> m | _ -> letRhsExpr.Range - let sources = (letRhsExpr :: [for SynExprAndBang(body=andExpr) in andBangBindings -> andExpr ]) |> List.map (mkSourceExprConditional isFromSource) - let pats = letPat :: [for SynExprAndBang(pat = andPat) in andBangBindings -> andPat ] - let sourcesRange = sources |> List.map (fun e -> e.Range) |> List.reduce unionRanges - - let numSources = sources.Length - let bindReturnNName = "Bind"+string numSources+"Return" - let bindNName = "Bind"+string numSources - - // Check if this is a Bind2Return etc. - let hasBindReturnN = not (isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBind ad bindReturnNName builderTy)) - if hasBindReturnN && Option.isSome (convertSimpleReturnToExpr varSpace innerComp) then - let consumePat = SynPat.Tuple(false, pats, [], letPat.Range) + let _, _, vspecs, envinner, _ = + TcMatchPattern cenv (NewInferenceType g) env tpenv consumePat None - // Add the variables to the query variable space, on demand - let varSpace = - addVarsToVarSpace varSpace (fun _mCustomOp env -> - use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType g) env tpenv consumePat None vspecs, envinner) - Some (transBind q varSpace mBind (addBindDebugPoint spBind) bindNName sources consumePat innerComp translatedCtxt) - - else + Some(transBind q varSpace mBind (addBindDebugPoint spBind) bindNName sources consumePat innerComp translatedCtxt) - // Check if this is a Bind2 etc. - let hasBindN = not (isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBind ad bindNName builderTy)) - if hasBindN then - let consumePat = SynPat.Tuple(false, pats, [], letPat.Range) + else - // Add the variables to the query variable space, on demand - let varSpace = - addVarsToVarSpace varSpace (fun _mCustomOp env -> + // Check if this is a Bind2 etc. + let hasBindN = + not ( + isNil ( + TryFindIntrinsicOrExtensionMethInfo + ResultCollectionSettings.AtMostOneResult + cenv + env + mBind + ad + bindNName + builderTy + ) + ) + + if hasBindN then + let consumePat = SynPat.Tuple(false, pats, [], letPat.Range) + + // Add the variables to the query variable space, on demand + let varSpace = + addVarsToVarSpace varSpace (fun _mCustomOp env -> use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType g) env tpenv consumePat None - vspecs, envinner) - Some (transBind q varSpace mBind (addBindDebugPoint spBind) bindNName sources consumePat innerComp translatedCtxt) - else + let _, _, vspecs, envinner, _ = + TcMatchPattern cenv (NewInferenceType g) env tpenv consumePat None - // Look for the maximum supported MergeSources, MergeSources3, ... - let mkMergeSourcesName n = if n = 2 then "MergeSources" else "MergeSources"+(string n) + vspecs, envinner) - let maxMergeSources = - let rec loop (n: int) = - let mergeSourcesName = mkMergeSourcesName n - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBind ad mergeSourcesName builderTy) then - (n-1) - else - loop (n+1) - loop 2 + Some(transBind q varSpace mBind (addBindDebugPoint spBind) bindNName sources consumePat innerComp translatedCtxt) + else - if maxMergeSources = 1 then error(Error(FSComp.SR.tcRequireMergeSourcesOrBindN(bindNName), mBind)) + // Look for the maximum supported MergeSources, MergeSources3, ... + let mkMergeSourcesName n = + if n = 2 then + "MergeSources" + else + "MergeSources" + (string n) + + let maxMergeSources = + let rec loop (n: int) = + let mergeSourcesName = mkMergeSourcesName n + + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo + ResultCollectionSettings.AtMostOneResult + cenv + env + mBind + ad + mergeSourcesName + builderTy + ) + then + (n - 1) + else + loop (n + 1) + + loop 2 + + if maxMergeSources = 1 then + error (Error(FSComp.SR.tcRequireMergeSourcesOrBindN (bindNName), mBind)) + + let rec mergeSources (sourcesAndPats: (SynExpr * SynPat) list) = + let numSourcesAndPats = sourcesAndPats.Length + assert (numSourcesAndPats <> 0) + + if numSourcesAndPats = 1 then + sourcesAndPats[0] + + elif numSourcesAndPats <= maxMergeSources then + + // Call MergeSources2(e1, e2), MergeSources3(e1, e2, e3) etc + let mergeSourcesName = mkMergeSourcesName numSourcesAndPats + + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo + ResultCollectionSettings.AtMostOneResult + cenv + env + mBind + ad + mergeSourcesName + builderTy + ) + then + error (Error(FSComp.SR.tcRequireMergeSourcesOrBindN (bindNName), mBind)) + + let source = mkSynCall mergeSourcesName sourcesRange (List.map fst sourcesAndPats) + let pat = SynPat.Tuple(false, List.map snd sourcesAndPats, [], letPat.Range) + source, pat - let rec mergeSources (sourcesAndPats: (SynExpr * SynPat) list) = - let numSourcesAndPats = sourcesAndPats.Length - assert (numSourcesAndPats <> 0) - if numSourcesAndPats = 1 then - sourcesAndPats[0] + else - elif numSourcesAndPats <= maxMergeSources then + // Call MergeSourcesMax(e1, e2, e3, e4, (...)) + let nowSourcesAndPats, laterSourcesAndPats = + List.splitAt (maxMergeSources - 1) sourcesAndPats - // Call MergeSources2(e1, e2), MergeSources3(e1, e2, e3) etc - let mergeSourcesName = mkMergeSourcesName numSourcesAndPats + let mergeSourcesName = mkMergeSourcesName maxMergeSources - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBind ad mergeSourcesName builderTy) then - error(Error(FSComp.SR.tcRequireMergeSourcesOrBindN(bindNName), mBind)) + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo + ResultCollectionSettings.AtMostOneResult + cenv + env + mBind + ad + mergeSourcesName + builderTy + ) + then + error (Error(FSComp.SR.tcRequireMergeSourcesOrBindN (bindNName), mBind)) - let source = mkSynCall mergeSourcesName sourcesRange (List.map fst sourcesAndPats) - let pat = SynPat.Tuple(false, List.map snd sourcesAndPats, [], letPat.Range) - source, pat + let laterSource, laterPat = mergeSources laterSourcesAndPats - else + let source = + mkSynCall mergeSourcesName sourcesRange (List.map fst nowSourcesAndPats @ [ laterSource ]) - // Call MergeSourcesMax(e1, e2, e3, e4, (...)) - let nowSourcesAndPats, laterSourcesAndPats = List.splitAt (maxMergeSources - 1) sourcesAndPats - let mergeSourcesName = mkMergeSourcesName maxMergeSources + let pat = + SynPat.Tuple(false, List.map snd nowSourcesAndPats @ [ laterPat ], [], letPat.Range) - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBind ad mergeSourcesName builderTy) then - error(Error(FSComp.SR.tcRequireMergeSourcesOrBindN(bindNName), mBind)) + source, pat - let laterSource, laterPat = mergeSources laterSourcesAndPats - let source = mkSynCall mergeSourcesName sourcesRange (List.map fst nowSourcesAndPats @ [laterSource]) - let pat = SynPat.Tuple(false, List.map snd nowSourcesAndPats @ [laterPat], [], letPat.Range) - source, pat + let mergedSources, consumePat = mergeSources (List.zip sources pats) - let mergedSources, consumePat = mergeSources (List.zip sources pats) - - // Add the variables to the query variable space, on demand - let varSpace = - addVarsToVarSpace varSpace (fun _mCustomOp env -> + // Add the variables to the query variable space, on demand + let varSpace = + addVarsToVarSpace varSpace (fun _mCustomOp env -> use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink - let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType g) env tpenv consumePat None - vspecs, envinner) - - // Build the 'Bind' call - Some (transBind q varSpace mBind (addBindDebugPoint spBind) "Bind" [mergedSources] consumePat innerComp translatedCtxt) - - | SynExpr.Match (spMatch, expr, clauses, m, trivia) -> - if isQuery then error(Error(FSComp.SR.tcMatchMayNotBeUsedWithQuery(), trivia.MatchKeyword)) - let clauses = clauses |> List.map (fun (SynMatchClause(pat, cond, innerComp, patm, sp, trivia)) -> SynMatchClause(pat, cond, transNoQueryOps innerComp, patm, sp, trivia)) - Some(translatedCtxt (SynExpr.Match (spMatch, expr, clauses, m, trivia))) - - // 'match! expr with pats ...' --> build.Bind(e1, (function pats ...)) - // FUTURE: consider allowing translation to BindReturn - | SynExpr.MatchBang (spMatch, expr, clauses, _m, trivia) -> - let inputExpr = mkSourceExpr expr - if isQuery then error(Error(FSComp.SR.tcMatchMayNotBeUsedWithQuery(), trivia.MatchBangKeyword)) - - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env trivia.MatchBangKeyword ad "Bind" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("Bind"), trivia.MatchBangKeyword)) - let clauses = clauses |> List.map (fun (SynMatchClause(pat, cond, innerComp, patm, sp, trivia)) -> SynMatchClause(pat, cond, transNoQueryOps innerComp, patm, sp, trivia)) - let consumeExpr = SynExpr.MatchLambda (false, trivia.MatchBangKeyword, clauses, DebugPointAtBinding.NoneAtInvisible, trivia.MatchBangKeyword) + let _, _, vspecs, envinner, _ = + TcMatchPattern cenv (NewInferenceType g) env tpenv consumePat None - let callExpr = - mkSynCall "Bind" trivia.MatchBangKeyword [inputExpr; consumeExpr] - |> addBindDebugPoint spMatch - - Some(translatedCtxt callExpr) + vspecs, envinner) - | SynExpr.TryWith (innerComp, clauses, mTryToLast, spTry, spWith, trivia) -> - let mTry = match spTry with DebugPointAtTry.Yes _ -> trivia.TryKeyword.NoteSourceConstruct(NotedSourceConstruct.Try) | _ -> trivia.TryKeyword - let spWith2 = match spWith with DebugPointAtWith.Yes _ -> DebugPointAtBinding.Yes trivia.WithKeyword | _ -> DebugPointAtBinding.NoneAtInvisible - - if isQuery then error(Error(FSComp.SR.tcTryWithMayNotBeUsedInQueries(), mTry)) + // Build the 'Bind' call + Some( + transBind + q + varSpace + mBind + (addBindDebugPoint spBind) + "Bind" + [ mergedSources ] + consumePat + innerComp + translatedCtxt + ) + + | SynExpr.Match(spMatch, expr, clauses, m, trivia) -> + if isQuery then + error (Error(FSComp.SR.tcMatchMayNotBeUsedWithQuery (), trivia.MatchKeyword)) + + let clauses = + clauses + |> List.map (fun (SynMatchClause(pat, cond, innerComp, patm, sp, trivia)) -> + SynMatchClause(pat, cond, transNoQueryOps innerComp, patm, sp, trivia)) + + Some(translatedCtxt (SynExpr.Match(spMatch, expr, clauses, m, trivia))) + + // 'match! expr with pats ...' --> build.Bind(e1, (function pats ...)) + // FUTURE: consider allowing translation to BindReturn + | SynExpr.MatchBang(spMatch, expr, clauses, _m, trivia) -> + let inputExpr = mkSourceExpr expr + + if isQuery then + error (Error(FSComp.SR.tcMatchMayNotBeUsedWithQuery (), trivia.MatchBangKeyword)) + + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo + ResultCollectionSettings.AtMostOneResult + cenv + env + trivia.MatchBangKeyword + ad + "Bind" + builderTy + ) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("Bind"), trivia.MatchBangKeyword)) + + let clauses = + clauses + |> List.map (fun (SynMatchClause(pat, cond, innerComp, patm, sp, trivia)) -> + SynMatchClause(pat, cond, transNoQueryOps innerComp, patm, sp, trivia)) + + let consumeExpr = + SynExpr.MatchLambda( + false, + trivia.MatchBangKeyword, + clauses, + DebugPointAtBinding.NoneAtInvisible, + trivia.MatchBangKeyword + ) + + let callExpr = + mkSynCall "Bind" trivia.MatchBangKeyword [ inputExpr; consumeExpr ] + |> addBindDebugPoint spMatch + + Some(translatedCtxt callExpr) + + | SynExpr.TryWith(innerComp, clauses, mTryToLast, spTry, spWith, trivia) -> + let mTry = + match spTry with + | DebugPointAtTry.Yes _ -> trivia.TryKeyword.NoteSourceConstruct(NotedSourceConstruct.Try) + | _ -> trivia.TryKeyword + + let spWith2 = + match spWith with + | DebugPointAtWith.Yes _ -> DebugPointAtBinding.Yes trivia.WithKeyword + | _ -> DebugPointAtBinding.NoneAtInvisible + + if isQuery then + error (Error(FSComp.SR.tcTryWithMayNotBeUsedInQueries (), mTry)) + + let clauses = + clauses + |> List.map (fun (SynMatchClause(pat, cond, clauseComp, patm, sp, trivia)) -> + SynMatchClause(pat, cond, transNoQueryOps clauseComp, patm, sp, trivia)) + + let consumeExpr = + SynExpr.MatchLambda(true, mTryToLast, clauses, spWith2, mTryToLast) + + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mTry ad "TryWith" builderTy + ) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("TryWith"), mTry)) + + if + isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mTry ad "Delay" builderTy) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("Delay"), mTry)) + + let innerExpr = transNoQueryOps innerComp + + let innerExpr = + match spTry with + | DebugPointAtTry.Yes _ -> SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mTry, true, innerExpr) + | _ -> innerExpr + + let callExpr = + mkSynCall "TryWith" mTry [ mkSynCall "Delay" mTry [ mkSynDelay2 innerExpr ]; consumeExpr ] + + Some(translatedCtxt callExpr) + + | SynExpr.YieldOrReturnFrom((true, _), synYieldExpr, m) -> + let yieldFromExpr = mkSourceExpr synYieldExpr + + if + isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "YieldFrom" builderTy) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("YieldFrom"), m)) + + let yieldFromCall = mkSynCall "YieldFrom" m [ yieldFromExpr ] + + let yieldFromCall = + if IsControlFlowExpression synYieldExpr then + yieldFromCall + else + SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes m, false, yieldFromCall) - let clauses = clauses |> List.map (fun (SynMatchClause(pat, cond, clauseComp, patm, sp, trivia)) -> SynMatchClause(pat, cond, transNoQueryOps clauseComp, patm, sp, trivia)) - let consumeExpr = SynExpr.MatchLambda(true, mTryToLast, clauses, spWith2, mTryToLast) + Some(translatedCtxt yieldFromCall) - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mTry ad "TryWith" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("TryWith"), mTry)) + | SynExpr.YieldOrReturnFrom((false, _), synReturnExpr, m) -> + let returnFromExpr = mkSourceExpr synReturnExpr - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mTry ad "Delay" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("Delay"), mTry)) + if isQuery then + error (Error(FSComp.SR.tcReturnMayNotBeUsedInQueries (), m)) - let innerExpr = transNoQueryOps innerComp + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "ReturnFrom" builderTy + ) + then + error (Error(FSComp.SR.tcRequireBuilderMethod ("ReturnFrom"), m)) - let innerExpr = - match spTry with - | DebugPointAtTry.Yes _ -> - SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes mTry, true, innerExpr) - | _ -> innerExpr - - let callExpr = - mkSynCall "TryWith" mTry [ - mkSynCall "Delay" mTry [mkSynDelay2 innerExpr] - consumeExpr - ] - - Some(translatedCtxt callExpr) - - | SynExpr.YieldOrReturnFrom ((true, _), synYieldExpr, m) -> - let yieldFromExpr = mkSourceExpr synYieldExpr - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "YieldFrom" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("YieldFrom"), m)) - - let yieldFromCall = mkSynCall "YieldFrom" m [yieldFromExpr] - - let yieldFromCall = - if IsControlFlowExpression synYieldExpr then - yieldFromCall - else - SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes m, false, yieldFromCall) + let returnFromCall = mkSynCall "ReturnFrom" m [ returnFromExpr ] - Some (translatedCtxt yieldFromCall) - - | SynExpr.YieldOrReturnFrom ((false, _), synReturnExpr, m) -> - let returnFromExpr = mkSourceExpr synReturnExpr - if isQuery then error(Error(FSComp.SR.tcReturnMayNotBeUsedInQueries(), m)) - - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "ReturnFrom" builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod("ReturnFrom"), m)) + let returnFromCall = + if IsControlFlowExpression synReturnExpr then + returnFromCall + else + SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes m, false, returnFromCall) - let returnFromCall = mkSynCall "ReturnFrom" m [returnFromExpr] + Some(translatedCtxt returnFromCall) - let returnFromCall = - if IsControlFlowExpression synReturnExpr then - returnFromCall - else - SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes m, false, returnFromCall) + | SynExpr.YieldOrReturn((isYield, _), synYieldOrReturnExpr, m) -> + let methName = (if isYield then "Yield" else "Return") - Some (translatedCtxt returnFromCall) + if isQuery && not isYield then + error (Error(FSComp.SR.tcReturnMayNotBeUsedInQueries (), m)) - | SynExpr.YieldOrReturn ((isYield, _), synYieldOrReturnExpr, m) -> - let methName = (if isYield then "Yield" else "Return") - if isQuery && not isYield then error(Error(FSComp.SR.tcReturnMayNotBeUsedInQueries(), m)) + if + isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad methName builderTy) + then + error (Error(FSComp.SR.tcRequireBuilderMethod (methName), m)) - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad methName builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod(methName), m)) + let yieldOrReturnCall = mkSynCall methName m [ synYieldOrReturnExpr ] - let yieldOrReturnCall = mkSynCall methName m [synYieldOrReturnExpr] - - let yieldOrReturnCall = - if IsControlFlowExpression synYieldOrReturnExpr then - yieldOrReturnCall - else - SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes m, false, yieldOrReturnCall) + let yieldOrReturnCall = + if IsControlFlowExpression synYieldOrReturnExpr then + yieldOrReturnCall + else + SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes m, false, yieldOrReturnCall) - Some(translatedCtxt yieldOrReturnCall) + Some(translatedCtxt yieldOrReturnCall) - | _ -> None + | _ -> None and consumeCustomOpClauses q (varSpace: LazyWithContext<_, _>) dataCompPrior compClausesExpr lastUsesBind mClause = @@ -1536,10 +2406,10 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol let varSpaceSimplePat = mkSimplePatForVarSpace mClause patvs let varSpacePat = mkPatForVarSpace mClause patvs - match compClausesExpr with - + match compClausesExpr with + // Detect one custom operation... This clause will always match at least once... - | OptionalSequential (CustomOperationClause (nm, opDatas, opExpr, mClause, optionalIntoPat), optionalCont) -> + | OptionalSequential(CustomOperationClause(nm, opDatas, opExpr, mClause, optionalIntoPat), optionalCont) -> let opName, _, _, _, _, _, _, _, methInfo = opDatas[0] let isLikeZip = customOperationIsLikeZip nm @@ -1547,16 +2417,18 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol let isLikeGroupJoin = customOperationIsLikeZip nm // Record the resolution of the custom operation for posterity - let item = Item.CustomOperation (opName, (fun () -> customOpUsageText nm), Some methInfo) + let item = + Item.CustomOperation(opName, (fun () -> customOpUsageText nm), Some methInfo) // FUTURE: consider whether we can do better than emptyTyparInst here, in order to display instantiations // of type variables in the quick info provided in the IDE. CallNameResolutionSink cenv.tcSink (nm.idRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, env.eAccessRights) if isLikeZip || isLikeJoin || isLikeGroupJoin then - errorR(Error(FSComp.SR.tcBinaryOperatorRequiresBody(nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) - match optionalCont with - | None -> + errorR (Error(FSComp.SR.tcBinaryOperatorRequiresBody (nm.idText, Option.get (customOpUsageText nm)), nm.idRange)) + + match optionalCont with + | None -> // we are about to drop the 'opExpr' AST on the floor. we've already reported an error. attempt to get name resolutions before dropping it RecordNameAndTypeResolutions cenv env tpenv opExpr dataCompPrior @@ -1568,217 +2440,346 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol let expectedArgCount = tryExpectedArgCountForCustomOperator nm - let dataCompAfterOp = - match opExpr with + let dataCompAfterOp = + match opExpr with | StripApps(SingleIdent nm, args) -> let argCountsMatch = match expectedArgCount with | Some n -> n = args.Length | None -> cenv.g.langVersion.SupportsFeature LanguageFeature.OverloadsForCustomOperations + if argCountsMatch then // Check for the [] attribute on each argument position - let args = args |> List.mapi (fun i arg -> - if isCustomOperationProjectionParameter (i+1) nm then - SynExpr.Lambda (false, false, varSpaceSimplePat, arg, None, arg.Range.MakeSynthetic(), SynExprLambdaTrivia.Zero) - else arg) + let args = + args + |> List.mapi (fun i arg -> + if isCustomOperationProjectionParameter (i + 1) nm then + SynExpr.Lambda( + false, + false, + varSpaceSimplePat, + arg, + None, + arg.Range.MakeSynthetic(), + SynExprLambdaTrivia.Zero + ) + else + arg) + mkSynCall methInfo.DisplayName mClause (dataCompPrior :: args) - else + else let expectedArgCount = defaultArg expectedArgCount 0 - errorR(Error(FSComp.SR.tcCustomOperationHasIncorrectArgCount(nm.idText, expectedArgCount, args.Length), nm.idRange)) - mkSynCall methInfo.DisplayName mClause ([ dataCompPrior ] @ List.init expectedArgCount (fun i -> arbExpr("_arg" + string i, mClause))) + + errorR ( + Error( + FSComp.SR.tcCustomOperationHasIncorrectArgCount (nm.idText, expectedArgCount, args.Length), + nm.idRange + ) + ) + + mkSynCall + methInfo.DisplayName + mClause + ([ dataCompPrior ] + @ List.init expectedArgCount (fun i -> arbExpr ("_arg" + string i, mClause))) | _ -> failwith "unreachable" - match optionalCont with - | None -> - match optionalIntoPat with - | Some intoPat -> errorR(Error(FSComp.SR.tcIntoNeedsRestOfQuery(), intoPat.Range)) + match optionalCont with + | None -> + match optionalIntoPat with + | Some intoPat -> errorR (Error(FSComp.SR.tcIntoNeedsRestOfQuery (), intoPat.Range)) | None -> () + dataCompAfterOp - | Some contExpr -> - - // select a.Name into name; ... - // distinct into d; ... - // - // Rebind the into pattern and process the rest of the clauses - match optionalIntoPat with - | Some intoPat -> - if not (customOperationAllowsInto nm) then - error(Error(FSComp.SR.tcOperatorDoesntAcceptInto(nm.idText), intoPat.Range)) - - // Rebind using either for ... or let!.... - let rebind = - if maintainsVarSpaceUsingBind then - SynExpr.LetOrUseBang (DebugPointAtBinding.NoneAtLet, false, false, intoPat, dataCompAfterOp, [], contExpr, intoPat.Range, SynExprLetOrUseBangTrivia.Zero) - else - SynExpr.ForEach (DebugPointAtFor.No, DebugPointAtInOrTo.No, SeqExprOnly false, false, intoPat, dataCompAfterOp, contExpr, intoPat.Range) - - trans CompExprTranslationPass.Initial q emptyVarSpace rebind id - - // select a.Name; ... - // distinct; ... - // - // Process the rest of the clauses - | None -> - if maintainsVarSpace || maintainsVarSpaceUsingBind then - consumeCustomOpClauses q varSpace dataCompAfterOp contExpr maintainsVarSpaceUsingBind mClause + | Some contExpr -> + + // select a.Name into name; ... + // distinct into d; ... + // + // Rebind the into pattern and process the rest of the clauses + match optionalIntoPat with + | Some intoPat -> + if not (customOperationAllowsInto nm) then + error (Error(FSComp.SR.tcOperatorDoesntAcceptInto (nm.idText), intoPat.Range)) + + // Rebind using either for ... or let!.... + let rebind = + if maintainsVarSpaceUsingBind then + SynExpr.LetOrUseBang( + DebugPointAtBinding.NoneAtLet, + false, + false, + intoPat, + dataCompAfterOp, + [], + contExpr, + intoPat.Range, + SynExprLetOrUseBangTrivia.Zero + ) else - consumeCustomOpClauses q emptyVarSpace dataCompAfterOp contExpr false mClause + SynExpr.ForEach( + DebugPointAtFor.No, + DebugPointAtInOrTo.No, + SeqExprOnly false, + false, + intoPat, + dataCompAfterOp, + contExpr, + intoPat.Range + ) + + trans CompExprTranslationPass.Initial q emptyVarSpace rebind id + + // select a.Name; ... + // distinct; ... + // + // Process the rest of the clauses + | None -> + if maintainsVarSpace || maintainsVarSpaceUsingBind then + consumeCustomOpClauses q varSpace dataCompAfterOp contExpr maintainsVarSpaceUsingBind mClause + else + consumeCustomOpClauses q emptyVarSpace dataCompAfterOp contExpr false mClause - // No more custom operator clauses in compClausesExpr, but there may be clauses like join, yield etc. + // No more custom operator clauses in compClausesExpr, but there may be clauses like join, yield etc. // Bind/iterate the dataCompPrior and use compClausesExpr as the body. - | _ -> + | _ -> // Rebind using either for ... or let!.... - let rebind = - if lastUsesBind then - SynExpr.LetOrUseBang (DebugPointAtBinding.NoneAtLet, false, false, varSpacePat, dataCompPrior, [], compClausesExpr, compClausesExpr.Range, SynExprLetOrUseBangTrivia.Zero) - else - SynExpr.ForEach (DebugPointAtFor.No, DebugPointAtInOrTo.No, SeqExprOnly false, false, varSpacePat, dataCompPrior, compClausesExpr, compClausesExpr.Range) - + let rebind = + if lastUsesBind then + SynExpr.LetOrUseBang( + DebugPointAtBinding.NoneAtLet, + false, + false, + varSpacePat, + dataCompPrior, + [], + compClausesExpr, + compClausesExpr.Range, + SynExprLetOrUseBangTrivia.Zero + ) + else + SynExpr.ForEach( + DebugPointAtFor.No, + DebugPointAtInOrTo.No, + SeqExprOnly false, + false, + varSpacePat, + dataCompPrior, + compClausesExpr, + compClausesExpr.Range + ) + trans CompExprTranslationPass.Initial q varSpace rebind id and transNoQueryOps comp = trans CompExprTranslationPass.Initial CustomOperationsMode.Denied emptyVarSpace comp id - and trans firstTry q varSpace comp translatedCtxt = - match tryTrans firstTry q varSpace comp translatedCtxt with + and trans firstTry q varSpace comp translatedCtxt = + match tryTrans firstTry q varSpace comp translatedCtxt with | Some e -> e - | None -> + | None -> // This only occurs in final position in a sequence - match comp with + match comp with // "do! expr;" in final position is treated as { let! () = expr in return () } when Return is provided (and no Zero with Default attribute is available) or as { let! () = expr in zero } otherwise - | SynExpr.DoBang (rhsExpr, m) -> + | SynExpr.DoBang(rhsExpr, m) -> let mUnit = rhsExpr.Range let rhsExpr = mkSourceExpr rhsExpr - if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), m)) + + if isQuery then + error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), m)) + let bodyExpr = - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Return" builderTy) then + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Return" builderTy + ) + then SynExpr.ImplicitZero m else - match TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Zero" builderTy with + match + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Zero" builderTy + with | minfo :: _ when MethInfoHasAttribute cenv.g m cenv.g.attrib_DefaultValueAttribute minfo -> SynExpr.ImplicitZero m - | _ -> SynExpr.YieldOrReturn ((false, true), SynExpr.Const (SynConst.Unit, m), m) - let letBangBind = SynExpr.LetOrUseBang (DebugPointAtBinding.NoneAtDo, false, false, SynPat.Const(SynConst.Unit, mUnit), rhsExpr, [], bodyExpr, m, SynExprLetOrUseBangTrivia.Zero) + | _ -> SynExpr.YieldOrReturn((false, true), SynExpr.Const(SynConst.Unit, m), m) + + let letBangBind = + SynExpr.LetOrUseBang( + DebugPointAtBinding.NoneAtDo, + false, + false, + SynPat.Const(SynConst.Unit, mUnit), + rhsExpr, + [], + bodyExpr, + m, + SynExprLetOrUseBangTrivia.Zero + ) + trans CompExprTranslationPass.Initial q varSpace letBangBind translatedCtxt // "expr;" in final position is treated as { expr; zero } // Suppress the sequence point on the "zero" - | _ -> + | _ -> // Check for 'where x > y' and other mis-applications of infix operators. If detected, give a good error message, and just ignore comp - if isQuery && checkForBinaryApp comp then + if isQuery && checkForBinaryApp comp then trans CompExprTranslationPass.Initial q varSpace (SynExpr.ImplicitZero comp.Range) translatedCtxt else - if isQuery && not comp.IsArbExprAndThusAlreadyReportedError then - match comp with + if isQuery && not comp.IsArbExprAndThusAlreadyReportedError then + match comp with | SynExpr.JoinIn _ -> () // an error will be reported later when we process innerComp1 as a sequential - | _ -> errorR(Error(FSComp.SR.tcUnrecognizedQueryOperator(), comp.RangeOfFirstPortion)) + | _ -> errorR (Error(FSComp.SR.tcUnrecognizedQueryOperator (), comp.RangeOfFirstPortion)) + trans CompExprTranslationPass.Initial q varSpace (SynExpr.ImplicitZero comp.Range) (fun holeFill -> - let fillExpr = - if enableImplicitYield then - let implicitYieldExpr = mkSynCall "Yield" comp.Range [comp] - SynExpr.SequentialOrImplicitYield(DebugPointAtSequential.SuppressExpr, comp, holeFill, implicitYieldExpr, comp.Range) + let fillExpr = + if enableImplicitYield then + let implicitYieldExpr = mkSynCall "Yield" comp.Range [ comp ] + + SynExpr.SequentialOrImplicitYield( + DebugPointAtSequential.SuppressExpr, + comp, + holeFill, + implicitYieldExpr, + comp.Range + ) else SynExpr.Sequential(DebugPointAtSequential.SuppressExpr, true, comp, holeFill, comp.Range) - translatedCtxt fillExpr) - and transBind q varSpace bindRange addBindDebugPoint bindName bindArgs (consumePat: SynPat) (innerComp: SynExpr) translatedCtxt = + translatedCtxt fillExpr) + + and transBind q varSpace bindRange addBindDebugPoint bindName bindArgs (consumePat: SynPat) (innerComp: SynExpr) translatedCtxt = let innerRange = innerComp.Range - - let innerCompReturn = + + let innerCompReturn = if cenv.g.langVersion.SupportsFeature LanguageFeature.AndBang then convertSimpleReturnToExpr varSpace innerComp - else None + else + None + + match innerCompReturn with + | Some(innerExpr, customOpInfo) when + (let bindName = bindName + "Return" - match innerCompReturn with - | Some (innerExpr, customOpInfo) when - (let bindName = bindName + "Return" - not (isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad bindName builderTy))) -> + not ( + isNil ( + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad bindName builderTy + ) + )) + -> let bindName = bindName + "Return" - + // Build the `BindReturn` call let dataCompPriorToOp = - let consumeExpr = SynExpr.MatchLambda(false, consumePat.Range, [SynMatchClause(consumePat, None, innerExpr, innerRange, DebugPointAtTarget.Yes, SynMatchClauseTrivia.Zero)], DebugPointAtBinding.NoneAtInvisible, innerRange) - translatedCtxt (mkSynCall bindName bindRange (bindArgs @ [consumeExpr])) - - match customOpInfo with + let consumeExpr = + SynExpr.MatchLambda( + false, + consumePat.Range, + [ + SynMatchClause(consumePat, None, innerExpr, innerRange, DebugPointAtTarget.Yes, SynMatchClauseTrivia.Zero) + ], + DebugPointAtBinding.NoneAtInvisible, + innerRange + ) + + translatedCtxt (mkSynCall bindName bindRange (bindArgs @ [ consumeExpr ])) + + match customOpInfo with | None -> dataCompPriorToOp - | Some (innerComp, mClause) -> + | Some(innerComp, mClause) -> // If the `BindReturn` was forced by a custom operation, continue to process the clauses of the CustomOp consumeCustomOpClauses q varSpace dataCompPriorToOp innerComp false mClause - | _ -> + | _ -> - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad bindName builderTy) then - error(Error(FSComp.SR.tcRequireBuilderMethod(bindName), bindRange)) + if + isNil ( + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad bindName builderTy + ) + then + error (Error(FSComp.SR.tcRequireBuilderMethod (bindName), bindRange)) // Build the `Bind` call trans CompExprTranslationPass.Initial q varSpace innerComp (fun holeFill -> - let consumeExpr = SynExpr.MatchLambda(false, consumePat.Range, [SynMatchClause(consumePat, None, holeFill, innerRange, DebugPointAtTarget.Yes, SynMatchClauseTrivia.Zero)], DebugPointAtBinding.NoneAtInvisible, innerRange) - let bindCall = mkSynCall bindName bindRange (bindArgs @ [consumeExpr]) + let consumeExpr = + SynExpr.MatchLambda( + false, + consumePat.Range, + [ + SynMatchClause(consumePat, None, holeFill, innerRange, DebugPointAtTarget.Yes, SynMatchClauseTrivia.Zero) + ], + DebugPointAtBinding.NoneAtInvisible, + innerRange + ) + + let bindCall = mkSynCall bindName bindRange (bindArgs @ [ consumeExpr ]) translatedCtxt (bindCall |> addBindDebugPoint)) /// This function is for desugaring into .Bind{N}Return calls if possible /// The outer option indicates if .BindReturn is possible. When it returns None, .BindReturn cannot be used /// The inner option indicates if a custom operation is involved inside and convertSimpleReturnToExpr varSpace innerComp = - match innerComp with - | SynExpr.YieldOrReturn ((false, _), returnExpr, m) -> + match innerComp with + | SynExpr.YieldOrReturn((false, _), returnExpr, m) -> let returnExpr = SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes m, false, returnExpr) - Some (returnExpr, None) + Some(returnExpr, None) - | SynExpr.Match (spMatch, expr, clauses, m, trivia) -> - let clauses = - clauses |> List.map (fun (SynMatchClause(pat, cond, innerComp2, patm, sp, trivia)) -> + | SynExpr.Match(spMatch, expr, clauses, m, trivia) -> + let clauses = + clauses + |> List.map (fun (SynMatchClause(pat, cond, innerComp2, patm, sp, trivia)) -> match convertSimpleReturnToExpr varSpace innerComp2 with | None -> None // failure - | Some (_, Some _) -> None // custom op on branch = failure - | Some (innerExpr2, None) -> Some (SynMatchClause(pat, cond, innerExpr2, patm, sp, trivia))) + | Some(_, Some _) -> None // custom op on branch = failure + | Some(innerExpr2, None) -> Some(SynMatchClause(pat, cond, innerExpr2, patm, sp, trivia))) + if clauses |> List.forall Option.isSome then - Some (SynExpr.Match (spMatch, expr, (clauses |> List.map Option.get), m, trivia), None) + Some(SynExpr.Match(spMatch, expr, (clauses |> List.map Option.get), m, trivia), None) else None - | SynExpr.IfThenElse (guardExpr, thenComp, elseCompOpt, spIfToThen, isRecovery, mIfToEndOfElseBranch, trivia) -> + | SynExpr.IfThenElse(guardExpr, thenComp, elseCompOpt, spIfToThen, isRecovery, mIfToEndOfElseBranch, trivia) -> match convertSimpleReturnToExpr varSpace thenComp with | None -> None - | Some (_, Some _) -> None - | Some (thenExpr, None) -> - let elseExprOptOpt = - match elseCompOpt with - // When we are missing an 'else' part alltogether in case of 'if cond then return exp', we fallback from BindReturn into regular Bind+Return - | None -> None - | Some elseComp -> - match convertSimpleReturnToExpr varSpace elseComp with - | None -> None // failure - | Some (_, Some _) -> None // custom op on branch = failure - | Some (elseExpr, None) -> Some (Some elseExpr) - match elseExprOptOpt with - | None -> None - | Some elseExprOpt -> Some (SynExpr.IfThenElse (guardExpr, thenExpr, elseExprOpt, spIfToThen, isRecovery, mIfToEndOfElseBranch, trivia), None) + | Some(_, Some _) -> None + | Some(thenExpr, None) -> + let elseExprOptOpt = + match elseCompOpt with + // When we are missing an 'else' part alltogether in case of 'if cond then return exp', we fallback from BindReturn into regular Bind+Return + | None -> None + | Some elseComp -> + match convertSimpleReturnToExpr varSpace elseComp with + | None -> None // failure + | Some(_, Some _) -> None // custom op on branch = failure + | Some(elseExpr, None) -> Some(Some elseExpr) + + match elseExprOptOpt with + | None -> None + | Some elseExprOpt -> + Some(SynExpr.IfThenElse(guardExpr, thenExpr, elseExprOpt, spIfToThen, isRecovery, mIfToEndOfElseBranch, trivia), None) - | SynExpr.LetOrUse (isRec, false, binds, innerComp, m, trivia) -> + | SynExpr.LetOrUse(isRec, false, binds, innerComp, m, trivia) -> match convertSimpleReturnToExpr varSpace innerComp with | None -> None - | Some (_, Some _) -> None - | Some (innerExpr, None) -> Some (SynExpr.LetOrUse (isRec, false, binds, innerExpr, m, trivia), None) + | Some(_, Some _) -> None + | Some(innerExpr, None) -> Some(SynExpr.LetOrUse(isRec, false, binds, innerExpr, m, trivia), None) - | OptionalSequential (CustomOperationClause (nm, _, _, mClause, _), _) when customOperationMaintainsVarSpaceUsingBind nm -> + | OptionalSequential(CustomOperationClause(nm, _, _, mClause, _), _) when customOperationMaintainsVarSpaceUsingBind nm -> let patvs, _env = varSpace.Force comp.Range let varSpaceExpr = mkExprForVarSpace mClause patvs - - Some (varSpaceExpr, Some (innerComp, mClause)) - | SynExpr.Sequential (sp, true, innerComp1, innerComp2, m) -> + Some(varSpaceExpr, Some(innerComp, mClause)) + + | SynExpr.Sequential(sp, true, innerComp1, innerComp2, m) -> // Check the first part isn't a computation expression construct if isSimpleExpr innerComp1 then // Check the second part is a simple return match convertSimpleReturnToExpr varSpace innerComp2 with | None -> None - | Some (innerExpr2, optionalCont) -> Some (SynExpr.Sequential (sp, true, innerComp1, innerExpr2, m), optionalCont) + | Some(innerExpr2, optionalCont) -> Some(SynExpr.Sequential(sp, true, innerComp1, innerExpr2, m), optionalCont) else None @@ -1787,7 +2788,7 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol /// Check if an expression has no computation expression constructs and isSimpleExpr comp = - match comp with + match comp with | ForEachThenJoinOrGroupJoinOrZipClause false _ -> false | SynExpr.ForEach _ -> false | SynExpr.For _ -> false @@ -1795,61 +2796,85 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol | SynExpr.WhileBang _ -> false | SynExpr.TryFinally _ -> false | SynExpr.ImplicitZero _ -> false - | OptionalSequential (JoinOrGroupJoinOrZipClause _, _) -> false - | OptionalSequential (CustomOperationClause _, _) -> false - | SynExpr.Sequential (_, _, innerComp1, innerComp2, _) -> isSimpleExpr innerComp1 && isSimpleExpr innerComp2 - | SynExpr.IfThenElse (thenExpr=thenComp; elseExpr=elseCompOpt) -> - isSimpleExpr thenComp && (match elseCompOpt with None -> true | Some c -> isSimpleExpr c) - | SynExpr.LetOrUse (body=innerComp) -> isSimpleExpr innerComp + | OptionalSequential(JoinOrGroupJoinOrZipClause _, _) -> false + | OptionalSequential(CustomOperationClause _, _) -> false + | SynExpr.Sequential(_, _, innerComp1, innerComp2, _) -> isSimpleExpr innerComp1 && isSimpleExpr innerComp2 + | SynExpr.IfThenElse(thenExpr = thenComp; elseExpr = elseCompOpt) -> + isSimpleExpr thenComp + && (match elseCompOpt with + | None -> true + | Some c -> isSimpleExpr c) + | SynExpr.LetOrUse(body = innerComp) -> isSimpleExpr innerComp | SynExpr.LetOrUseBang _ -> false - | SynExpr.Match (clauses=clauses) -> - clauses |> List.forall (fun (SynMatchClause(resultExpr = innerComp)) -> isSimpleExpr innerComp) + | SynExpr.Match(clauses = clauses) -> + clauses + |> List.forall (fun (SynMatchClause(resultExpr = innerComp)) -> isSimpleExpr innerComp) | SynExpr.MatchBang _ -> false - | SynExpr.TryWith (tryExpr=innerComp; withCases=clauses) -> - isSimpleExpr innerComp && - clauses |> List.forall (fun (SynMatchClause(resultExpr = clauseComp)) -> isSimpleExpr clauseComp) + | SynExpr.TryWith(tryExpr = innerComp; withCases = clauses) -> + isSimpleExpr innerComp + && clauses + |> List.forall (fun (SynMatchClause(resultExpr = clauseComp)) -> isSimpleExpr clauseComp) | SynExpr.YieldOrReturnFrom _ -> false | SynExpr.YieldOrReturn _ -> false | SynExpr.DoBang _ -> false | _ -> true - let basicSynExpr = - trans CompExprTranslationPass.Initial (hasCustomOperations ()) (LazyWithContext.NotLazy ([], env)) comp id + let basicSynExpr = + trans CompExprTranslationPass.Initial (hasCustomOperations ()) (LazyWithContext.NotLazy([], env)) comp id - let mDelayOrQuoteOrRun = mBuilderVal.NoteSourceConstruct(NotedSourceConstruct.DelayOrQuoteOrRun).MakeSynthetic() + let mDelayOrQuoteOrRun = + mBuilderVal + .NoteSourceConstruct(NotedSourceConstruct.DelayOrQuoteOrRun) + .MakeSynthetic() // Add a call to 'Delay' if the method is present - let delayedExpr = - match TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBuilderVal ad "Delay" builderTy with + let delayedExpr = + match TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBuilderVal ad "Delay" builderTy with | [] -> basicSynExpr - | _ -> - mkSynCall "Delay" mDelayOrQuoteOrRun [(mkSynDelay2 basicSynExpr)] + | _ -> mkSynCall "Delay" mDelayOrQuoteOrRun [ (mkSynDelay2 basicSynExpr) ] // Add a call to 'Quote' if the method is present - let quotedSynExpr = - if isAutoQuote then - SynExpr.Quote (mkSynIdGet mDelayOrQuoteOrRun (CompileOpName "<@ @>"), false, delayedExpr, true, mWhole) - else delayedExpr - + let quotedSynExpr = + if isAutoQuote then + SynExpr.Quote(mkSynIdGet mDelayOrQuoteOrRun (CompileOpName "<@ @>"), false, delayedExpr, true, mWhole) + else + delayedExpr + // Add a call to 'Run' if the method is present - let runExpr = - match TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBuilderVal ad "Run" builderTy with + let runExpr = + match TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mBuilderVal ad "Run" builderTy with | [] -> quotedSynExpr - | _ -> mkSynCall "Run" mDelayOrQuoteOrRun [quotedSynExpr] - - let lambdaExpr = - SynExpr.Lambda (false, false, SynSimplePats.SimplePats ([mkSynSimplePatVar false (mkSynId mBuilderVal builderValName)], [], mBuilderVal), runExpr, None, mBuilderVal, SynExprLambdaTrivia.Zero) + | _ -> mkSynCall "Run" mDelayOrQuoteOrRun [ quotedSynExpr ] + + let lambdaExpr = + SynExpr.Lambda( + false, + false, + SynSimplePats.SimplePats([ mkSynSimplePatVar false (mkSynId mBuilderVal builderValName) ], [], mBuilderVal), + runExpr, + None, + mBuilderVal, + SynExprLambdaTrivia.Zero + ) let env = match comp with - | SynExpr.YieldOrReturn ((true, _), _, _) -> { env with eContextInfo = ContextInfo.YieldInComputationExpression } - | SynExpr.YieldOrReturn ((_, true), _, _) -> { env with eContextInfo = ContextInfo.ReturnInComputationExpression } + | SynExpr.YieldOrReturn(flags = (true, _)) -> + { env with + eContextInfo = ContextInfo.YieldInComputationExpression + } + | SynExpr.YieldOrReturn(flags = (_, true)) -> + { env with + eContextInfo = ContextInfo.ReturnInComputationExpression + } | _ -> env - let lambdaExpr, tpenv = TcExpr cenv (MustEqual (mkFunTy g builderTy overallTy)) env tpenv lambdaExpr + let lambdaExpr, tpenv = + TcExpr cenv (MustEqual(mkFunTy g builderTy overallTy)) env tpenv lambdaExpr // beta-var-reduce to bind the builder using a 'let' binding - let coreExpr = mkApps cenv.g ((lambdaExpr, tyOfExpr cenv.g lambdaExpr), [], [interpExpr], mBuilderVal) + let coreExpr = + mkApps cenv.g ((lambdaExpr, tyOfExpr cenv.g lambdaExpr), [], [ interpExpr ], mBuilderVal) coreExpr, tpenv @@ -1858,13 +2883,16 @@ let mkSeqEmpty (cenv: cenv) env m genTy = let g = cenv.g let genResultTy = NewInferenceType g UnifyTypes cenv env m genTy (mkSeqTy g genResultTy) - mkCallSeqEmpty g m genResultTy + mkCallSeqEmpty g m genResultTy let mkSeqCollect (cenv: cenv) env m enumElemTy genTy lam enumExpr = let g = cenv.g let genResultTy = NewInferenceType g UnifyTypes cenv env m genTy (mkSeqTy cenv.g genResultTy) - let enumExpr = mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g enumElemTy) (tyOfExpr cenv.g enumExpr) enumExpr + + let enumExpr = + mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g enumElemTy) (tyOfExpr cenv.g enumExpr) enumExpr + mkCallSeqCollect cenv.g m enumElemTy genResultTy lam enumExpr let mkSeqUsing (cenv: cenv) (env: TcEnv) m resourceTy genTy resourceExpr lam = @@ -1872,58 +2900,83 @@ let mkSeqUsing (cenv: cenv) (env: TcEnv) m resourceTy genTy resourceExpr lam = AddCxTypeMustSubsumeType ContextInfo.NoContext env.DisplayEnv cenv.css m NoTrace cenv.g.system_IDisposable_ty resourceTy let genResultTy = NewInferenceType g UnifyTypes cenv env m genTy (mkSeqTy cenv.g genResultTy) - mkCallSeqUsing cenv.g m resourceTy genResultTy resourceExpr lam + mkCallSeqUsing cenv.g m resourceTy genResultTy resourceExpr lam let mkSeqDelay (cenv: cenv) env m genTy lam = let g = cenv.g let genResultTy = NewInferenceType g UnifyTypes cenv env m genTy (mkSeqTy cenv.g genResultTy) - mkCallSeqDelay cenv.g m genResultTy (mkUnitDelayLambda cenv.g m lam) + mkCallSeqDelay cenv.g m genResultTy (mkUnitDelayLambda cenv.g m lam) let mkSeqAppend (cenv: cenv) env m genTy e1 e2 = let g = cenv.g let genResultTy = NewInferenceType g UnifyTypes cenv env m genTy (mkSeqTy cenv.g genResultTy) - let e1 = mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g genResultTy) (tyOfExpr cenv.g e1) e1 - let e2 = mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g genResultTy) (tyOfExpr cenv.g e2) e2 - mkCallSeqAppend cenv.g m genResultTy e1 e2 + + let e1 = + mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g genResultTy) (tyOfExpr cenv.g e1) e1 + + let e2 = + mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g genResultTy) (tyOfExpr cenv.g e2) e2 + + mkCallSeqAppend cenv.g m genResultTy e1 e2 let mkSeqFromFunctions (cenv: cenv) env m genTy e1 e2 = let g = cenv.g let genResultTy = NewInferenceType g UnifyTypes cenv env m genTy (mkSeqTy cenv.g genResultTy) - let e2 = mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g genResultTy) (tyOfExpr cenv.g e2) e2 - mkCallSeqGenerated cenv.g m genResultTy e1 e2 + + let e2 = + mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g genResultTy) (tyOfExpr cenv.g e2) e2 + + mkCallSeqGenerated cenv.g m genResultTy e1 e2 let mkSeqFinally (cenv: cenv) env m genTy e1 e2 = let g = cenv.g let genResultTy = NewInferenceType g UnifyTypes cenv env m genTy (mkSeqTy cenv.g genResultTy) - let e1 = mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g genResultTy) (tyOfExpr cenv.g e1) e1 - mkCallSeqFinally cenv.g m genResultTy e1 e2 + + let e1 = + mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g genResultTy) (tyOfExpr cenv.g e1) e1 + + mkCallSeqFinally cenv.g m genResultTy e1 e2 let mkSeqTryWith (cenv: cenv) env m genTy origSeq exnFilter exnHandler = let g = cenv.g let genResultTy = NewInferenceType g UnifyTypes cenv env m genTy (mkSeqTy cenv.g genResultTy) - let origSeq = mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g genResultTy) (tyOfExpr cenv.g origSeq) origSeq - mkCallSeqTryWith cenv.g m genResultTy origSeq exnFilter exnHandler -let mkSeqExprMatchClauses (pat, vspecs) innerExpr = - [MatchClause(pat, None, TTarget(vspecs, innerExpr, None), pat.Range) ] + let origSeq = + mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g genResultTy) (tyOfExpr cenv.g origSeq) origSeq + + mkCallSeqTryWith cenv.g m genResultTy origSeq exnFilter exnHandler + +let mkSeqExprMatchClauses (pat, vspecs) innerExpr = + [ MatchClause(pat, None, TTarget(vspecs, innerExpr, None), pat.Range) ] -let compileSeqExprMatchClauses (cenv: cenv) env inputExprMark (pat: Pattern, vspecs) innerExpr inputExprOpt bindPatTy genInnerTy = +let compileSeqExprMatchClauses (cenv: cenv) env inputExprMark (pat: Pattern, vspecs) innerExpr inputExprOpt bindPatTy genInnerTy = let patMark = pat.Range - let tclauses = mkSeqExprMatchClauses (pat, vspecs) innerExpr - CompilePatternForMatchClauses cenv env inputExprMark patMark false ThrowIncompleteMatchException inputExprOpt bindPatTy genInnerTy tclauses + let tclauses = mkSeqExprMatchClauses (pat, vspecs) innerExpr + + CompilePatternForMatchClauses + cenv + env + inputExprMark + patMark + false + ThrowIncompleteMatchException + inputExprOpt + bindPatTy + genInnerTy + tclauses /// This case is used for computation expressions which are sequence expressions. Technically the code path is different because it /// typechecks rather than doing a shallow syntactic translation, and generates calls into the Seq.* library -/// and helpers rather than to the builder methods (there is actually no builder for 'seq' in the library). -/// These are later detected by state machine compilation. +/// and helpers rather than to the builder methods (there is actually no builder for 'seq' in the library). +/// These are later detected by state machine compilation. /// /// Also "ienumerable extraction" is performed on arguments to "for". -let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = +let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = let g = cenv.g let genEnumElemTy = NewInferenceType g @@ -1933,68 +2986,94 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = let flex = not (isTyparTy cenv.g genEnumElemTy) // If there are no 'yield' in the computation expression then allow the type-directed rule - // interpreting non-unit-typed expressions in statement positions as 'yield'. 'yield!' may be + // interpreting non-unit-typed expressions in statement positions as 'yield'. 'yield!' may be // present in the computation expression. let enableImplicitYield = cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield && (YieldFree cenv comp) - let mkSeqDelayedExpr m (coreExpr: Expr) = + let mkSeqDelayedExpr m (coreExpr: Expr) = let overallTy = tyOfExpr cenv.g coreExpr mkSeqDelay cenv env m overallTy coreExpr let rec tryTcSequenceExprBody env genOuterTy tpenv comp = - match comp with - | SynExpr.ForEach (spFor, spIn, SeqExprOnly _seqExprOnly, _isFromSource, pat, pseudoEnumExpr, innerComp, _m) -> + match comp with + | SynExpr.ForEach(spFor, spIn, SeqExprOnly _seqExprOnly, _isFromSource, pat, pseudoEnumExpr, innerComp, _m) -> let pseudoEnumExpr = match RewriteRangeExpr pseudoEnumExpr with | Some e -> e | None -> pseudoEnumExpr - // This expression is not checked with the knowledge it is an IEnumerable, since we permit other enumerable types with GetEnumerator/MoveNext methods, as does C# - let pseudoEnumExpr, arbitraryTy, tpenv = TcExprOfUnknownType cenv env tpenv pseudoEnumExpr - let enumExpr, enumElemTy = ConvertArbitraryExprToEnumerable cenv arbitraryTy env pseudoEnumExpr - let patR, _, vspecs, envinner, tpenv = TcMatchPattern cenv enumElemTy env tpenv pat None + // This expression is not checked with the knowledge it is an IEnumerable, since we permit other enumerable types with GetEnumerator/MoveNext methods, as does C# + let pseudoEnumExpr, arbitraryTy, tpenv = + TcExprOfUnknownType cenv env tpenv pseudoEnumExpr + + let enumExpr, enumElemTy = + ConvertArbitraryExprToEnumerable cenv arbitraryTy env pseudoEnumExpr + + let patR, _, vspecs, envinner, tpenv = + TcMatchPattern cenv enumElemTy env tpenv pat None + let innerExpr, tpenv = let envinner = { envinner with eIsControlFlow = true } tcSequenceExprBody envinner genOuterTy tpenv innerComp - + let enumExprRange = enumExpr.Range // We attach the debug point to the lambda expression so we can fetch it out again in LowerComputedListOrArraySeqExpr - let mFor = match spFor with DebugPointAtFor.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.For) | _ -> enumExprRange + let mFor = + match spFor with + | DebugPointAtFor.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.For) + | _ -> enumExprRange // We attach the debug point to the lambda expression so we can fetch it out again in LowerComputedListOrArraySeqExpr - let mIn = match spIn with DebugPointAtInOrTo.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.InOrTo) | _ -> pat.Range + let mIn = + match spIn with + | DebugPointAtInOrTo.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.InOrTo) + | _ -> pat.Range - match patR, vspecs, innerExpr with + match patR, vspecs, innerExpr with // Legacy peephole optimization: // "seq { .. for x in e1 -> e2 .. }" == "e1 |> Seq.map (fun x -> e2)" // "seq { .. for x in e1 do yield e2 .. }" == "e1 |> Seq.map (fun x -> e2)" // // This transformation is visible in quotations and thus needs to remain. - | (TPat_as (TPat_wild _, PatternValBinding (v, _), _), - [_], - DebugPoints(Expr.App (Expr.Val (vref, _, _), _, [genEnumElemTy], [yieldExpr], _mYield), recreate)) - when valRefEq cenv.g vref cenv.g.seq_singleton_vref -> + | (TPat_as(TPat_wild _, PatternValBinding(v, _), _), + [ _ ], + DebugPoints(Expr.App(Expr.Val(vref, _, _), _, [ genEnumElemTy ], [ yieldExpr ], _mYield), recreate)) when + valRefEq cenv.g vref cenv.g.seq_singleton_vref + -> // The debug point mFor is attached to the 'map' // The debug point mIn is attached to the lambda - // Note: the 'yield' part of the debug point for 'yield expr' is currently lost in debug points. + // Note: the 'yield' part of the debug point for 'yield expr' is currently lost in debug points. let lam = mkLambda mIn v (recreate yieldExpr, genEnumElemTy) - let enumExpr = mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g enumElemTy) (tyOfExpr cenv.g enumExpr) enumExpr + + let enumExpr = + mkCoerceIfNeeded cenv.g (mkSeqTy cenv.g enumElemTy) (tyOfExpr cenv.g enumExpr) enumExpr + Some(mkCallSeqMap cenv.g mFor enumElemTy genEnumElemTy lam enumExpr, tpenv) - | _ -> + | _ -> // The debug point mFor is attached to the 'collect' // The debug point mIn is attached to the lambda - let matchv, matchExpr = compileSeqExprMatchClauses cenv env enumExprRange (patR, vspecs) innerExpr None enumElemTy genOuterTy + let matchv, matchExpr = + compileSeqExprMatchClauses cenv env enumExprRange (patR, vspecs) innerExpr None enumElemTy genOuterTy + let lam = mkLambda mIn matchv (matchExpr, tyOfExpr cenv.g matchExpr) Some(mkSeqCollect cenv env mFor enumElemTy genOuterTy lam enumExpr, tpenv) - | SynExpr.For (forDebugPoint=spFor; toDebugPoint=spTo; ident=id; identBody=start; direction=dir; toBody=finish; doBody=innerComp; range=m) -> + | SynExpr.For( + forDebugPoint = spFor + toDebugPoint = spTo + ident = id + identBody = start + direction = dir + toBody = finish + doBody = innerComp + range = m) -> Some(tcSequenceExprBody env genOuterTy tpenv (elimFastIntegerForLoop (spFor, spTo, id, start, dir, finish, innerComp, m))) - | SynExpr.While (spWhile, guardExpr, innerComp, _m) -> + | SynExpr.While(spWhile, guardExpr, innerComp, _m) -> let guardExpr, tpenv = let env = { env with eIsControlFlow = false } TcExpr cenv (MustEqual cenv.g.bool_ty) env tpenv guardExpr @@ -2002,10 +3081,10 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = let innerExpr, tpenv = let env = { env with eIsControlFlow = true } tcSequenceExprBody env genOuterTy tpenv innerComp - + let guardExprMark = guardExpr.Range let guardLambdaExpr = mkUnitDelayLambda cenv.g guardExprMark guardExpr - + // We attach the debug point to the lambda expression so we can fetch it out again in LowerComputedListOrArraySeqExpr let mWhile = match spWhile with @@ -2015,78 +3094,105 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = let innerDelayedExpr = mkSeqDelayedExpr mWhile innerExpr Some(mkSeqFromFunctions cenv env guardExprMark genOuterTy guardLambdaExpr innerDelayedExpr, tpenv) - | SynExpr.TryFinally (innerComp, unwindExpr, mTryToLast, spTry, spFinally, trivia) -> + | SynExpr.TryFinally(innerComp, unwindExpr, mTryToLast, spTry, spFinally, trivia) -> let env = { env with eIsControlFlow = true } let innerExpr, tpenv = tcSequenceExprBody env genOuterTy tpenv innerComp let unwindExpr, tpenv = TcExpr cenv (MustEqual cenv.g.unit_ty) env tpenv unwindExpr - + // We attach the debug points to the lambda expressions so we can fetch it out again in LowerComputedListOrArraySeqExpr - let mTry = - match spTry with + let mTry = + match spTry with | DebugPointAtTry.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Try) | _ -> trivia.TryKeyword - let mFinally = - match spFinally with + let mFinally = + match spFinally with | DebugPointAtFinally.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Finally) | _ -> trivia.FinallyKeyword let innerExpr = mkSeqDelayedExpr mTry innerExpr let unwindExpr = mkUnitDelayLambda cenv.g mFinally unwindExpr - + Some(mkSeqFinally cenv env mTryToLast genOuterTy innerExpr unwindExpr, tpenv) - | SynExpr.Paren (_, _, _, m) when not (cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield)-> - error(Error(FSComp.SR.tcConstructIsAmbiguousInSequenceExpression(), m)) + | SynExpr.Paren(range = m) when not (cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield) -> + error (Error(FSComp.SR.tcConstructIsAmbiguousInSequenceExpression (), m)) - | SynExpr.ImplicitZero m -> - Some(mkSeqEmpty cenv env m genOuterTy, tpenv ) + | SynExpr.ImplicitZero m -> Some(mkSeqEmpty cenv env m genOuterTy, tpenv) - | SynExpr.DoBang (_rhsExpr, m) -> - error(Error(FSComp.SR.tcDoBangIllegalInSequenceExpression(), m)) + | SynExpr.DoBang(_rhsExpr, m) -> error (Error(FSComp.SR.tcDoBangIllegalInSequenceExpression (), m)) - | SynExpr.Sequential (sp, true, innerComp1, innerComp2, m) -> - let env1 = { env with eIsControlFlow = (match sp with DebugPointAtSequential.SuppressNeither | DebugPointAtSequential.SuppressExpr -> true | _ -> false) } + | SynExpr.Sequential(sp, true, innerComp1, innerComp2, m) -> + let env1 = + { env with + eIsControlFlow = + (match sp with + | DebugPointAtSequential.SuppressNeither + | DebugPointAtSequential.SuppressExpr -> true + | _ -> false) + } - let res, tpenv = tcSequenceExprBodyAsSequenceOrStatement env1 genOuterTy tpenv innerComp1 + let res, tpenv = + tcSequenceExprBodyAsSequenceOrStatement env1 genOuterTy tpenv innerComp1 - let env2 = { env with eIsControlFlow = (match sp with DebugPointAtSequential.SuppressNeither | DebugPointAtSequential.SuppressStmt -> true | _ -> false) } + let env2 = + { env with + eIsControlFlow = + (match sp with + | DebugPointAtSequential.SuppressNeither + | DebugPointAtSequential.SuppressStmt -> true + | _ -> false) + } // "expr; cexpr" is treated as sequential execution // "cexpr; cexpr" is treated as append - match res with - | Choice1Of2 innerExpr1 -> + match res with + | Choice1Of2 innerExpr1 -> let innerExpr2, tpenv = tcSequenceExprBody env2 genOuterTy tpenv innerComp2 let innerExpr2 = mkSeqDelayedExpr innerExpr2.Range innerExpr2 Some(mkSeqAppend cenv env innerComp1.Range genOuterTy innerExpr1 innerExpr2, tpenv) - | Choice2Of2 stmt1 -> + | Choice2Of2 stmt1 -> let innerExpr2, tpenv = tcSequenceExprBody env2 genOuterTy tpenv innerComp2 Some(Expr.Sequential(stmt1, innerExpr2, NormalSeq, m), tpenv) - | SynExpr.IfThenElse (guardExpr, thenComp, elseCompOpt, spIfToThen, _isRecovery, mIfToEndOfElseBranch, trivia) -> + | SynExpr.IfThenElse(guardExpr, thenComp, elseCompOpt, spIfToThen, _isRecovery, mIfToEndOfElseBranch, trivia) -> let guardExpr', tpenv = TcExpr cenv (MustEqual cenv.g.bool_ty) env tpenv guardExpr let env = { env with eIsControlFlow = true } let thenExpr, tpenv = tcSequenceExprBody env genOuterTy tpenv thenComp - let elseComp = (match elseCompOpt with Some c -> c | None -> SynExpr.ImplicitZero trivia.IfToThenRange) + + let elseComp = + (match elseCompOpt with + | Some c -> c + | None -> SynExpr.ImplicitZero trivia.IfToThenRange) + let elseExpr, tpenv = tcSequenceExprBody env genOuterTy tpenv elseComp Some(mkCond spIfToThen mIfToEndOfElseBranch genOuterTy guardExpr' thenExpr elseExpr, tpenv) // 'let x = expr in expr' - | SynExpr.LetOrUse (isUse=false (* not a 'use' binding *)) -> - TcLinearExprs - (fun overallTy envinner tpenv e -> tcSequenceExprBody envinner overallTy.Commit tpenv e) - cenv env overallTy - tpenv + | SynExpr.LetOrUse(isUse = false) -> + TcLinearExprs + (fun overallTy envinner tpenv e -> tcSequenceExprBody envinner overallTy.Commit tpenv e) + cenv + env + overallTy + tpenv true - comp - id |> Some + comp + id + |> Some // 'use x = expr in expr' - | SynExpr.LetOrUse (isUse=true; bindings=[SynBinding (kind=SynBindingKind.Normal; headPat=pat; expr=rhsExpr; debugPoint=spBind)]; body=innerComp; range=wholeExprMark) -> + | SynExpr.LetOrUse( + isUse = true + bindings = [ SynBinding(kind = SynBindingKind.Normal; headPat = pat; expr = rhsExpr; debugPoint = spBind) ] + body = innerComp + range = wholeExprMark) -> let bindPatTy = NewInferenceType g let inputExprTy = NewInferenceType g - let pat', _, vspecs, envinner, tpenv = TcMatchPattern cenv bindPatTy env tpenv pat None + + let pat', _, vspecs, envinner, tpenv = + TcMatchPattern cenv bindPatTy env tpenv pat None UnifyTypes cenv env m inputExprTy bindPatTy @@ -2098,84 +3204,117 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = let envinner = { envinner with eIsControlFlow = true } tcSequenceExprBody envinner genOuterTy tpenv innerComp - let mBind = - match spBind with + let mBind = + match spBind with | DebugPointAtBinding.Yes m -> m.NoteSourceConstruct(NotedSourceConstruct.Binding) | _ -> inputExpr.Range let inputExprMark = inputExpr.Range - let matchv, matchExpr = compileSeqExprMatchClauses cenv envinner inputExprMark (pat', vspecs) innerExpr (Some inputExpr) bindPatTy genOuterTy + let matchv, matchExpr = + compileSeqExprMatchClauses cenv envinner inputExprMark (pat', vspecs) innerExpr (Some inputExpr) bindPatTy genOuterTy let consumeExpr = mkLambda mBind matchv (matchExpr, genOuterTy) // The 'mBind' is attached to the lambda Some(mkSeqUsing cenv env wholeExprMark bindPatTy genOuterTy inputExpr consumeExpr, tpenv) - | SynExpr.LetOrUseBang (range=m) -> - error(Error(FSComp.SR.tcUseForInSequenceExpression(), m)) + | SynExpr.LetOrUseBang(range = m) -> error (Error(FSComp.SR.tcUseForInSequenceExpression (), m)) - | SynExpr.Match (spMatch, expr, clauses, _m, _trivia) -> + | SynExpr.Match(spMatch, expr, clauses, _m, _trivia) -> let inputExpr, inputTy, tpenv = TcExprOfUnknownType cenv env tpenv expr - let tclauses, tpenv = - (tpenv, clauses) ||> List.mapFold (fun tpenv (SynMatchClause(pat, cond, innerComp, _, sp, _)) -> - let patR, condR, vspecs, envinner, tpenv = TcMatchPattern cenv inputTy env tpenv pat cond - let envinner = - match sp with - | DebugPointAtTarget.Yes -> { envinner with eIsControlFlow = true } - | DebugPointAtTarget.No -> envinner - let innerExpr, tpenv = tcSequenceExprBody envinner genOuterTy tpenv innerComp - MatchClause(patR, condR, TTarget(vspecs, innerExpr, None), patR.Range), tpenv) + let tclauses, tpenv = + (tpenv, clauses) + ||> List.mapFold (fun tpenv (SynMatchClause(pat, cond, innerComp, _, sp, _)) -> + let patR, condR, vspecs, envinner, tpenv = + TcMatchPattern cenv inputTy env tpenv pat cond + + let envinner = + match sp with + | DebugPointAtTarget.Yes -> { envinner with eIsControlFlow = true } + | DebugPointAtTarget.No -> envinner + + let innerExpr, tpenv = tcSequenceExprBody envinner genOuterTy tpenv innerComp + MatchClause(patR, condR, TTarget(vspecs, innerExpr, None), patR.Range), tpenv) let inputExprTy = tyOfExpr cenv.g inputExpr let inputExprMark = inputExpr.Range - let matchv, matchExpr = CompilePatternForMatchClauses cenv env inputExprMark inputExprMark true ThrowIncompleteMatchException (Some inputExpr) inputExprTy genOuterTy tclauses + + let matchv, matchExpr = + CompilePatternForMatchClauses + cenv + env + inputExprMark + inputExprMark + true + ThrowIncompleteMatchException + (Some inputExpr) + inputExprTy + genOuterTy + tclauses Some(mkLet spMatch inputExprMark matchv inputExpr matchExpr, tpenv) - | SynExpr.TryWith (innerTry,withList,mTryToWith,_spTry,_spWith,trivia) -> - if not(g.langVersion.SupportsFeature(LanguageFeature.TryWithInSeqExpression)) then - error(Error(FSComp.SR.tcTryIllegalInSequenceExpression(), mTryToWith)) + | SynExpr.TryWith(innerTry, withList, mTryToWith, _spTry, _spWith, trivia) -> + if not (g.langVersion.SupportsFeature(LanguageFeature.TryWithInSeqExpression)) then + error (Error(FSComp.SR.tcTryIllegalInSequenceExpression (), mTryToWith)) let env = { env with eIsControlFlow = true } - let tryExpr, tpenv = - let inner,tpenv = tcSequenceExprBody env genOuterTy tpenv innerTry + + let tryExpr, tpenv = + let inner, tpenv = tcSequenceExprBody env genOuterTy tpenv innerTry mkSeqDelayedExpr mTryToWith inner, tpenv // Compile the pattern twice, once as a filter with all succeeding targets returning "1", and once as a proper catch block. - let clauses, tpenv = - (tpenv, withList) ||> List.mapFold (fun tpenv (SynMatchClause(pat, cond, innerComp, m, sp, _)) -> - let patR, condR, vspecs, envinner, tpenv = TcMatchPattern cenv g.exn_ty env tpenv pat cond + let clauses, tpenv = + (tpenv, withList) + ||> List.mapFold (fun tpenv (SynMatchClause(pat, cond, innerComp, m, sp, _)) -> + let patR, condR, vspecs, envinner, tpenv = + TcMatchPattern cenv g.exn_ty env tpenv pat cond + let envinner = match sp with | DebugPointAtTarget.Yes -> { envinner with eIsControlFlow = true } | DebugPointAtTarget.No -> envinner + let matchBody, tpenv = tcSequenceExprBody envinner genOuterTy tpenv innerComp - let handlerClause = MatchClause(patR, condR, TTarget(vspecs, matchBody, None), patR.Range) - let filterClause = MatchClause(patR, condR, TTarget([], Expr.Const(Const.Int32 1,m,g.int_ty), None), patR.Range) - (handlerClause,filterClause), tpenv) + + let handlerClause = + MatchClause(patR, condR, TTarget(vspecs, matchBody, None), patR.Range) + + let filterClause = + MatchClause(patR, condR, TTarget([], Expr.Const(Const.Int32 1, m, g.int_ty), None), patR.Range) + + (handlerClause, filterClause), tpenv) let handlers, filterClauses = List.unzip clauses let withRange = trivia.WithToEndRange - let v1, filterExpr = CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty g.int_ty filterClauses - let v2, handlerExpr = CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty genOuterTy handlers + + let v1, filterExpr = + CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty g.int_ty filterClauses + + let v2, handlerExpr = + CompilePatternForMatchClauses cenv env withRange withRange true FailFilter None g.exn_ty genOuterTy handlers let filterLambda = mkLambda filterExpr.Range v1 (filterExpr, genOuterTy) let handlerLambda = mkLambda handlerExpr.Range v2 (handlerExpr, genOuterTy) - let combinatorExpr = mkSeqTryWith cenv env mTryToWith genOuterTy tryExpr filterLambda handlerLambda - Some (combinatorExpr,tpenv) + let combinatorExpr = + mkSeqTryWith cenv env mTryToWith genOuterTy tryExpr filterLambda handlerLambda + + Some(combinatorExpr, tpenv) - | SynExpr.YieldOrReturnFrom ((isYield, _), synYieldExpr, m) -> + | SynExpr.YieldOrReturnFrom((isYield, _), synYieldExpr, m) -> let env = { env with eIsControlFlow = false } let resultExpr, genExprTy, tpenv = TcExprOfUnknownType cenv env tpenv synYieldExpr - if not isYield then errorR(Error(FSComp.SR.tcUseYieldBangForMultipleResults(), m)) + if not isYield then + errorR (Error(FSComp.SR.tcUseYieldBangForMultipleResults (), m)) AddCxTypeMustSubsumeType ContextInfo.NoContext env.DisplayEnv cenv.css m NoTrace genOuterTy genExprTy - let resultExpr = mkCoerceExpr(resultExpr, genOuterTy, m, genExprTy) + let resultExpr = mkCoerceExpr (resultExpr, genOuterTy, m, genExprTy) let resultExpr = if IsControlFlowExpression synYieldExpr then @@ -2185,11 +3324,12 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = Some(resultExpr, tpenv) - | SynExpr.YieldOrReturn ((isYield, _), synYieldExpr, m) -> + | SynExpr.YieldOrReturn((isYield, _), synYieldExpr, m) -> let env = { env with eIsControlFlow = false } let genResultTy = NewInferenceType g - if not isYield then errorR(Error(FSComp.SR.tcSeqResultsUseYield(), m)) + if not isYield then + errorR (Error(FSComp.SR.tcSeqResultsUseYield (), m)) UnifyTypes cenv env m genOuterTy (mkSeqTy cenv.g genResultTy) @@ -2203,30 +3343,34 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = else mkDebugPoint m resultExpr - Some(resultExpr, tpenv ) + Some(resultExpr, tpenv) | _ -> None - + and tcSequenceExprBody env (genOuterTy: TType) tpenv comp = - let res, tpenv = tcSequenceExprBodyAsSequenceOrStatement env genOuterTy tpenv comp - match res with - | Choice1Of2 expr -> - expr, tpenv - | Choice2Of2 stmt -> + let res, tpenv = tcSequenceExprBodyAsSequenceOrStatement env genOuterTy tpenv comp + + match res with + | Choice1Of2 expr -> expr, tpenv + | Choice2Of2 stmt -> let m = comp.Range let resExpr = Expr.Sequential(stmt, mkSeqEmpty cenv env m genOuterTy, NormalSeq, m) resExpr, tpenv and tcSequenceExprBodyAsSequenceOrStatement env genOuterTy tpenv comp = - match tryTcSequenceExprBody env genOuterTy tpenv comp with - | Some (expr, tpenv) -> Choice1Of2 expr, tpenv - | None -> + match tryTcSequenceExprBody env genOuterTy tpenv comp with + | Some(expr, tpenv) -> Choice1Of2 expr, tpenv + | None -> - let env = { env with eContextInfo = ContextInfo.SequenceExpression genOuterTy } + let env = + { env with + eContextInfo = ContextInfo.SequenceExpression genOuterTy + } if enableImplicitYield then let hasTypeUnit, expr, tpenv = TryTcStmt cenv env tpenv comp - if hasTypeUnit then + + if hasTypeUnit then Choice2Of2 expr, tpenv else let genResultTy = NewInferenceType g @@ -2235,7 +3379,10 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = let expr, tpenv = TcExprFlex cenv flex true genResultTy env tpenv comp let exprTy = tyOfExpr cenv.g expr AddCxTypeMustSubsumeType env.eContextInfo env.DisplayEnv cenv.css mExpr NoTrace genResultTy exprTy - let resExpr = mkCallSeqSingleton cenv.g mExpr genResultTy (mkCoerceExpr(expr, genResultTy, mExpr, exprTy)) + + let resExpr = + mkCallSeqSingleton cenv.g mExpr genResultTy (mkCoerceExpr (expr, genResultTy, mExpr, exprTy)) + Choice1Of2 resExpr, tpenv else let stmt, tpenv = TcStmtThatCantBeCtorBody cenv env tpenv comp @@ -2247,32 +3394,32 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp (overallTy: OverallTy) m = let TcSequenceExpressionEntry (cenv: cenv) env (overallTy: OverallTy) tpenv (hasBuilder, comp) m = match RewriteRangeExpr comp with - | Some replacementExpr -> - TcExpr cenv overallTy env tpenv replacementExpr + | Some replacementExpr -> TcExpr cenv overallTy env tpenv replacementExpr | None -> - let implicitYieldEnabled = cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield + let implicitYieldEnabled = + cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield - let validateObjectSequenceOrRecordExpression = not implicitYieldEnabled + let validateObjectSequenceOrRecordExpression = not implicitYieldEnabled - match comp with - | SynExpr.New _ -> - try - TcExprUndelayed cenv overallTy env tpenv comp |> ignore - with RecoverableException e -> - errorRecovery e m - errorR(Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm(), m)) - | SimpleSemicolonSequence cenv false _ when validateObjectSequenceOrRecordExpression -> - errorR(Error(FSComp.SR.tcInvalidObjectSequenceOrRecordExpression(), m)) - | _ -> - () - - if not hasBuilder && not cenv.g.compilingFSharpCore then - error(Error(FSComp.SR.tcInvalidSequenceExpressionSyntaxForm(), m)) - - TcSequenceExpression cenv env tpenv comp overallTy m - -let TcArrayOrListComputedExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (isArray, comp) m = + match comp with + | SynExpr.New _ -> + try + TcExprUndelayed cenv overallTy env tpenv comp |> ignore + with RecoverableException e -> + errorRecovery e m + + errorR (Error(FSComp.SR.tcInvalidObjectExpressionSyntaxForm (), m)) + | SimpleSemicolonSequence cenv false _ when validateObjectSequenceOrRecordExpression -> + errorR (Error(FSComp.SR.tcInvalidObjectSequenceOrRecordExpression (), m)) + | _ -> () + + if not hasBuilder && not cenv.g.compilingFSharpCore then + error (Error(FSComp.SR.tcInvalidSequenceExpressionSyntaxForm (), m)) + + TcSequenceExpression cenv env tpenv comp overallTy m + +let TcArrayOrListComputedExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (isArray, comp) m = let g = cenv.g // The syntax '[ n .. m ]' and '[ n .. step .. m ]' is not really part of array or list syntax. @@ -2280,7 +3427,7 @@ let TcArrayOrListComputedExpression (cenv: cenv) env (overallTy: OverallTy) tpen // // The elaborated form of '[ n .. m ]' is 'List.ofSeq (seq (op_Range n m))' and this shouldn't change match RewriteRangeExpr comp with - | Some replacementExpr -> + | Some replacementExpr -> let genCollElemTy = NewInferenceType g let genCollTy = (if isArray then mkArrayType else mkListTy) cenv.g genCollElemTy @@ -2291,87 +3438,131 @@ let TcArrayOrListComputedExpression (cenv: cenv) env (overallTy: OverallTy) tpen let expr, tpenv = TcExpr cenv (MustEqual exprTy) env tpenv replacementExpr - let expr = - if cenv.g.compilingFSharpCore then - expr - else + let expr = + if cenv.g.compilingFSharpCore then + expr + else // We add a call to 'seq ... ' to make sure sequence expression compilation gets applied to the contents of the // comprehension. But don't do this in FSharp.Core.dll since 'seq' may not yet be defined. mkCallSeq cenv.g m genCollElemTy expr - - let expr = mkCoerceExpr(expr, exprTy, expr.Range, overallTy.Commit) - let expr = - if isArray then + let expr = mkCoerceExpr (expr, exprTy, expr.Range, overallTy.Commit) + + let expr = + if isArray then mkCallSeqToArray cenv.g m genCollElemTy expr - else + else mkCallSeqToList cenv.g m genCollElemTy expr + expr, tpenv | None -> - // LanguageFeatures.ImplicitYield do not require this validation - let implicitYieldEnabled = cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield - let validateExpressionWithIfRequiresParenthesis = not implicitYieldEnabled - let acceptDeprecatedIfThenExpression = not implicitYieldEnabled + // LanguageFeatures.ImplicitYield do not require this validation + let implicitYieldEnabled = + cenv.g.langVersion.SupportsFeature LanguageFeature.ImplicitYield + + let validateExpressionWithIfRequiresParenthesis = not implicitYieldEnabled + let acceptDeprecatedIfThenExpression = not implicitYieldEnabled - match comp with - | SimpleSemicolonSequence cenv acceptDeprecatedIfThenExpression elems -> match comp with - | SimpleSemicolonSequence cenv false _ -> () - | _ when validateExpressionWithIfRequiresParenthesis -> errorR(Deprecated(FSComp.SR.tcExpressionWithIfRequiresParenthesis(), m)) - | _ -> () + | SimpleSemicolonSequence cenv acceptDeprecatedIfThenExpression elems -> + match comp with + | SimpleSemicolonSequence cenv false _ -> () + | _ when validateExpressionWithIfRequiresParenthesis -> + errorR (Deprecated(FSComp.SR.tcExpressionWithIfRequiresParenthesis (), m)) + | _ -> () - let replacementExpr = - if isArray then - // This are to improve parsing/processing speed for parser tables by converting to an array blob ASAP - let nelems = elems.Length - if nelems > 0 && List.forall (function SynExpr.Const (SynConst.UInt16 _, _) -> true | _ -> false) elems - then SynExpr.Const (SynConst.UInt16s (Array.ofList (List.map (function SynExpr.Const (SynConst.UInt16 x, _) -> x | _ -> failwith "unreachable") elems)), m) - elif nelems > 0 && List.forall (function SynExpr.Const (SynConst.Byte _, _) -> true | _ -> false) elems - then SynExpr.Const (SynConst.Bytes (Array.ofList (List.map (function SynExpr.Const (SynConst.Byte x, _) -> x | _ -> failwith "unreachable") elems), SynByteStringKind.Regular, m), m) - else SynExpr.ArrayOrList (isArray, elems, m) - else - if cenv.g.langVersion.SupportsFeature(LanguageFeature.ReallyLongLists) then - SynExpr.ArrayOrList (isArray, elems, m) - else - if elems.Length > 500 then - error(Error(FSComp.SR.tcListLiteralMaxSize(), m)) - SynExpr.ArrayOrList (isArray, elems, m) + let replacementExpr = + if isArray then + // This are to improve parsing/processing speed for parser tables by converting to an array blob ASAP + let nelems = elems.Length + + if + nelems > 0 + && List.forall + (function + | SynExpr.Const(SynConst.UInt16 _, _) -> true + | _ -> false) + elems + then + SynExpr.Const( + SynConst.UInt16s( + Array.ofList ( + List.map + (function + | SynExpr.Const(SynConst.UInt16 x, _) -> x + | _ -> failwith "unreachable") + elems + ) + ), + m + ) + elif + nelems > 0 + && List.forall + (function + | SynExpr.Const(SynConst.Byte _, _) -> true + | _ -> false) + elems + then + SynExpr.Const( + SynConst.Bytes( + Array.ofList ( + List.map + (function + | SynExpr.Const(SynConst.Byte x, _) -> x + | _ -> failwith "unreachable") + elems + ), + SynByteStringKind.Regular, + m + ), + m + ) + else + SynExpr.ArrayOrList(isArray, elems, m) + else if cenv.g.langVersion.SupportsFeature(LanguageFeature.ReallyLongLists) then + SynExpr.ArrayOrList(isArray, elems, m) + else + if elems.Length > 500 then + error (Error(FSComp.SR.tcListLiteralMaxSize (), m)) - TcExprUndelayed cenv overallTy env tpenv replacementExpr - | _ -> + SynExpr.ArrayOrList(isArray, elems, m) - let genCollElemTy = NewInferenceType g + TcExprUndelayed cenv overallTy env tpenv replacementExpr + | _ -> - let genCollTy = (if isArray then mkArrayType else mkListTy) cenv.g genCollElemTy + let genCollElemTy = NewInferenceType g - // Propagating type directed conversion, e.g. for - // let x : seq = [ yield 1; if true then yield 2 ] - TcPropagatingExprLeafThenConvert cenv overallTy genCollTy env (* canAdhoc *) m (fun () -> - - let exprTy = mkSeqTy cenv.g genCollElemTy + let genCollTy = (if isArray then mkArrayType else mkListTy) cenv.g genCollElemTy - // Check the comprehension - let expr, tpenv = TcSequenceExpression cenv env tpenv comp (MustEqual exprTy) m + // Propagating type directed conversion, e.g. for + // let x : seq = [ yield 1; if true then yield 2 ] + TcPropagatingExprLeafThenConvert cenv overallTy genCollTy env (* canAdhoc *) m (fun () -> - let expr = mkCoerceIfNeeded cenv.g exprTy (tyOfExpr cenv.g expr) expr + let exprTy = mkSeqTy cenv.g genCollElemTy - let expr = - if cenv.g.compilingFSharpCore then - //warning(Error(FSComp.SR.fslibUsingComputedListOrArray(), expr.Range)) - expr - else - // We add a call to 'seq ... ' to make sure sequence expression compilation gets applied to the contents of the - // comprehension. But don't do this in FSharp.Core.dll since 'seq' may not yet be defined. - mkCallSeq cenv.g m genCollElemTy expr - - let expr = mkCoerceExpr(expr, exprTy, expr.Range, overallTy.Commit) + // Check the comprehension + let expr, tpenv = TcSequenceExpression cenv env tpenv comp (MustEqual exprTy) m - let expr = - if isArray then - mkCallSeqToArray cenv.g m genCollElemTy expr - else - mkCallSeqToList cenv.g m genCollElemTy expr - - expr, tpenv) + let expr = mkCoerceIfNeeded cenv.g exprTy (tyOfExpr cenv.g expr) expr + + let expr = + if cenv.g.compilingFSharpCore then + //warning(Error(FSComp.SR.fslibUsingComputedListOrArray(), expr.Range)) + expr + else + // We add a call to 'seq ... ' to make sure sequence expression compilation gets applied to the contents of the + // comprehension. But don't do this in FSharp.Core.dll since 'seq' may not yet be defined. + mkCallSeq cenv.g m genCollElemTy expr + + let expr = mkCoerceExpr (expr, exprTy, expr.Range, overallTy.Commit) + + let expr = + if isArray then + mkCallSeqToArray cenv.g m genCollElemTy expr + else + mkCallSeqToList cenv.g m genCollElemTy expr + + expr, tpenv) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index ef9a56204a0..c5e21e744cd 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -866,7 +866,8 @@ module AddAugmentationDeclarations = let ShouldAugmentUnion (g: TcGlobals) (tycon: Tycon) = g.langVersion.SupportsFeature LanguageFeature.UnionIsPropertiesVisible && - HasDefaultAugmentationAttribute g (mkLocalTyconRef tycon) + HasDefaultAugmentationAttribute g (mkLocalTyconRef tycon) && + tycon.UnionCasesArray.Length > 1 let AddUnionAugmentationValues (cenv: cenv) (env: TcEnv) tycon = let tcref = mkLocalTyconRef tycon @@ -1039,7 +1040,7 @@ module MutRecBindingChecking = let innerState = (None, envForTycon, tpenv, recBindIdx, uncheckedBindsRev) [Phase2AIncrClassCtor (staticCtorInfo, None)], innerState - | Some (SynMemberDefn.ImplicitCtor (vis, Attributes attrs, SynSimplePats.SimplePats(pats=spats), thisIdOpt, xmlDoc, m,_)), ContainerInfo(_, Some memberContainerInfo) -> + | Some (SynMemberDefn.ImplicitCtor (vis, Attributes attrs, pat, thisIdOpt, xmlDoc, m,_)), ContainerInfo(_, Some memberContainerInfo) -> let (MemberOrValContainerInfo(tcref, _, baseValOpt, safeInitInfo, _)) = memberContainerInfo @@ -1050,7 +1051,7 @@ module MutRecBindingChecking = let staticCtorInfo = TcStaticImplicitCtorInfo_Phase2A(cenv, envForTycon, tcref, m, copyOfTyconTypars) // Phase2A: make incrCtorInfo - ctorv, thisVal etc, type depends on argty(s) - let incrCtorInfo = TcImplicitCtorInfo_Phase2A(cenv, envForTycon, tpenv, tcref, vis, attrs, spats, thisIdOpt, baseValOpt, safeInitInfo, m, copyOfTyconTypars, objTy, thisTy, xmlDoc) + let incrCtorInfo = TcImplicitCtorInfo_Phase2A(cenv, envForTycon, tpenv, tcref, vis, attrs, pat, thisIdOpt, baseValOpt, safeInitInfo, m, copyOfTyconTypars, objTy, thisTy, xmlDoc) // Phase2A: Add copyOfTyconTypars from incrCtorInfo - or from tcref let envForTycon = AddDeclaredTypars CheckForDuplicateTypars staticCtorInfo.IncrCtorDeclaredTypars envForTycon @@ -1926,9 +1927,10 @@ module MutRecBindingChecking = let private ReportErrorOnStaticClass (synMembers: SynMemberDefn list) = for mem in synMembers do match mem with - | SynMemberDefn.ImplicitCtor(ctorArgs = SynSimplePats.SimplePats(pats = pats)) when (not pats.IsEmpty) -> - for pat in pats do - warning(Error(FSComp.SR.chkConstructorWithArgumentsOnStaticClasses(), pat.Range)) + | SynMemberDefn.ImplicitCtor(ctorArgs = pat) -> + match pat with + | SynPat.Paren(innerPat, _) -> warning(Error(FSComp.SR.chkConstructorWithArgumentsOnStaticClasses(), innerPat.Range)) + | _ -> () | SynMemberDefn.Member(SynBinding(valData = SynValData(memberFlags = Some memberFlags)), m) when memberFlags.MemberKind = SynMemberKind.Constructor -> warning(Error(FSComp.SR.chkAdditionalConstructorOnStaticClasses(), m)) | SynMemberDefn.Member(SynBinding(valData = SynValData(memberFlags = Some memberFlags)), m) when memberFlags.IsInstance -> @@ -2623,8 +2625,8 @@ module EstablishTypeDefinitionCores = match implicitCtorSynPats with | None -> () - | Some spats -> - let ctorArgNames, patEnv = TcSimplePatsOfUnknownType cenv true NoCheckCxs env tpenv spats + | Some pat -> + let ctorArgNames, patEnv, _ = TcSimplePatsOfUnknownType cenv true NoCheckCxs env tpenv pat let (TcPatLinearEnv(_, names, _)) = patEnv @@ -2790,19 +2792,24 @@ module EstablishTypeDefinitionCores = match synTyconRepr with | SynTypeDefnSimpleRepr.General (SynTypeDefnKind.Delegate (_ty, arity), _, _, _, _, _, _, _) -> arity.ArgNames | SynTypeDefnSimpleRepr.General (SynTypeDefnKind.Unspecified, _, _, _, _, _, Some synPats, _) -> - let rec patName (p: SynSimplePat) = + let rec patName (p: SynPat) = match p with - | SynSimplePat.Id (id, _, _, _, _, _) -> id.idText - | SynSimplePat.Typed(pat, _, _) -> patName pat - | SynSimplePat.Attrib(pat, _, _) -> patName pat + | SynPat.Named(ident = (SynIdent(id, _))) -> Some id.idText + | SynPat.Typed(pat = pat) + | SynPat.Attrib(pat = pat) -> patName pat + | _ -> None - let rec pats (p: SynSimplePats) = - match p with - | SynSimplePats.SimplePats (pats = ps) -> ps + let getSimplePats (pat: SynPat) = + match pat with + | SynPat.Paren(pat, _) -> + match pat with + | SynPat.Tuple(false, pats, _, _) -> pats + | pat -> [pat] + | _ -> [] let patNames = - pats synPats - |> List.map patName + getSimplePats synPats + |> List.choose patName patNames | _ -> [] @@ -2935,7 +2942,7 @@ module EstablishTypeDefinitionCores = | Some (tc, args, m) -> let ad = envinner.AccessRights match ResolveTypeLongIdent cenv.tcSink cenv.nameResolver ItemOccurence.UseInType OpenQualified envinner.NameEnv ad tc TypeNameResolutionStaticArgsInfo.DefiniteEmpty PermitDirectReferenceToGeneratedType.Yes with - | Result (_, tcrefBeforeStaticArguments) when + | Result (_, tcrefBeforeStaticArguments, _) when tcrefBeforeStaticArguments.IsProvided && not tcrefBeforeStaticArguments.IsErased -> @@ -3378,14 +3385,13 @@ module EstablishTypeDefinitionCores = | rf :: _ -> errorR (Error(FSComp.SR.tcInterfaceTypesAndDelegatesCannotContainFields(), rf.Range)) | _ -> () - let primaryConstructorInDelegateCheck(implicitCtorSynPats : SynSimplePats option) = + let primaryConstructorInDelegateCheck(implicitCtorSynPats : SynPat option) = match implicitCtorSynPats with | None -> () - | Some spats -> - let ctorArgNames, _ = TcSimplePatsOfUnknownType cenv true CheckCxs envinner tpenv spats + | Some pat -> + let ctorArgNames, _, _ = TcSimplePatsOfUnknownType cenv true CheckCxs envinner tpenv pat if not ctorArgNames.IsEmpty then - match spats with - | SynSimplePats.SimplePats(range = m) -> errorR (Error(FSComp.SR.parsOnlyClassCanTakeValueArguments(), m)) + errorR (Error(FSComp.SR.parsOnlyClassCanTakeValueArguments(), pat.Range)) let envinner = AddDeclaredTypars CheckForDuplicateTypars (tycon.Typars m) envinner let envinner = MakeInnerEnvForTyconRef envinner thisTyconRef false @@ -3539,9 +3545,9 @@ module EstablishTypeDefinitionCores = match implicitCtorSynPats with | None -> () - | Some spats -> - if tycon.IsFSharpStructOrEnumTycon then - let ctorArgNames, patEnv = TcSimplePatsOfUnknownType cenv true CheckCxs envinner tpenv spats + | Some pat -> + if tycon.IsFSharpStructOrEnumTycon then + let ctorArgNames, patEnv, _ = TcSimplePatsOfUnknownType cenv true CheckCxs envinner tpenv pat let (TcPatLinearEnv(_, names, _)) = patEnv @@ -4112,11 +4118,11 @@ module TcDeclarations = | _ -> let resInfo = TypeNameResolutionStaticArgsInfo.FromTyArgs synTypars.Length - let _, tcref = + let tcref = match ResolveTypeLongIdent cenv.tcSink cenv.nameResolver ItemOccurence.Binding OpenQualified envForDecls.NameEnv ad longPath resInfo PermitDirectReferenceToGeneratedType.No with | Result res -> // Update resolved type parameters with the names from the source. - let _, tcref = res + let _, tcref, _ = res if tcref.TyparsNoRange.Length = synTypars.Length then (tcref.TyparsNoRange, synTypars) ||> List.zip @@ -4126,11 +4132,12 @@ module TcDeclarations = typar.SetIdent(untypedIdent) ) - res - | res when inSig && List.isSingleton longPath -> - errorR(Deprecated(FSComp.SR.tcReservedSyntaxForAugmentation(), m)) - ForceRaise res - | res -> ForceRaise res + tcref + + | Exception exn -> + if inSig && List.isSingleton longPath then + errorR(Deprecated(FSComp.SR.tcReservedSyntaxForAugmentation(), m)) + ForceRaise (Exception exn) tcref let isInterfaceOrDelegateOrEnum = @@ -4441,7 +4448,7 @@ module TcDeclarations = let implicitCtorSynPats = members |> List.tryPick (function - | SynMemberDefn.ImplicitCtor (ctorArgs = SynSimplePats.SimplePats _ as spats) -> Some spats + | SynMemberDefn.ImplicitCtor (ctorArgs = pat) -> Some pat | _ -> None) // An ugly bit of code to pre-determine if a type has a nullary constructor, prior to establishing the @@ -4450,7 +4457,7 @@ module TcDeclarations = members |> List.exists (function | SynMemberDefn.Member(memberDefn=SynBinding(valData=SynValData(memberFlags=Some memberFlags); headPat = SynPatForConstructorDecl SynPatForNullaryArgs)) -> memberFlags.MemberKind=SynMemberKind.Constructor - | SynMemberDefn.ImplicitCtor (ctorArgs = SynSimplePats.SimplePats(pats = spats)) -> isNil spats + | SynMemberDefn.ImplicitCtor (ctorArgs = SynPat.Const(SynConst.Unit, _)) -> true | _ -> false) let repr = SynTypeDefnSimpleRepr.General(kind, inherits, slotsigs, fields, isConcrete, isIncrClass, implicitCtorSynPats, m) let isAtOriginalTyconDefn = not (isAugmentationTyconDefnRepr repr) @@ -5590,7 +5597,7 @@ let SolveInternalUnknowns g (cenv: cenv) denvAtEnd moduleContents extraAttribs = if (tp.Rigidity <> TyparRigidity.Rigid) && not tp.IsSolved then ChooseTyparSolutionAndSolve cenv.css denvAtEnd tp -let CheckModuleSignature g (cenv: cenv) m denvAtEnd rootSigOpt implFileTypePriorToSig implFileSpecPriorToSig moduleContents = +let CheckModuleSignature g (cenv: cenv) m denvAtEnd rootSigOpt implFileTypePriorToSig implFileSpecPriorToSig moduleContents fileName qualifiedNameOfFile = match rootSigOpt with | None -> // Deep copy the inferred type of the module @@ -5598,7 +5605,13 @@ let CheckModuleSignature g (cenv: cenv) m denvAtEnd rootSigOpt implFileTypePrior (implFileTypePriorToSigCopied, moduleContents) - | Some sigFileType -> + | Some sigFileType -> + use _ = + Activity.start "CheckDeclarations.CheckModuleSignature" + [| + Activity.Tags.fileName, fileName + Activity.Tags.qualifiedNameOfFile, qualifiedNameOfFile + |] // We want to show imperative type variables in any types in error messages at this late point let denv = { denvAtEnd with showInferenceTyparAnnotations=true } @@ -5724,7 +5737,7 @@ let CheckOneImplFile // Check the module matches the signature let implFileTy, implFileContents = conditionallySuppressErrorReporting (checkForErrors()) (fun () -> - CheckModuleSignature g cenv m denvAtEnd rootSigOpt implFileTypePriorToSig implFileSpecPriorToSig moduleContents) + CheckModuleSignature g cenv m denvAtEnd rootSigOpt implFileTypePriorToSig implFileSpecPriorToSig moduleContents fileName qualNameOfFile.Text) do conditionallySuppressErrorReporting (checkForErrors()) (fun () -> @@ -5744,6 +5757,12 @@ let CheckOneImplFile try let reportErrors = not (checkForErrors()) let tcVal = LightweightTcValForUsingInBuildMethodCall g + use _ = + Activity.start "PostTypeCheckSemanticChecks.CheckImplFile" + [| + Activity.Tags.fileName, fileName + Activity.Tags.qualifiedNameOfFile, qualNameOfFile.Text + |] PostTypeCheckSemanticChecks.CheckImplFile (g, cenv.amap, reportErrors, cenv.infoReader, env.eInternalsVisibleCompPaths, cenv.thisCcu, tcVal, envAtEnd.DisplayEnv, diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index f182c98ac85..e59fb6bd5ff 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -767,7 +767,7 @@ let TcConst (cenv: cenv) (overallTy: TType) m env synConst = | SynMeasure.One _ -> Measure.One | SynMeasure.Named(tc, m) -> let ad = env.eAccessRights - let _, tcref = ForceRaise(ResolveTypeLongIdent cenv.tcSink cenv.nameResolver ItemOccurence.Use OpenQualified env.eNameResEnv ad tc TypeNameResolutionStaticArgsInfo.DefiniteEmpty PermitDirectReferenceToGeneratedType.No) + let _, tcref, _ = ForceRaise(ResolveTypeLongIdent cenv.tcSink cenv.nameResolver ItemOccurence.Use OpenQualified env.eNameResEnv ad tc TypeNameResolutionStaticArgsInfo.DefiniteEmpty PermitDirectReferenceToGeneratedType.No) match tcref.TypeOrMeasureKind with | TyparKind.Type -> error(Error(FSComp.SR.tcExpectedUnitOfMeasureNotType(), m)) | TyparKind.Measure -> Measure.Const tcref @@ -1895,7 +1895,7 @@ let BuildFieldMap (cenv: cenv) env isPartial ty (flds: ((Ident list * Ident) * ' | _ -> error(Error(FSComp.SR.tcRecordFieldInconsistentTypes(), m))) Some(tinst, tcref, fldsmap, List.rev rfldsList) -let rec ApplyUnionCaseOrExn (makerForUnionCase, makerForExnTag) m (cenv: cenv) env overallTy item = +let ApplyUnionCaseOrExn (makerForUnionCase, makerForExnTag) m (cenv: cenv) env overallTy item = let g = cenv.g let ad = env.eAccessRights match item with @@ -3115,15 +3115,17 @@ let BuildRecdFieldSet g m objExpr (rfinfo: RecdFieldInfo) argExpr = // Helpers dealing with named and optional args at callsites //------------------------------------------------------------------------- +[] let (|BinOpExpr|_|) expr = match expr with - | SynExpr.App (_, _, SynExpr.App (_, _, SingleIdent opId, a, _), b, _) -> Some (opId, a, b) - | _ -> None + | SynExpr.App (_, _, SynExpr.App (_, _, SingleIdent opId, a, _), b, _) -> ValueSome (opId, a, b) + | _ -> ValueNone +[] let (|SimpleEqualsExpr|_|) expr = match expr with - | BinOpExpr(opId, a, b) when opId.idText = opNameEquals -> Some (a, b) - | _ -> None + | BinOpExpr(opId, a, b) when opId.idText = opNameEquals -> ValueSome (a, b) + | _ -> ValueNone /// Detect a named argument at a callsite let TryGetNamedArg expr = @@ -4458,7 +4460,7 @@ and TcLongIdentType kindOpt (cenv: cenv) newOk checkConstraints occ iwsam env tp let m = synLongId.Range let ad = env.eAccessRights - let tinstEnclosing, tcref = ForceRaise(ResolveTypeLongIdent cenv.tcSink cenv.nameResolver occ OpenQualified env.NameEnv ad tc TypeNameResolutionStaticArgsInfo.DefiniteEmpty PermitDirectReferenceToGeneratedType.No) + let tinstEnclosing, tcref, inst = ForceRaise(ResolveTypeLongIdent cenv.tcSink cenv.nameResolver occ OpenQualified env.NameEnv ad tc TypeNameResolutionStaticArgsInfo.DefiniteEmpty PermitDirectReferenceToGeneratedType.No) CheckIWSAM cenv env checkConstraints iwsam m tcref @@ -4472,7 +4474,7 @@ and TcLongIdentType kindOpt (cenv: cenv) newOk checkConstraints occ iwsam env tp | _, TyparKind.Measure -> TType_measure (Measure.Const tcref), tpenv | _, TyparKind.Type -> - TcTypeApp cenv newOk checkConstraints occ env tpenv m tcref tinstEnclosing [] + TcTypeApp cenv newOk checkConstraints occ env tpenv m tcref tinstEnclosing [] inst /// Some.Long.TypeName /// ty1 SomeLongTypeName @@ -4480,7 +4482,7 @@ and TcLongIdentAppType kindOpt (cenv: cenv) newOk checkConstraints occ iwsam env let (SynLongIdent(tc, _, _)) = longId let ad = env.eAccessRights - let tinstEnclosing, tcref = + let tinstEnclosing, tcref, inst = let tyResInfo = TypeNameResolutionStaticArgsInfo.FromTyArgs args.Length ResolveTypeLongIdent cenv.tcSink cenv.nameResolver ItemOccurence.UseInType OpenQualified env.eNameResEnv ad tc tyResInfo PermitDirectReferenceToGeneratedType.No |> ForceRaise @@ -4499,7 +4501,7 @@ and TcLongIdentAppType kindOpt (cenv: cenv) newOk checkConstraints occ iwsam env | _, TyparKind.Type -> if postfix && tcref.Typars m |> List.exists (fun tp -> match tp.Kind with TyparKind.Measure -> true | _ -> false) then error(Error(FSComp.SR.tcInvalidUnitsOfMeasurePrefix(), m)) - TcTypeApp cenv newOk checkConstraints occ env tpenv m tcref tinstEnclosing args + TcTypeApp cenv newOk checkConstraints occ env tpenv m tcref tinstEnclosing args inst | _, TyparKind.Measure -> match args, postfix with @@ -4518,8 +4520,8 @@ and TcNestedAppType (cenv: cenv) newOk checkConstraints occ iwsam env tpenv synL let leftTy, tpenv = TcType cenv newOk checkConstraints occ iwsam env tpenv synLeftTy match leftTy with | AppTy g (tcref, tinst) -> - let tcref = ResolveTypeLongIdentInTyconRef cenv.tcSink cenv.nameResolver env.eNameResEnv (TypeNameResolutionInfo.ResolveToTypeRefs (TypeNameResolutionStaticArgsInfo.FromTyArgs args.Length)) ad m tcref longId - TcTypeApp cenv newOk checkConstraints occ env tpenv m tcref tinst args + let tcref, inst = ResolveTypeLongIdentInTyconRef cenv.tcSink cenv.nameResolver env.eNameResEnv (TypeNameResolutionInfo.ResolveToTypeRefs (TypeNameResolutionStaticArgsInfo.FromTyArgs args.Length)) ad m tcref longId + TcTypeApp cenv newOk checkConstraints occ env tpenv m tcref tinst args inst | _ -> error(Error(FSComp.SR.tcTypeHasNoNestedTypes(), m)) @@ -4943,7 +4945,7 @@ and TcProvidedTypeApp (cenv: cenv) env tpenv tcref args m = /// Note that the generic type may be a nested generic type List.ListEnumerator. /// In this case, 'argsR is only the instantiation of the suffix type arguments, and pathTypeArgs gives /// the prefix of type arguments. -and TcTypeApp (cenv: cenv) newOk checkConstraints occ env tpenv m tcref pathTypeArgs (synArgTys: SynType list) = +and TcTypeApp (cenv: cenv) newOk checkConstraints occ env tpenv m tcref pathTypeArgs (synArgTys: SynType list) (tinst: TypeInst) = let g = cenv.g CheckTyconAccessible cenv.amap m env.AccessRights tcref |> ignore CheckEntityAttributes g tcref m |> CommitOperationResult @@ -4954,16 +4956,22 @@ and TcTypeApp (cenv: cenv) newOk checkConstraints occ env tpenv m tcref pathType if tcref.Deref.IsProvided then TcProvidedTypeApp cenv env tpenv tcref synArgTys m else #endif - let tps, _, tinst, _ = FreshenTyconRef2 g m tcref - - // If we're not checking constraints, i.e. when we first assert the super/interfaces of a type definition, then just - // clear the constraint lists of the freshly generated type variables. A little ugly but fairly localized. - if checkConstraints = NoCheckCxs then tps |> List.iter (fun tp -> tp.SetConstraints []) let synArgTysLength = synArgTys.Length let pathTypeArgsLength = pathTypeArgs.Length if tinst.Length <> pathTypeArgsLength + synArgTysLength then error (TyconBadArgs(env.DisplayEnv, tcref, pathTypeArgsLength + synArgTysLength, m)) + let tps = tinst |> List.skip pathTypeArgsLength |> List.map (fun t -> + match t with + | TType_var(typar, _) + | TType_measure(Measure.Var typar) -> typar + | t -> failwith $"TcTypeApp: {t}" + ) + + // If we're not checking constraints, i.e. when we first assert the super/interfaces of a type definition, then just + // clear the constraint lists of the freshly generated type variables. A little ugly but fairly localized. + if checkConstraints = NoCheckCxs then tps |> List.iter (fun tp -> tp.SetConstraints []) + let argTys, tpenv = // Get the suffix of typars let tpsForArgs = List.skip (tps.Length - synArgTysLength) tps @@ -5009,9 +5017,9 @@ and TcNestedTypeApplication (cenv: cenv) newOk checkConstraints occ iwsam env tp error(Error(FSComp.SR.tcTypeHasNoNestedTypes(), mWholeTypeApp)) match ty with - | TType_app(tcref, _, _) -> + | TType_app(tcref, inst, _) -> CheckIWSAM cenv env checkConstraints iwsam mWholeTypeApp tcref - TcTypeApp cenv newOk checkConstraints occ env tpenv mWholeTypeApp tcref pathTypeArgs tyargs + TcTypeApp cenv newOk checkConstraints occ env tpenv mWholeTypeApp tcref pathTypeArgs tyargs inst | _ -> error(InternalError("TcNestedTypeApplication: expected type application", mWholeTypeApp)) @@ -5085,7 +5093,11 @@ and TcPatLongIdentActivePatternCase warnOnUpper (cenv: cenv) (env: TcEnv) vFlags if dtys.Length = args.Length + 1 && ((isOptionTy g retTy && isUnitTy g (destOptionTy g retTy)) || - (isValueOptionTy g retTy && isUnitTy g (destValueOptionTy g retTy))) then + (isValueOptionTy g retTy && isUnitTy g (destValueOptionTy g retTy))) || + // `bool` partial AP always be treated as `unit option` + // For `val (|P|_|) : _ -> bool`, only allow `match x with | P -> ...` + // For `val (|P|_|) : _ -> _ -> bool`, only allow `match x with | P parameter -> ...` + (not apinfo.IsTotal && isBoolTy g retTy) then args, SynPat.Const(SynConst.Unit, m) else List.frontAndBack args @@ -8163,10 +8175,10 @@ and TcNameOfExpr (cenv: cenv) env tpenv (synArg: SynExpr) = if (match delayed with [DelayedTypeApp _] | [] -> true | _ -> false) then let (TypeNameResolutionInfo(_, staticArgsInfo)) = GetLongIdentTypeNameInfo delayed match ResolveTypeLongIdent cenv.tcSink cenv.nameResolver ItemOccurence.UseInAttribute OpenQualified env.eNameResEnv ad longId staticArgsInfo PermitDirectReferenceToGeneratedType.No with - | Result (tinstEnclosing, tcref) when IsEntityAccessible cenv.amap m ad tcref -> + | Result (tinstEnclosing, tcref, inst) when IsEntityAccessible cenv.amap m ad tcref -> match delayed with | [DelayedTypeApp (tyargs, _, mExprAndTypeArgs)] -> - TcTypeApp cenv NewTyparsOK CheckCxs ItemOccurence.UseInType env tpenv mExprAndTypeArgs tcref tinstEnclosing tyargs |> ignore + TcTypeApp cenv NewTyparsOK CheckCxs ItemOccurence.UseInType env tpenv mExprAndTypeArgs tcref tinstEnclosing tyargs inst |> ignore | _ -> () true // resolved to a type name, done with checks | _ -> @@ -10744,14 +10756,25 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt | Some (apinfo, apOverallTy, _) -> let activePatResTys = NewInferenceTypes g apinfo.ActiveTags let _, apReturnTy = stripFunTy g apOverallTy - - if isStructRetTy && apinfo.IsTotal then - errorR(Error(FSComp.SR.tcInvalidStructReturn(), mBinding)) - - if isStructRetTy then + let apRetTy = + if apinfo.IsTotal then + if isStructRetTy then errorR(Error(FSComp.SR.tcInvalidStructReturn(), mBinding)) + ActivePatternReturnKind.RefTypeWrapper + else + if isStructRetTy || isValueOptionTy cenv.g apReturnTy then ActivePatternReturnKind.StructTypeWrapper + elif isBoolTy cenv.g apReturnTy then ActivePatternReturnKind.Boolean + else ActivePatternReturnKind.RefTypeWrapper + + match apRetTy with + | ActivePatternReturnKind.Boolean -> + checkLanguageFeatureError g.langVersion LanguageFeature.BooleanReturningAndReturnTypeDirectedPartialActivePattern mBinding + | ActivePatternReturnKind.StructTypeWrapper when not isStructRetTy -> + checkLanguageFeatureError g.langVersion LanguageFeature.BooleanReturningAndReturnTypeDirectedPartialActivePattern mBinding + | ActivePatternReturnKind.StructTypeWrapper -> checkLanguageFeatureError g.langVersion LanguageFeature.StructActivePattern mBinding + | ActivePatternReturnKind.RefTypeWrapper -> () - UnifyTypes cenv env mBinding (apinfo.ResultType g rhsExpr.Range activePatResTys isStructRetTy) apReturnTy + UnifyTypes cenv env mBinding (apinfo.ResultType g rhsExpr.Range activePatResTys apRetTy) apReturnTy | None -> if isStructRetTy then @@ -10858,7 +10881,7 @@ and TcAttributeEx canFail (cenv: cenv) (env: TcEnv) attrTgt attrEx (synAttr: Syn match ResolveTypeLongIdent cenv.tcSink cenv.nameResolver ItemOccurence.UseInAttribute OpenQualified env.eNameResEnv ad tycon TypeNameResolutionStaticArgsInfo.DefiniteEmpty PermitDirectReferenceToGeneratedType.No with | Exception err -> raze err - | Result(tinstEnclosing, tcref) -> success(TcTypeApp cenv NoNewTypars CheckCxs ItemOccurence.UseInAttribute env tpenv mAttr tcref tinstEnclosing []) + | Result(tinstEnclosing, tcref, inst) -> success(TcTypeApp cenv NoNewTypars CheckCxs ItemOccurence.UseInAttribute env tpenv mAttr tcref tinstEnclosing [] inst) ForceRaise ((try1 (tyid.idText + "Attribute")) |> otherwise (fun () -> (try1 tyid.idText))) diff --git a/src/Compiler/Checking/CheckExpressions.fsi b/src/Compiler/Checking/CheckExpressions.fsi index 3f1e8d78b97..852fee213e6 100644 --- a/src/Compiler/Checking/CheckExpressions.fsi +++ b/src/Compiler/Checking/CheckExpressions.fsi @@ -712,7 +712,8 @@ val TcMatchPattern: synWhenExprOpt: SynExpr option -> Pattern * Expr option * Val list * TcEnv * UnscopedTyparEnv -val (|BinOpExpr|_|): SynExpr -> (Ident * SynExpr * SynExpr) option +[] +val (|BinOpExpr|_|): SynExpr -> (Ident * SynExpr * SynExpr) voption /// Check a set of let bindings in a class or module val TcLetBindings: diff --git a/src/Compiler/Checking/CheckIncrementalClasses.fs b/src/Compiler/Checking/CheckIncrementalClasses.fs index 8ef4d3734f9..85262e72a4c 100644 --- a/src/Compiler/Checking/CheckIncrementalClasses.fs +++ b/src/Compiler/Checking/CheckIncrementalClasses.fs @@ -123,7 +123,7 @@ let TcStaticImplicitCtorInfo_Phase2A(cenv: cenv, env, tcref: TyconRef, m, copyOf /// Check and elaborate the "left hand side" of the implicit class construction /// syntax. -let TcImplicitCtorInfo_Phase2A(cenv: cenv, env, tpenv, tcref: TyconRef, vis, attrs, spats, thisIdOpt, baseValOpt: Val option, safeInitInfo, m, copyOfTyconTypars, objTy, thisTy, xmlDoc: PreXmlDoc) = +let TcImplicitCtorInfo_Phase2A(cenv: cenv, env, tpenv, tcref: TyconRef, vis, attrs, pat: SynPat, thisIdOpt, baseValOpt: Val option, safeInitInfo, m, copyOfTyconTypars, objTy, thisTy, xmlDoc: PreXmlDoc) = let g = cenv.g let baseValOpt = @@ -135,16 +135,30 @@ let TcImplicitCtorInfo_Phase2A(cenv: cenv, env, tpenv, tcref: TyconRef, vis, att let env = AddDeclaredTypars CheckForDuplicateTypars copyOfTyconTypars env // Type check arguments by processing them as 'simple' patterns - // NOTE: if we allow richer patterns here this is where we'd process those patterns - let ctorArgNames, patEnv = TcSimplePatsOfUnknownType cenv true CheckCxs env tpenv (SynSimplePats.SimplePats (spats, [], m)) + let ctorArgNames, patEnv, SynSimplePats.SimplePats(spats, _, _) = TcSimplePatsOfUnknownType cenv true CheckCxs env tpenv pat + + let rec reportGeneratedPattern spat = + match spat with + | SynSimplePat.Id(_, _, isCompilerGenerated, _, _, m) -> + if isCompilerGenerated then + errorR (Error(FSComp.SR.parsOnlySimplePatternsAreAllowedInConstructors(), m)) + + | SynSimplePat.Typed(pat, _, _) + | SynSimplePat.Attrib(pat, _, _) -> + reportGeneratedPattern pat + + for spat in spats do + reportGeneratedPattern spat let (TcPatLinearEnv(_, names, _)) = patEnv // Create the values with the given names let _, vspecs = MakeAndPublishSimpleVals cenv env names - if tcref.IsStructOrEnumTycon && isNil spats then + match tcref.IsStructOrEnumTycon, pat with + | true, SynPat.Const(SynConst.Unit, _) -> errorR (ParameterlessStructCtor(tcref.Range)) + | _ -> () // Put them in order let ctorArgs = List.map (fun v -> NameMap.find v vspecs) ctorArgNames diff --git a/src/Compiler/Checking/CheckIncrementalClasses.fsi b/src/Compiler/Checking/CheckIncrementalClasses.fsi index 0de56111ff9..428ae98b632 100644 --- a/src/Compiler/Checking/CheckIncrementalClasses.fsi +++ b/src/Compiler/Checking/CheckIncrementalClasses.fsi @@ -130,7 +130,7 @@ val TcImplicitCtorInfo_Phase2A: tcref: TyconRef * vis: SynAccess option * attrs: SynAttribute list * - spats: SynSimplePat list * + pat: SynPat * thisIdOpt: Ident option * baseValOpt: Val option * safeInitInfo: SafeInitData * diff --git a/src/Compiler/Checking/CheckPatterns.fs b/src/Compiler/Checking/CheckPatterns.fs index 00229e5eb4b..896df82f844 100644 --- a/src/Compiler/Checking/CheckPatterns.fs +++ b/src/Compiler/Checking/CheckPatterns.fs @@ -171,11 +171,13 @@ and TcSimplePats (cenv: cenv) optionalArgsOK checkConstraints ty env patEnv synS let ps', patEnvR = (patEnv, List.zip ptys ps) ||> List.mapFold (fun patEnv (ty, pat) -> TcSimplePat optionalArgsOK checkConstraints cenv ty env patEnv pat) ps', patEnvR -and TcSimplePatsOfUnknownType (cenv: cenv) optionalArgsOK checkConstraints env tpenv synSimplePats = +and TcSimplePatsOfUnknownType (cenv: cenv) optionalArgsOK checkConstraints env tpenv (pat: SynPat) = let g = cenv.g let argTy = NewInferenceType g let patEnv = TcPatLinearEnv (tpenv, NameMap.empty, Set.empty) - TcSimplePats cenv optionalArgsOK checkConstraints argTy env patEnv synSimplePats + let spats, _ = SimplePatsOfPat cenv.synArgNameGenerator pat + let names, patEnv = TcSimplePats cenv optionalArgsOK checkConstraints argTy env patEnv spats + names, patEnv, spats and TcPatBindingName cenv env id ty isMemberThis vis1 valReprInfo (vFlags: TcPatValFlags) (names, takenNames: Set) = let (TcPatValFlags(inlineFlag, declaredTypars, argAttribs, isMutable, vis2, isCompGen)) = vFlags diff --git a/src/Compiler/Checking/CheckPatterns.fsi b/src/Compiler/Checking/CheckPatterns.fsi index 46e400b8a92..da797b35a88 100644 --- a/src/Compiler/Checking/CheckPatterns.fsi +++ b/src/Compiler/Checking/CheckPatterns.fsi @@ -15,8 +15,8 @@ val TcSimplePatsOfUnknownType: checkConstraints: CheckConstraints -> env: TcEnv -> tpenv: UnscopedTyparEnv -> - synSimplePats: SynSimplePats -> - string list * TcPatLinearEnv + pat: SynPat -> + string list * TcPatLinearEnv * SynSimplePats // Check a pattern, e.g. for a binding or a match clause val TcPat: diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index c30ca0cdc66..2667ea6ccfc 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -57,97 +57,18 @@ open FSharp.Compiler.Import open FSharp.Compiler.InfoReader open FSharp.Compiler.Infos open FSharp.Compiler.MethodCalls +open FSharp.Compiler.NameResolution open FSharp.Compiler.Syntax open FSharp.Compiler.Syntax.PrettyNaming open FSharp.Compiler.SyntaxTreeOps open FSharp.Compiler.TcGlobals open FSharp.Compiler.Text -open FSharp.Compiler.Text.Range open FSharp.Compiler.TypedTree open FSharp.Compiler.TypedTreeBasics open FSharp.Compiler.TypedTreeOps open FSharp.Compiler.TypeHierarchy open FSharp.Compiler.TypeRelations -//------------------------------------------------------------------------- -// Generate type variables and record them in within the scope of the -// compilation environment, which currently corresponds to the scope -// of the constraint resolution carried out by type checking. -//------------------------------------------------------------------------- - -let compgenId = mkSynId range0 unassignedTyparName - -let NewCompGenTypar (kind, rigid, staticReq, dynamicReq, error) = - Construct.NewTypar(kind, rigid, SynTypar(compgenId, staticReq, true), error, dynamicReq, [], false, false) - -let AnonTyparId m = mkSynId m unassignedTyparName - -let NewAnonTypar (kind, m, rigid, var, dyn) = - Construct.NewTypar (kind, rigid, SynTypar(AnonTyparId m, var, true), false, dyn, [], false, false) - -let NewNamedInferenceMeasureVar (_m, rigid, var, id) = - Construct.NewTypar(TyparKind.Measure, rigid, SynTypar(id, var, false), false, TyparDynamicReq.No, [], false, false) - -let NewInferenceMeasurePar () = - NewCompGenTypar (TyparKind.Measure, TyparRigidity.Flexible, TyparStaticReq.None, TyparDynamicReq.No, false) - -let NewErrorTypar () = - NewCompGenTypar (TyparKind.Type, TyparRigidity.Flexible, TyparStaticReq.None, TyparDynamicReq.No, true) - -let NewErrorMeasureVar () = - NewCompGenTypar (TyparKind.Measure, TyparRigidity.Flexible, TyparStaticReq.None, TyparDynamicReq.No, true) - -let NewInferenceType (g: TcGlobals) = - ignore g // included for future, minimizing code diffs, see https://github.com/dotnet/fsharp/pull/6804 - mkTyparTy (Construct.NewTypar (TyparKind.Type, TyparRigidity.Flexible, SynTypar(compgenId, TyparStaticReq.None, true), false, TyparDynamicReq.No, [], false, false)) - -let NewErrorType () = - mkTyparTy (NewErrorTypar ()) - -let NewErrorMeasure () = - Measure.Var (NewErrorMeasureVar ()) - -let NewByRefKindInferenceType (g: TcGlobals) m = - let tp = Construct.NewTypar (TyparKind.Type, TyparRigidity.Flexible, SynTypar(compgenId, TyparStaticReq.HeadType, true), false, TyparDynamicReq.No, [], false, false) - if g.byrefkind_InOut_tcr.CanDeref then - tp.SetConstraints [TyparConstraint.DefaultsTo(10, TType_app(g.byrefkind_InOut_tcr, [], g.knownWithoutNull), m)] - mkTyparTy tp - -let NewInferenceTypes g l = l |> List.map (fun _ -> NewInferenceType g) - -let FreshenTypar (g: TcGlobals) rigid (tp: Typar) = - let clearStaticReq = g.langVersion.SupportsFeature LanguageFeature.InterfacesWithAbstractStaticMembers - let staticReq = if clearStaticReq then TyparStaticReq.None else tp.StaticReq - let dynamicReq = if rigid = TyparRigidity.Rigid then TyparDynamicReq.Yes else TyparDynamicReq.No - NewCompGenTypar (tp.Kind, rigid, staticReq, dynamicReq, false) - -// QUERY: should 'rigid' ever really be 'true'? We set this when we know -// we are going to have to generalize a typar, e.g. when implementing a -// abstract generic method slot. But we later check the generalization -// condition anyway, so we could get away with a non-rigid typar. This -// would sort of be cleaner, though give errors later. -let FreshenAndFixupTypars g m rigid fctps tinst tpsorig = - let tps = tpsorig |> List.map (FreshenTypar g rigid) - let renaming, tinst = FixupNewTypars m fctps tinst tpsorig tps - tps, renaming, tinst - -let FreshenTypeInst g m tpsorig = - FreshenAndFixupTypars g m TyparRigidity.Flexible [] [] tpsorig - -let FreshMethInst g m fctps tinst tpsorig = - FreshenAndFixupTypars g m TyparRigidity.Flexible fctps tinst tpsorig - -let FreshenTypars g m tpsorig = - match tpsorig with - | [] -> [] - | _ -> - let _, _, tpTys = FreshenTypeInst g m tpsorig - tpTys - -let FreshenMethInfo m (minfo: MethInfo) = - let _, _, tpTys = FreshMethInst minfo.TcGlobals m (minfo.GetFormalTyparsOfDeclaringType m) minfo.DeclaringTypeInst minfo.FormalMethodTypars - tpTys - //------------------------------------------------------------------------- // Unification of types: solve/record equality constraints // Subsumption of types: solve/record subtyping constraints @@ -1718,8 +1639,8 @@ and SolveMemberConstraint (csenv: ConstraintSolverEnv) ignoreUnresolvedOverload let propName = nm[4..] let props = supportTys |> List.choose (fun ty -> - match NameResolution.TryFindAnonRecdFieldOfType g ty propName with - | Some (NameResolution.Item.AnonRecdField(anonInfo, tinst, i, _)) -> Some (anonInfo, tinst, i) + match TryFindAnonRecdFieldOfType g ty propName with + | Some (Item.AnonRecdField(anonInfo, tinst, i, _)) -> Some (anonInfo, tinst, i) | _ -> None) match props with | [ prop ] -> Some prop diff --git a/src/Compiler/Checking/ConstraintSolver.fsi b/src/Compiler/Checking/ConstraintSolver.fsi index eb48ce3b439..aab7c04dfec 100644 --- a/src/Compiler/Checking/ConstraintSolver.fsi +++ b/src/Compiler/Checking/ConstraintSolver.fsi @@ -15,61 +15,6 @@ open FSharp.Compiler.Text open FSharp.Compiler.TypedTree open FSharp.Compiler.TypedTreeOps -/// Create a type variable representing the use of a "_" in F# code -val NewAnonTypar: TyparKind * range * TyparRigidity * TyparStaticReq * TyparDynamicReq -> Typar - -/// Create an inference type variable -val NewInferenceType: TcGlobals -> TType - -/// Create an inference type variable for the kind of a byref pointer -val NewByRefKindInferenceType: TcGlobals -> range -> TType - -/// Create an inference type variable representing an error condition when checking an expression -val NewErrorType: unit -> TType - -/// Create an inference type variable representing an error condition when checking a measure -val NewErrorMeasure: unit -> Measure - -/// Create a list of inference type variables, one for each element in the input list -val NewInferenceTypes: TcGlobals -> 'T list -> TType list - -/// Given a set of formal type parameters and their constraints, make new inference type variables for -/// each and ensure that the constraints on the new type variables are adjusted to refer to these. -/// -/// Returns -/// 1. the new type parameters -/// 2. the instantiation mapping old type parameters to inference variables -/// 3. the inference type variables as a list of types. -val FreshenAndFixupTypars: - g: TcGlobals -> - m: range -> - rigid: TyparRigidity -> - Typars -> - TType list -> - Typars -> - Typars * TyparInstantiation * TType list - -/// Given a set of type parameters, make new inference type variables for -/// each and ensure that the constraints on the new type variables are adjusted. -/// -/// Returns -/// 1. the new type parameters -/// 2. the instantiation mapping old type parameters to inference variables -/// 3. the inference type variables as a list of types. -val FreshenTypeInst: g: TcGlobals -> range -> Typars -> Typars * TyparInstantiation * TType list - -/// Given a set of type parameters, make new inference type variables for -/// each and ensure that the constraints on the new type variables are adjusted. -/// -/// Returns the inference type variables as a list of types. -val FreshenTypars: g: TcGlobals -> range -> Typars -> TType list - -/// Given a method, which may be generic, make new inference type variables for -/// its generic parameters, and ensure that the constraints the new type variables are adjusted. -/// -/// Returns the inference type variables as a list of types. -val FreshenMethInfo: range -> MethInfo -> TType list - /// Information about the context of a type equation. [] type ContextInfo = diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index 5afdada9b05..68b333b52d9 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -87,9 +87,9 @@ let ActivePatternElemsOfValRef g (vref: ValRef) = match TryGetActivePatternInfo vref with | Some apinfo -> - let isStructRetTy = + let retKind = if apinfo.IsTotal then - false + ActivePatternReturnKind.RefTypeWrapper else let _, apReturnTy = stripFunTy g vref.TauType let hasStructAttribute() = @@ -97,8 +97,10 @@ let ActivePatternElemsOfValRef g (vref: ValRef) = |> List.exists (function | Attrib(targetsOpt = Some(System.AttributeTargets.ReturnValue)) as a -> IsMatchingFSharpAttribute g g.attrib_StructAttribute a | _ -> false) - isStructTy g apReturnTy || hasStructAttribute() - apinfo.ActiveTags |> List.mapi (fun i _ -> APElemRef(apinfo, vref, i, isStructRetTy)) + if isValueOptionTy g apReturnTy || hasStructAttribute() then ActivePatternReturnKind.StructTypeWrapper + elif isBoolTy g apReturnTy then ActivePatternReturnKind.Boolean + else ActivePatternReturnKind.RefTypeWrapper + apinfo.ActiveTags |> List.mapi (fun i _ -> APElemRef(apinfo, vref, i, retKind)) | None -> [] /// Try to make a reference to a value in a module. @@ -276,7 +278,7 @@ type Item = | Item.CtorGroup(nm, _) -> nm |> DemangleGenericTypeName | Item.DelegateCtor ty -> match ty with - | AbbrevOrAppTy tcref -> tcref.DisplayNameCore + | AbbrevOrAppTy(tcref, _) -> tcref.DisplayNameCore // This case is not expected | _ -> "" | Item.UnqualifiedType(tcref :: _) -> tcref.DisplayNameCore @@ -307,7 +309,7 @@ type Item = | Item.Property(info = pinfo :: _) -> pinfo.DisplayName | Item.Event einfo -> einfo.DisplayName | Item.MethodGroup(_, minfo :: _, _) -> minfo.DisplayName - | Item.DelegateCtor (AbbrevOrAppTy tcref) -> tcref.DisplayName + | Item.DelegateCtor (AbbrevOrAppTy(tcref, _)) -> tcref.DisplayName | Item.UnqualifiedType(tcref :: _) -> tcref.DisplayName | Item.ModuleOrNamespaces(modref :: _) -> modref.DisplayName | Item.TypeVar (nm, _) -> nm |> ConvertLogicalNameToDisplayName @@ -1566,6 +1568,85 @@ let FreshenUnionCaseRef (ncenv: NameResolver) m (ucref: UnionCaseRef) = let FreshenRecdFieldRef (ncenv: NameResolver) m (rfref: RecdFieldRef) = RecdFieldInfo(ncenv.InstantiationGenerator m (rfref.Tycon.Typars m), rfref) +//------------------------------------------------------------------------- +// Generate type variables and record them in within the scope of the +// compilation environment, which currently corresponds to the scope +// of the constraint resolution carried out by type checking. +//------------------------------------------------------------------------- + +let compgenId = mkSynId range0 unassignedTyparName + +let NewCompGenTypar (kind, rigid, staticReq, dynamicReq, error) = + Construct.NewTypar(kind, rigid, SynTypar(compgenId, staticReq, true), error, dynamicReq, [], false, false) + +let AnonTyparId m = mkSynId m unassignedTyparName + +let NewAnonTypar (kind, m, rigid, var, dyn) = + Construct.NewTypar (kind, rigid, SynTypar(AnonTyparId m, var, true), false, dyn, [], false, false) + +let NewNamedInferenceMeasureVar (_m: range, rigid, var, id) = + Construct.NewTypar(TyparKind.Measure, rigid, SynTypar(id, var, false), false, TyparDynamicReq.No, [], false, false) + +let NewInferenceMeasurePar () = + NewCompGenTypar (TyparKind.Measure, TyparRigidity.Flexible, TyparStaticReq.None, TyparDynamicReq.No, false) + +let NewErrorTypar () = + NewCompGenTypar (TyparKind.Type, TyparRigidity.Flexible, TyparStaticReq.None, TyparDynamicReq.No, true) + +let NewErrorMeasureVar () = + NewCompGenTypar (TyparKind.Measure, TyparRigidity.Flexible, TyparStaticReq.None, TyparDynamicReq.No, true) + +let NewInferenceType (g: TcGlobals) = + ignore g // included for future, minimizing code diffs, see https://github.com/dotnet/fsharp/pull/6804 + mkTyparTy (Construct.NewTypar (TyparKind.Type, TyparRigidity.Flexible, SynTypar(compgenId, TyparStaticReq.None, true), false, TyparDynamicReq.No, [], false, false)) + +let NewErrorType () = + mkTyparTy (NewErrorTypar ()) + +let NewErrorMeasure () = + Measure.Var (NewErrorMeasureVar ()) + +let NewByRefKindInferenceType (g: TcGlobals) m = + let tp = Construct.NewTypar (TyparKind.Type, TyparRigidity.Flexible, SynTypar(compgenId, TyparStaticReq.HeadType, true), false, TyparDynamicReq.No, [], false, false) + if g.byrefkind_InOut_tcr.CanDeref then + tp.SetConstraints [TyparConstraint.DefaultsTo(10, TType_app(g.byrefkind_InOut_tcr, [], g.knownWithoutNull), m)] + mkTyparTy tp + +let NewInferenceTypes g l = l |> List.map (fun _ -> NewInferenceType g) + +let FreshenTypar (g: TcGlobals) rigid (tp: Typar) = + let clearStaticReq = g.langVersion.SupportsFeature LanguageFeature.InterfacesWithAbstractStaticMembers + let staticReq = if clearStaticReq then TyparStaticReq.None else tp.StaticReq + let dynamicReq = if rigid = TyparRigidity.Rigid then TyparDynamicReq.Yes else TyparDynamicReq.No + NewCompGenTypar (tp.Kind, rigid, staticReq, dynamicReq, false) + +// QUERY: should 'rigid' ever really be 'true'? We set this when we know +// we are going to have to generalize a typar, e.g. when implementing a +// abstract generic method slot. But we later check the generalization +// condition anyway, so we could get away with a non-rigid typar. This +// would sort of be cleaner, though give errors later. +let FreshenAndFixupTypars g m rigid fctps tinst tpsorig = + let tps = tpsorig |> List.map (FreshenTypar g rigid) + let renaming, tinst = FixupNewTypars m fctps tinst tpsorig tps + tps, renaming, tinst + +let FreshenTypeInst g m tpsorig = + FreshenAndFixupTypars g m TyparRigidity.Flexible [] [] tpsorig + +let FreshMethInst g m fctps tinst tpsorig = + FreshenAndFixupTypars g m TyparRigidity.Flexible fctps tinst tpsorig + +let FreshenTypars g m tpsorig = + match tpsorig with + | [] -> [] + | _ -> + let _, _, tpTys = FreshenTypeInst g m tpsorig + tpTys + +let FreshenMethInfo m (minfo: MethInfo) = + let _, _, tpTys = FreshMethInst minfo.TcGlobals m (minfo.GetFormalTyparsOfDeclaringType m) minfo.DeclaringTypeInst minfo.FormalMethodTypars + tpTys + /// This must be called after fetching unqualified items that may need to be freshened /// or have type instantiations let ResolveUnqualifiedItem (ncenv: NameResolver) nenv m res = @@ -1739,87 +1820,100 @@ let (|ValRefOfProp|_|) (pi: PropInfo) = pi.ArbitraryValRef let (|ValRefOfMeth|_|) (mi: MethInfo) = mi.ArbitraryValRef let (|ValRefOfEvent|_|) (evt: EventInfo) = evt.ArbitraryValRef +[] let rec (|RecordFieldUse|_|) (item: Item) = match item with - | Item.RecdField(RecdFieldInfo(_, RecdFieldRef(tcref, name))) -> Some (name, tcref) - | Item.SetterArg(_, RecordFieldUse f) -> Some f - | _ -> None + | Item.RecdField(RecdFieldInfo(_, RecdFieldRef(tcref, name))) -> ValueSome (name, tcref) + | Item.SetterArg(_, RecordFieldUse f) -> ValueSome f + | _ -> ValueNone +[] let (|UnionCaseFieldUse|_|) (item: Item) = match item with - | Item.UnionCaseField (uci, fieldIndex) -> Some (fieldIndex, uci.UnionCaseRef) - | _ -> None + | Item.UnionCaseField (uci, fieldIndex) -> ValueSome (fieldIndex, uci.UnionCaseRef) + | _ -> ValueNone +[] let rec (|ILFieldUse|_|) (item: Item) = match item with - | Item.ILField finfo -> Some finfo - | Item.SetterArg(_, ILFieldUse f) -> Some f - | _ -> None + | Item.ILField finfo -> ValueSome finfo + | Item.SetterArg(_, ILFieldUse f) -> ValueSome f + | _ -> ValueNone +[] let rec (|PropertyUse|_|) (item: Item) = match item with - | Item.Property(info = pinfo :: _) -> Some pinfo - | Item.SetterArg(_, PropertyUse pinfo) -> Some pinfo - | _ -> None + | Item.Property(info = pinfo :: _) -> ValueSome pinfo + | Item.SetterArg(_, PropertyUse pinfo) -> ValueSome pinfo + | _ -> ValueNone +[] let rec (|FSharpPropertyUse|_|) (item: Item) = match item with - | Item.Property(info = [ValRefOfProp vref]) -> Some vref - | Item.SetterArg(_, FSharpPropertyUse propDef) -> Some propDef - | _ -> None + | Item.Property(info = [ValRefOfProp vref]) -> ValueSome vref + | Item.SetterArg(_, FSharpPropertyUse propDef) -> ValueSome propDef + | _ -> ValueNone +[] let (|MethodUse|_|) (item: Item) = match item with - | Item.MethodGroup(_, [minfo], _) -> Some minfo - | _ -> None + | Item.MethodGroup(_, [minfo], _) -> ValueSome minfo + | _ -> ValueNone +[] let (|FSharpMethodUse|_|) (item: Item) = match item with - | Item.MethodGroup(_, [ValRefOfMeth vref], _) -> Some vref - | Item.Value vref when vref.IsMember -> Some vref - | _ -> None + | Item.MethodGroup(_, [ValRefOfMeth vref], _) -> ValueSome vref + | Item.Value vref when vref.IsMember -> ValueSome vref + | _ -> ValueNone +[] let (|EntityUse|_|) (item: Item) = match item with - | Item.UnqualifiedType (tcref :: _) -> Some tcref - | Item.ExnCase tcref -> Some tcref - | Item.Types(_, [AbbrevOrAppTy tcref]) - | Item.DelegateCtor(AbbrevOrAppTy tcref) -> Some tcref + | Item.UnqualifiedType (tcref :: _) -> ValueSome tcref + | Item.ExnCase tcref -> ValueSome tcref + | Item.Types(_, [AbbrevOrAppTy(tcref, _)]) + | Item.DelegateCtor(AbbrevOrAppTy(tcref, _)) -> ValueSome tcref | Item.CtorGroup(_, ctor :: _) -> match ctor.ApparentEnclosingType with - | AbbrevOrAppTy tcref -> Some tcref - | _ -> None - | _ -> None + | AbbrevOrAppTy(tcref, _) -> ValueSome tcref + | _ -> ValueNone + | _ -> ValueNone +[] let (|EventUse|_|) (item: Item) = match item with - | Item.Event einfo -> Some einfo - | _ -> None + | Item.Event einfo -> ValueSome einfo + | _ -> ValueNone +[] let (|FSharpEventUse|_|) (item: Item) = match item with - | Item.Event(ValRefOfEvent vref) -> Some vref - | _ -> None + | Item.Event(ValRefOfEvent vref) -> ValueSome vref + | _ -> ValueNone +[] let (|UnionCaseUse|_|) (item: Item) = match item with - | Item.UnionCase(UnionCaseInfo(_, u1), _) -> Some u1 - | _ -> None + | Item.UnionCase(UnionCaseInfo(_, u1), _) -> ValueSome u1 + | _ -> ValueNone +[] let (|ValUse|_|) (item: Item) = match item with | Item.Value vref | FSharpPropertyUse vref | FSharpMethodUse vref | FSharpEventUse vref - | Item.CustomBuilder(_, vref) -> Some vref - | _ -> None + | Item.CustomBuilder(_, vref) -> ValueSome vref + | _ -> ValueNone +[] let (|ActivePatternCaseUse|_|) (item: Item) = match item with - | Item.ActivePatternCase(APElemRef(_, vref, idx, _)) -> Some (vref.SigRange, vref.DefinitionRange, idx) - | Item.ActivePatternResult(ap, _, idx, _) -> Some (ap.Range, ap.Range, idx) - | _ -> None + | Item.ActivePatternCase(APElemRef(_, vref, idx, _)) -> ValueSome (vref.SigRange, vref.DefinitionRange, idx) + | Item.ActivePatternResult(ap, _, idx, _) -> ValueSome (ap.Range, ap.Range, idx) + | _ -> ValueNone let tyconRefDefnHash (_g: TcGlobals) (eref1: EntityRef) = hash eref1.LogicalName @@ -1864,7 +1958,7 @@ let ItemsAreEffectivelyEqual g orig other = not tp1.IsCompilerGenerated && not tp1.IsFromError && not tp2.IsCompilerGenerated && not tp2.IsFromError && equals tp1.Range tp2.Range - | AbbrevOrAppTy tcref1, AbbrevOrAppTy tcref2 -> + | AbbrevOrAppTy(tcref1, _), AbbrevOrAppTy(tcref2, _) -> tyconRefDefnEq g tcref1 tcref2 | _ -> false) @@ -2840,9 +2934,10 @@ let private ResolveLongIdentInTyconRefs atMostOne (ncenv: NameResolver) nenv loo // ResolveExprLongIdentInModuleOrNamespace //------------------------------------------------------------------------- +[] let (|AccessibleEntityRef|_|) amap m ad (modref: ModuleOrNamespaceRef) mspec = let eref = modref.NestedTyconRef mspec - if IsEntityAccessible amap m ad eref then Some eref else None + if IsEntityAccessible amap m ad eref then ValueSome eref else ValueNone let rec ResolveExprLongIdentInModuleOrNamespace (ncenv: NameResolver) nenv (typeNameResInfo: TypeNameResolutionInfo) ad resInfo depth m modref (mty: ModuleOrNamespaceType) (id: Ident) (rest: Ident list) = // resInfo records the modules or namespaces actually relevant to a resolution @@ -3082,8 +3177,9 @@ let rec ResolveExprLongIdentPrim sink (ncenv: NameResolver) first fullyQualified |> CollectResults success match tyconSearch () with - | Result ((resInfo, tcref) :: _) -> - let item = Item.Types(id.idText, [ generalizedTyconRef ncenv.g tcref ]) + | Result((resInfo, tcref) :: _) -> + let _, _, tyargs = FreshenTypeInst ncenv.g m (tcref.Typars m) + let item = Item.Types(id.idText, [TType_app(tcref, tyargs, ncenv.g.knownWithoutNull)]) success (resInfo, item) | _ -> @@ -3447,9 +3543,12 @@ let ResolveTypeLongIdentInTyconRef sink (ncenv: NameResolver) nenv typeNameResIn | id :: rest -> ForceRaise (ResolveTypeLongIdentInTyconRefPrim ncenv typeNameResInfo ad ResolutionInfo.Empty PermitDirectReferenceToGeneratedType.No 0 m tcref id rest) ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.Use, ad, resInfo, ResultTyparChecker(fun () -> true)) - let item = Item.Types(tcref.DisplayName, [FreshenTycon ncenv m tcref]) - CallNameResolutionSink sink (rangeOfLid lid, nenv, item, emptyTyparInst, ItemOccurence.UseInType, ad) - tcref + + let _, tinst, tyargs = FreshenTypeInst ncenv.g m (tcref.Typars m) + let item = Item.Types(tcref.DisplayName, [TType_app(tcref, tyargs, ncenv.g.knownWithoutNull)]) + CallNameResolutionSink sink (rangeOfLid lid, nenv, item, tinst, ItemOccurence.UseInType, ad) + + tcref, tyargs /// Create an UndefinedName error with details let SuggestTypeLongIdentInModuleOrNamespace depth (modref: ModuleOrNamespaceRef) amap ad m (id: Ident) = @@ -3593,7 +3692,6 @@ let rec ResolveTypeLongIdentPrim sink (ncenv: NameResolver) occurence first full let r = AddResults searchSoFar (modulSearchFailed()) AtMostOneResult m2 (r |?> (fun tcrefs -> CheckForTypeLegitimacyAndMultipleGenericTypeAmbiguities (tcrefs, typeNameResInfo, genOk, m))) - /// Resolve a long identifier representing a type and report it let ResolveTypeLongIdentAux sink (ncenv: NameResolver) occurence fullyQualified nenv ad (lid: Ident list) staticResInfo genOk = let m = rangeOfLid lid @@ -3608,15 +3706,20 @@ let ResolveTypeLongIdentAux sink (ncenv: NameResolver) occurence fullyQualified match res with | Result (resInfo, tcref) -> ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.UseInType, ad, resInfo, ResultTyparChecker(fun () -> true)) - let item = Item.Types(tcref.DisplayName, [FreshenTycon ncenv m tcref]) - CallNameResolutionSink sink (m, nenv, item, emptyTyparInst, occurence, ad) - | _ -> () - res + + let _, tinst, tyargs = FreshenTypeInst ncenv.g m (tcref.Typars m) + let item = Item.Types(tcref.DisplayName, [TType_app(tcref, tyargs, ncenv.g.knownWithoutNull)]) + CallNameResolutionSink sink (m, nenv, item, tinst, occurence, ad) + + Result(resInfo, tcref, tyargs) + + | Exception exn -> + Exception exn /// Resolve a long identifier representing a type and report it let ResolveTypeLongIdent sink ncenv occurence fullyQualified nenv ad lid staticResInfo genOk = let res = ResolveTypeLongIdentAux sink ncenv occurence fullyQualified nenv ad lid staticResInfo genOk - (res |?> fun (resInfo, tcref) -> (resInfo.EnclosingTypeInst, tcref)) + res |?> fun (resInfo, tcref, ttypes) -> (resInfo.EnclosingTypeInst, tcref, ttypes) //------------------------------------------------------------------------- // Resolve F#/IL "." syntax in records etc. @@ -4088,11 +4191,12 @@ let ResolveLongIdentAsExprAndComputeRange (sink: TcResultsSink) (ncenv: NameReso success (tinstEnclosing, item, itemRange, rest, afterResolution) +[] let (|NonOverridable|_|) namedItem = match namedItem with - | Item.MethodGroup(_, minfos, _) when minfos |> List.exists(fun minfo -> minfo.IsVirtual || minfo.IsAbstract) -> None - | Item.Property(info = pinfos) when pinfos |> List.exists(fun pinfo -> pinfo.IsVirtualProperty) -> None - | _ -> Some () + | Item.MethodGroup(_, minfos, _) when minfos |> List.exists(fun minfo -> minfo.IsVirtual || minfo.IsAbstract) -> ValueNone + | Item.Property(info = pinfos) when pinfos |> List.exists(fun pinfo -> pinfo.IsVirtualProperty) -> ValueNone + | _ -> ValueSome () /// Called for 'expression.Bar' - for VS IntelliSense, we can filter out static members from method groups /// Also called for 'GenericType.Bar' - for VS IntelliSense, we can filter out non-static members from method groups diff --git a/src/Compiler/Checking/NameResolution.fsi b/src/Compiler/Checking/NameResolution.fsi index 43cfdd12d5c..c80125f1862 100755 --- a/src/Compiler/Checking/NameResolution.fsi +++ b/src/Compiler/Checking/NameResolution.fsi @@ -676,6 +676,67 @@ exception internal UpperCaseIdentifierInPattern of range /// Generate a new reference to a record field with a fresh type instantiation val FreshenRecdFieldRef: NameResolver -> range -> RecdFieldRef -> RecdFieldInfo +/// Create a type variable representing the use of a "_" in F# code +val NewAnonTypar: TyparKind * range * TyparRigidity * TyparStaticReq * TyparDynamicReq -> Typar + +val NewNamedInferenceMeasureVar: range * TyparRigidity * TyparStaticReq * Ident -> Typar + +val NewNamedInferenceMeasureVar: range * TyparRigidity * TyparStaticReq * Ident -> Typar + +val NewInferenceMeasurePar: unit -> Typar + +/// Create an inference type variable +val NewInferenceType: TcGlobals -> TType + +/// Create an inference type variable for the kind of a byref pointer +val NewByRefKindInferenceType: TcGlobals -> range -> TType + +/// Create an inference type variable representing an error condition when checking an expression +val NewErrorType: unit -> TType + +/// Create an inference type variable representing an error condition when checking a measure +val NewErrorMeasure: unit -> Measure + +/// Create a list of inference type variables, one for each element in the input list +val NewInferenceTypes: TcGlobals -> 'T list -> TType list + +/// Given a set of type parameters, make new inference type variables for +/// each and ensure that the constraints on the new type variables are adjusted. +/// +/// Returns the inference type variables as a list of types. +val FreshenTypars: g: TcGlobals -> range -> Typars -> TType list + +/// Given a method, which may be generic, make new inference type variables for +/// its generic parameters, and ensure that the constraints the new type variables are adjusted. +/// +/// Returns the inference type variables as a list of types. +val FreshenMethInfo: range -> MethInfo -> TType list + +/// Given a set of formal type parameters and their constraints, make new inference type variables for +/// each and ensure that the constraints on the new type variables are adjusted to refer to these. +/// +/// Returns +/// 1. the new type parameters +/// 2. the instantiation mapping old type parameters to inference variables +/// 3. the inference type variables as a list of types. +val FreshenAndFixupTypars: + g: TcGlobals -> + m: range -> + rigid: TyparRigidity -> + fctps: Typars -> + tinst: TType list -> + tpsorig: Typar list -> + Typar list * TyparInstantiation * TTypes + +/// Given a set of type parameters, make new inference type variables for +/// each and ensure that the constraints on the new type variables are adjusted. +/// +/// Returns +/// 1. the new type parameters +/// 2. the instantiation mapping old type parameters to inference variables +/// 3. the inference type variables as a list of types. +val FreshenTypeInst: g: TcGlobals -> m: range -> tpsorig: Typar list -> Typar list * TyparInstantiation * TTypes + /// Resolve a long identifier to a namespace, module. val internal ResolveLongIdentAsModuleOrNamespace: sink: TcResultsSink -> @@ -733,7 +794,7 @@ val internal ResolveTypeLongIdentInTyconRef: m: range -> tcref: TyconRef -> lid: Ident list -> - TyconRef + TyconRef * TypeInst /// Resolve a long identifier to a type definition val internal ResolveTypeLongIdent: @@ -746,7 +807,7 @@ val internal ResolveTypeLongIdent: lid: Ident list -> staticResInfo: TypeNameResolutionStaticArgsInfo -> genOk: PermitDirectReferenceToGeneratedType -> - ResultOrException + ResultOrException /// Resolve a long identifier to a field val internal ResolveField: diff --git a/src/Compiler/Checking/PatternMatchCompilation.fs b/src/Compiler/Checking/PatternMatchCompilation.fs index 3caacecb982..4e8ced8fadb 100644 --- a/src/Compiler/Checking/PatternMatchCompilation.fs +++ b/src/Compiler/Checking/PatternMatchCompilation.fs @@ -43,7 +43,7 @@ type Pattern = | TPat_as of Pattern * PatternValBinding * range (* note: can be replaced by TPat_var, i.e. equals TPat_conjs([TPat_var; pat]) *) | TPat_disjs of Pattern list * range | TPat_conjs of Pattern list * range - | TPat_query of (Expr * TType list * bool * (ValRef * TypeInst) option * int * ActivePatternInfo) * Pattern * range + | TPat_query of (Expr * TType list * ActivePatternReturnKind * (ValRef * TypeInst) option * int * ActivePatternInfo) * Pattern * range | TPat_unioncase of UnionCaseRef * TypeInst * Pattern list * range | TPat_exnconstr of TyconRef * Pattern list * range | TPat_tuple of TupInfo * Pattern list * TType list * range @@ -618,8 +618,8 @@ let getDiscrimOfPattern (g: TcGlobals) tpinst t = Some(DecisionTreeTest.UnionCase (c, instTypes tpinst tyargs')) | TPat_array (args, ty, _m) -> Some(DecisionTreeTest.ArrayLength (args.Length, ty)) - | TPat_query ((activePatExpr, resTys, isStructRetTy, apatVrefOpt, idx, apinfo), _, _m) -> - Some (DecisionTreeTest.ActivePatternCase (activePatExpr, instTypes tpinst resTys, isStructRetTy, apatVrefOpt, idx, apinfo)) + | TPat_query ((activePatExpr, resTys, retKind, apatVrefOpt, idx, apinfo), _, _m) -> + Some (DecisionTreeTest.ActivePatternCase (activePatExpr, instTypes tpinst resTys, retKind, apatVrefOpt, idx, apinfo)) | TPat_error range -> Some (DecisionTreeTest.Error range) @@ -740,19 +740,22 @@ let ChooseInvestigationPointLeftToRight frontiers = // This is an initial attempt to remove extra typetests/castclass for simple list pattern matching "match x with h :: t -> ... | [] -> ..." // The problem with this technique is that it creates extra locals which inhibit the process of converting pattern matches into linear let bindings. +[] let (|ListConsDiscrim|_|) g = function | (DecisionTreeTest.UnionCase (ucref, tinst)) (* check we can use a simple 'isinst' instruction *) - when tyconRefEq g ucref.TyconRef g.list_tcr_canon & ucref.CaseName = "op_ColonColon" -> Some tinst - | _ -> None + when tyconRefEq g ucref.TyconRef g.list_tcr_canon & ucref.CaseName = "op_ColonColon" -> ValueSome tinst + | _ -> ValueNone +[] let (|ListEmptyDiscrim|_|) g = function | (DecisionTreeTest.UnionCase (ucref, tinst)) (* check we can use a simple 'isinst' instruction *) - when tyconRefEq g ucref.TyconRef g.list_tcr_canon & ucref.CaseName = "op_Nil" -> Some tinst - | _ -> None + when tyconRefEq g ucref.TyconRef g.list_tcr_canon & ucref.CaseName = "op_Nil" -> ValueSome tinst + | _ -> ValueNone #endif +[] let (|ConstNeedsDefaultCase|_|) c = match c with | Const.Decimal _ @@ -767,8 +770,8 @@ let (|ConstNeedsDefaultCase|_|) c = | Const.UInt64 _ | Const.IntPtr _ | Const.UIntPtr _ - | Const.Char _ -> Some () - | _ -> None + | Const.Char _ -> ValueSome () + | _ -> ValueNone /// Build a dtree, equivalent to: TDSwitch("expr", edges, default, m) /// @@ -938,8 +941,8 @@ let rec investigationPoints inpPat = let rec erasePartialPatterns inpPat = match inpPat with - | TPat_query ((expr, resTys, isStructRetTy, apatVrefOpt, idx, apinfo), p, m) -> - if apinfo.IsTotal then TPat_query ((expr, resTys, isStructRetTy, apatVrefOpt, idx, apinfo), erasePartialPatterns p, m) + | TPat_query ((expr, resTys, retKind, apatVrefOpt, idx, apinfo), p, m) -> + if apinfo.IsTotal then TPat_query ((expr, resTys, retKind, apatVrefOpt, idx, apinfo), erasePartialPatterns p, m) else TPat_disjs ([], m) (* always fail *) | TPat_as (p, x, m) -> TPat_as (erasePartialPatterns p, x, m) | TPat_disjs (subPats, m) -> TPat_disjs(erasePartials subPats, m) @@ -1290,15 +1293,20 @@ let CompilePatternBasic // Active pattern matches: create a variable to hold the results of executing the active pattern. // If a struct return we continue with an expression for taking the address of that location. - | EdgeDiscrim(_, DecisionTreeTest.ActivePatternCase(activePatExpr, resTys, isStructRetTy, _apatVrefOpt, _, apinfo), m) :: _ -> + | EdgeDiscrim(_, DecisionTreeTest.ActivePatternCase(activePatExpr, resTys, retKind, _apatVrefOpt, _, apinfo), m) :: _ -> if not (isNil origInputValTypars) then error(InternalError("Unexpected generalized type variables when compiling an active pattern", m)) - let resTy = apinfo.ResultType g m resTys isStructRetTy + let resTy = apinfo.ResultType g m resTys retKind let argExpr = GetSubExprOfInput subexpr let appExpr = mkApps g ((activePatExpr, tyOfExpr g activePatExpr), [], [argExpr], m) - let vOpt, addrExp, _readonly, _writeonly = mkExprAddrOfExprAux g isStructRetTy false NeverMutates appExpr None mMatch + let mustTakeAddress = + match retKind with + | ActivePatternReturnKind.StructTypeWrapper -> true + | ActivePatternReturnKind.RefTypeWrapper + | ActivePatternReturnKind.Boolean -> false + let vOpt, addrExp, _readonly, _writeonly = mkExprAddrOfExprAux g mustTakeAddress false NeverMutates appExpr None mMatch match vOpt with | None -> let v, vExpr = mkCompGenLocal m ("activePatternResult" + string (newUnique())) resTy @@ -1354,13 +1362,17 @@ let CompilePatternBasic // Convert active pattern edges to tests on results data let discrim' = match discrim with - | DecisionTreeTest.ActivePatternCase(_pexp, resTys, isStructRetTy, _apatVrefOpt, idx, apinfo) -> + | DecisionTreeTest.ActivePatternCase(_pexp, resTys, retKind, _apatVrefOpt, idx, apinfo) -> let aparity = apinfo.ActiveTags.Length let total = apinfo.IsTotal if not total && aparity > 1 then error(Error(FSComp.SR.patcPartialActivePatternsGenerateOneResult(), m)) - if not total then DecisionTreeTest.UnionCase(mkAnySomeCase g isStructRetTy, resTys) + if not total then + match retKind with + | ActivePatternReturnKind.Boolean -> DecisionTreeTest.Const(Const.Bool true) + | ActivePatternReturnKind.RefTypeWrapper -> DecisionTreeTest.UnionCase(mkAnySomeCase g false, resTys) + | ActivePatternReturnKind.StructTypeWrapper -> DecisionTreeTest.UnionCase(mkAnySomeCase g true, resTys) elif aparity <= 1 then DecisionTreeTest.Const(Const.Unit) else DecisionTreeTest.UnionCase(mkChoiceCaseRef g m aparity idx, resTys) | _ -> discrim @@ -1432,7 +1444,7 @@ let CompilePatternBasic let newActives = removeActive path actives match patAtActive with | TPat_wild _ | TPat_as _ | TPat_tuple _ | TPat_disjs _ | TPat_conjs _ | TPat_recd _ -> failwith "Unexpected projection pattern" - | TPat_query ((_, resTys, isStructRetTy, apatVrefOpt, idx, apinfo), p, m) -> + | TPat_query ((_, resTys, retKind, apatVrefOpt, idx, apinfo), p, m) -> if apinfo.IsTotal then // Total active patterns always return choice values let hasParam = (match apatVrefOpt with None -> true | Some (vref, _) -> doesActivePatternHaveFreeTypars g vref) @@ -1460,10 +1472,12 @@ let CompilePatternBasic if i = iInvestigated then let subAccess _j tpinst _ = let expr = Option.get inpExprOpt - if isStructRetTy then + match retKind with + | ActivePatternReturnKind.Boolean -> expr + | ActivePatternReturnKind.StructTypeWrapper -> // In this case, the inpExprOpt is already an address-of expression mkUnionCaseFieldGetProvenViaExprAddr (expr, mkValueSomeCase g, instTypes tpinst resTys, 0, mExpr) - else + | ActivePatternReturnKind.RefTypeWrapper -> mkUnionCaseFieldGetUnprovenViaExprAddr (expr, mkSomeCase g, instTypes tpinst resTys, 0, mExpr) mkSubFrontiers path subAccess newActives [p] (fun path j -> PathQuery(path, int64 j)) else diff --git a/src/Compiler/Checking/PatternMatchCompilation.fsi b/src/Compiler/Checking/PatternMatchCompilation.fsi index 5b2d94c8ff5..b4d68aa320c 100644 --- a/src/Compiler/Checking/PatternMatchCompilation.fsi +++ b/src/Compiler/Checking/PatternMatchCompilation.fsi @@ -27,7 +27,10 @@ type Pattern = | TPat_as of Pattern * PatternValBinding * range | TPat_disjs of Pattern list * range | TPat_conjs of Pattern list * range - | TPat_query of (Expr * TType list * bool * (ValRef * TypeInst) option * int * ActivePatternInfo) * Pattern * range + | TPat_query of + (Expr * TType list * ActivePatternReturnKind * (ValRef * TypeInst) option * int * ActivePatternInfo) * + Pattern * + range | TPat_unioncase of UnionCaseRef * TypeInst * Pattern list * range | TPat_exnconstr of TyconRef * Pattern list * range | TPat_tuple of TupInfo * Pattern list * TType list * range diff --git a/src/Compiler/Checking/QuotationTranslator.fs b/src/Compiler/Checking/QuotationTranslator.fs index fa0d317ab95..8173c13755f 100644 --- a/src/Compiler/Checking/QuotationTranslator.fs +++ b/src/Compiler/Checking/QuotationTranslator.fs @@ -167,33 +167,37 @@ exception IgnoringPartOfQuotedTermWarning of string * range let wfail e = raise (InvalidQuotedTerm e) +[] let (|ModuleValueOrMemberUse|_|) g expr = let rec loop expr args = match stripExpr expr with | Expr.App (InnerExprPat(Expr.Val (vref, vFlags, _) as f), fty, tyargs, actualArgs, _m) when vref.IsMemberOrModuleBinding -> - Some(vref, vFlags, f, fty, tyargs, actualArgs @ args) + ValueSome(vref, vFlags, f, fty, tyargs, actualArgs @ args) | Expr.App (f, _fTy, [], actualArgs, _) -> loop f (actualArgs @ args) | Expr.Val (vref, vFlags, _m) as f when (match vref.TryDeclaringEntity with ParentNone -> false | _ -> true) -> let fty = tyOfExpr g f - Some(vref, vFlags, f, fty, [], args) + ValueSome(vref, vFlags, f, fty, [], args) | _ -> - None + ValueNone loop expr [] +[] let (|SimpleArrayLoopUpperBound|_|) expr = match expr with - | Expr.Op (TOp.ILAsm ([AI_sub], _), _, [Expr.Op (TOp.ILAsm ([I_ldlen; AI_conv ILBasicType.DT_I4], _), _, _, _); Expr.Const (Const.Int32 1, _, _) ], _) -> Some () - | _ -> None + | Expr.Op (TOp.ILAsm ([AI_sub], _), _, [Expr.Op (TOp.ILAsm ([I_ldlen; AI_conv ILBasicType.DT_I4], _), _, _, _); Expr.Const (Const.Int32 1, _, _) ], _) -> ValueSome () + | _ -> ValueNone +[] let (|SimpleArrayLoopBody|_|) g expr = match expr with | Expr.Lambda (_, a, b, ([_] as args), DebugPoints (Expr.Let (TBind(forVarLoop, DebugPoints (Expr.Op (TOp.ILAsm ([I_ldelem_any(ILArrayShape [(Some 0, None)], _)], _), [elemTy], [arr; idx], m1), _), seqPoint), body, m2, freeVars), _), m, ty) -> let body = Expr.Let (TBind(forVarLoop, mkCallArrayGet g m1 elemTy arr idx, seqPoint), body, m2, freeVars) let expr = Expr.Lambda (newUnique(), a, b, args, body, m, ty) - Some (arr, elemTy, expr) - | _ -> None + ValueSome (arr, elemTy, expr) + | _ -> ValueNone +[] let (|ObjectInitializationCheck|_|) g expr = // recognize "if this.init@ < 1 then failinit" match expr with @@ -207,8 +211,8 @@ let (|ObjectInitializationCheck|_|) g expr = name.StartsWithOrdinal("init") && selfRef.IsMemberThisVal && valRefEq g failInitRef (ValRefForIntrinsic g.fail_init_info) && - isUnitTy g resultTy -> Some() - | _ -> None + isUnitTy g resultTy -> ValueSome() + | _ -> ValueNone let isSplice g vref = valRefEq g vref g.splice_expr_vref || valRefEq g vref g.splice_raw_expr_vref diff --git a/src/Compiler/Checking/QuotationTranslator.fsi b/src/Compiler/Checking/QuotationTranslator.fsi index 288a8e1e73d..25567f51a63 100644 --- a/src/Compiler/Checking/QuotationTranslator.fsi +++ b/src/Compiler/Checking/QuotationTranslator.fsi @@ -41,10 +41,17 @@ val ConvExprPublic: QuotationGenerationScope -> suppressWitnesses: bool -> Expr val ConvReflectedDefinition: QuotationGenerationScope -> string -> Val -> Expr -> QuotationPickler.MethodBaseData * QuotationPickler.ExprData +[] val (|ModuleValueOrMemberUse|_|): - TcGlobals -> Expr -> (ValRef * ValUseFlag * Expr * TType * TypeInst * Expr list) option + TcGlobals -> Expr -> (ValRef * ValUseFlag * Expr * TType * TypeInst * Expr list) voption + +[] +val (|SimpleArrayLoopUpperBound|_|): Expr -> unit voption + +[] +val (|SimpleArrayLoopBody|_|): TcGlobals -> Expr -> (Expr * TType * Expr) voption + +[] +val (|ObjectInitializationCheck|_|): TcGlobals -> Expr -> unit voption -val (|SimpleArrayLoopUpperBound|_|): Expr -> unit option -val (|SimpleArrayLoopBody|_|): TcGlobals -> Expr -> (Expr * TType * Expr) option -val (|ObjectInitializationCheck|_|): TcGlobals -> Expr -> unit option val isSplice: TcGlobals -> ValRef -> bool diff --git a/src/Compiler/Checking/TailCallChecks.fs b/src/Compiler/Checking/TailCallChecks.fs index a962252fe7f..c26683e3da9 100644 --- a/src/Compiler/Checking/TailCallChecks.fs +++ b/src/Compiler/Checking/TailCallChecks.fs @@ -18,11 +18,12 @@ open FSharp.Compiler.TypeRelations let PostInferenceChecksStackGuardDepth = GetEnvInteger "FSHARP_TailCallChecks" 50 +[] let (|ValUseAtApp|_|) e = match e with | InnerExprPat(Expr.App(funcExpr = InnerExprPat(Expr.Val(valRef = vref; flags = valUseFlags))) | Expr.Val( - valRef = vref; flags = valUseFlags)) -> Some(vref, valUseFlags) - | _ -> None + valRef = vref; flags = valUseFlags)) -> ValueSome(vref, valUseFlags) + | _ -> ValueNone type TailCallReturnType = | MustReturnVoid // indicates "has unit return type and must return void" @@ -67,8 +68,6 @@ type cenv = amap: Import.ImportMap - reportErrors: bool - /// Values in module that have been marked [] mustTailCall: Zset } @@ -139,12 +138,8 @@ let rec mkArgsForAppliedExpr isBaseCall argsl x = | Expr.Op(TOp.Coerce, _, [ f ], _) -> mkArgsForAppliedExpr isBaseCall argsl f | _ -> [] -/// Check an expression, where the expression is in a position where byrefs can be generated -let rec CheckExprNoByrefs cenv (tailCall: TailCall) expr = - CheckExpr cenv expr PermitByRefExpr.No tailCall - /// Check an expression, warn if it's attributed with TailCall but our analysis concludes it's not a valid tail call -and CheckForNonTailRecCall (cenv: cenv) expr (tailCall: TailCall) = +let CheckForNonTailRecCall (cenv: cenv) expr (tailCall: TailCall) = let g = cenv.g let expr = stripExpr expr let expr = stripDebugPoints expr @@ -152,68 +147,70 @@ and CheckForNonTailRecCall (cenv: cenv) expr (tailCall: TailCall) = match expr with | Expr.App(f, _fty, _tyargs, argsl, m) -> - if cenv.reportErrors then - if cenv.g.langVersion.SupportsFeature LanguageFeature.WarningWhenTailRecAttributeButNonTailRecUsage then - match f with - | ValUseAtApp(vref, valUseFlags) when cenv.mustTailCall.Contains vref.Deref -> - - let canTailCall = - match tailCall with - | TailCall.No -> // an upper level has already decided that this is not in a tailcall position - false - | TailCall.Yes returnType -> - if vref.IsMemberOrModuleBinding && vref.ValReprInfo.IsSome then - let topValInfo = vref.ValReprInfo.Value - - let nowArgs, laterArgs = - let _, curriedArgInfos, _, _ = - GetValReprTypeInFSharpForm cenv.g topValInfo vref.Type m - - if argsl.Length >= curriedArgInfos.Length then - (List.splitAfter curriedArgInfos.Length argsl) - else - ([], argsl) - - let numEnclosingTypars = CountEnclosingTyparsOfActualParentOfVal vref.Deref - - let _, _, _, returnTy, _ = - GetValReprTypeInCompiledForm g topValInfo numEnclosingTypars vref.Type m - - let _, _, isNewObj, isSuperInit, isSelfInit, _, _, _ = - GetMemberCallInfo cenv.g (vref, valUseFlags) - - let isCCall = - match valUseFlags with - | PossibleConstrainedCall _ -> true - | _ -> false - - let hasByrefArg = nowArgs |> List.exists (tyOfExpr cenv.g >> isByrefTy cenv.g) - - let mustGenerateUnitAfterCall = - (Option.isNone returnTy && returnType <> TailCallReturnType.MustReturnVoid) - - let noTailCallBlockers = - not isNewObj - && not isSuperInit - && not isSelfInit - && not mustGenerateUnitAfterCall - && isNil laterArgs - && not (IsValRefIsDllImport cenv.g vref) - && not isCCall - && not hasByrefArg - - noTailCallBlockers // blockers that will prevent the IL level from emmiting a tail instruction + match f with + | ValUseAtApp(vref, valUseFlags) when cenv.mustTailCall.Contains vref.Deref -> + + let canTailCall = + match tailCall with + | TailCall.No -> // an upper level has already decided that this is not in a tailcall position + false + | TailCall.Yes returnType -> + if vref.IsMemberOrModuleBinding && vref.ValReprInfo.IsSome then + let topValInfo = vref.ValReprInfo.Value + + let nowArgs, laterArgs = + let _, curriedArgInfos, _, _ = + GetValReprTypeInFSharpForm cenv.g topValInfo vref.Type m + + if argsl.Length >= curriedArgInfos.Length then + (List.splitAfter curriedArgInfos.Length argsl) else - true + ([], argsl) - // warn if we call inside of recursive scope in non-tail-call manner/with tail blockers. See - // ``Warn successfully in match clause`` - // ``Warn for byref parameters`` - if not canTailCall then - warning (Error(FSComp.SR.chkNotTailRecursive vref.DisplayName, m)) - | _ -> () + let numEnclosingTypars = CountEnclosingTyparsOfActualParentOfVal vref.Deref + + let _, _, _, returnTy, _ = + GetValReprTypeInCompiledForm g topValInfo numEnclosingTypars vref.Type m + + let _, _, isNewObj, isSuperInit, isSelfInit, _, _, _ = + GetMemberCallInfo cenv.g (vref, valUseFlags) + + let isCCall = + match valUseFlags with + | PossibleConstrainedCall _ -> true + | _ -> false + + let hasByrefArg = nowArgs |> List.exists (tyOfExpr cenv.g >> isByrefTy cenv.g) + + let mustGenerateUnitAfterCall = + (Option.isNone returnTy && returnType <> TailCallReturnType.MustReturnVoid) + + let noTailCallBlockers = + not isNewObj + && not isSuperInit + && not isSelfInit + && not mustGenerateUnitAfterCall + && isNil laterArgs + && not (IsValRefIsDllImport cenv.g vref) + && not isCCall + && not hasByrefArg + + noTailCallBlockers // blockers that will prevent the IL level from emmiting a tail instruction + else + true + + // warn if we call inside of recursive scope in non-tail-call manner/with tail blockers. See + // ``Warn successfully in match clause`` + // ``Warn for byref parameters`` + if not canTailCall then + warning (Error(FSComp.SR.chkNotTailRecursive vref.DisplayName, m)) + | _ -> () | _ -> () +/// Check an expression, where the expression is in a position where byrefs can be generated +let rec CheckExprNoByrefs cenv (tailCall: TailCall) expr = + CheckExpr cenv expr PermitByRefExpr.No tailCall + /// Check call arguments, including the return argument. and CheckCall cenv args ctxts (tailCall: TailCall) = // detect CPS-like expressions @@ -542,12 +539,10 @@ and CheckExprOp cenv (op, tyargs, args, m) ctxt : unit = | TOp.ValFieldSet _rf, _, [ _arg1; _arg2 ] -> () | TOp.Coerce, [ tgtTy; srcTy ], [ x ] -> - let tailCall = TailCall.YesFromExpr cenv.g x - if TypeDefinitelySubsumesTypeNoCoercion 0 g cenv.amap m tgtTy srcTy then - CheckExpr cenv x ctxt tailCall + CheckExpr cenv x ctxt TailCall.No else - CheckExprNoByrefs cenv tailCall x + CheckExprNoByrefs cenv TailCall.No x | TOp.Reraise, [ _ty1 ], [] -> () @@ -729,10 +724,7 @@ and CheckBindings cenv binds = let CheckModuleBinding cenv (isRec: bool) (TBind _ as bind) = // warn for non-rec functions which have the attribute - if - cenv.reportErrors - && cenv.g.langVersion.SupportsFeature LanguageFeature.WarningWhenTailCallAttrOnNonRec - then + if cenv.g.langVersion.SupportsFeature LanguageFeature.WarningWhenTailCallAttrOnNonRec then let isNotAFunction = match bind.Var.ValReprInfo with | Some info -> info.HasNoArgs @@ -841,14 +833,17 @@ and CheckModuleSpec cenv isRec mbind = | ModuleOrNamespaceBinding.Module(_mspec, rhs) -> CheckDefnInModule cenv rhs -let CheckImplFile (g, amap, reportErrors, implFileContents) = - let cenv = - { - g = g - reportErrors = reportErrors - stackGuard = StackGuard(PostInferenceChecksStackGuardDepth, "CheckImplFile") - amap = amap - mustTailCall = Zset.empty valOrder - } - - CheckDefnInModule cenv implFileContents +let CheckImplFile (g: TcGlobals, amap, reportErrors, implFileContents) = + if + reportErrors + && g.langVersion.SupportsFeature LanguageFeature.WarningWhenTailRecAttributeButNonTailRecUsage + then + let cenv = + { + g = g + stackGuard = StackGuard(PostInferenceChecksStackGuardDepth, "CheckImplFile") + amap = amap + mustTailCall = Zset.empty valOrder + } + + CheckDefnInModule cenv implFileContents diff --git a/src/Compiler/Checking/infos.fs b/src/Compiler/Checking/infos.fs index d7fd858d957..c90d49f3dcc 100644 --- a/src/Compiler/Checking/infos.fs +++ b/src/Compiler/Checking/infos.fs @@ -702,7 +702,7 @@ type MethInfo = member x.DebuggerDisplayName = match x with | ILMeth(_, y, _) -> y.DeclaringTyconRef.DisplayNameWithStaticParametersAndUnderscoreTypars + "::" + y.ILName - | FSMeth(_, AbbrevOrAppTy tcref, vref, _) -> tcref.DisplayNameWithStaticParametersAndUnderscoreTypars + "::" + vref.LogicalName + | FSMeth(_, AbbrevOrAppTy(tcref, _), vref, _) -> tcref.DisplayNameWithStaticParametersAndUnderscoreTypars + "::" + vref.LogicalName | FSMeth(_, _, vref, _) -> "??::" + vref.LogicalName #if !NO_TYPEPROVIDERS | ProvidedMeth(_, mi, _, m) -> "ProvidedMeth: " + mi.PUntaint((fun mi -> mi.Name), m) @@ -2391,18 +2391,19 @@ let SettersOfPropInfos (pinfos: PropInfo list) = pinfos |> List.choose (fun pinf let GettersOfPropInfos (pinfos: PropInfo list) = pinfos |> List.choose (fun pinfo -> if pinfo.HasGetter then Some(pinfo.GetterMethod, Some pinfo) else None) +[] let (|DifferentGetterAndSetter|_|) (pinfo: PropInfo) = if not (pinfo.HasGetter && pinfo.HasSetter) then - None + ValueNone else match pinfo.GetterMethod.ArbitraryValRef, pinfo.SetterMethod.ArbitraryValRef with | Some getValRef, Some setValRef -> if getValRef.Accessibility <> setValRef.Accessibility then - Some (getValRef, setValRef) + ValueSome (getValRef, setValRef) else match getValRef.ValReprInfo with | Some getValReprInfo when // Getter has an index parameter - getValReprInfo.TotalArgCount > 1 -> Some (getValRef, setValRef) - | _ -> None - | _ -> None \ No newline at end of file + getValReprInfo.TotalArgCount > 1 -> ValueSome (getValRef, setValRef) + | _ -> ValueNone + | _ -> ValueNone \ No newline at end of file diff --git a/src/Compiler/Checking/infos.fsi b/src/Compiler/Checking/infos.fsi index 7e1ee813ca8..a2b178d92ac 100644 --- a/src/Compiler/Checking/infos.fsi +++ b/src/Compiler/Checking/infos.fsi @@ -1101,4 +1101,5 @@ val SettersOfPropInfos: pinfos: PropInfo list -> (MethInfo * PropInfo option) li val GettersOfPropInfos: pinfos: PropInfo list -> (MethInfo * PropInfo option) list -val (|DifferentGetterAndSetter|_|): pinfo: PropInfo -> (ValRef * ValRef) option +[] +val (|DifferentGetterAndSetter|_|): pinfo: PropInfo -> (ValRef * ValRef) voption diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index d1d3c9f85c8..e301813edae 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -92,7 +92,7 @@ let ChooseParamNames fieldNamesAndTypes = ilParamName, ilFieldName, ilPropType) /// Approximation for purposes of optimization and giving a warning when compiling definition-only files as EXEs -let rec CheckCodeDoesSomething (code: ILCode) = +let CheckCodeDoesSomething (code: ILCode) = code.Instrs |> Array.exists (function | AI_ldnull @@ -476,7 +476,7 @@ let CompLocForPrivateImplementationDetails cloc = } /// Compute an ILTypeRef for a CompilationLocation -let rec TypeRefForCompLoc cloc = +let TypeRefForCompLoc cloc = match cloc.Enclosing with | [] -> mkILTyRef (cloc.Scope, TypeNameForPrivateImplementationDetails cloc) | [ h ] -> diff --git a/src/Compiler/Driver/CreateILModule.fs b/src/Compiler/Driver/CreateILModule.fs index 7bc9be6b373..04cda7f6c3f 100644 --- a/src/Compiler/Driver/CreateILModule.fs +++ b/src/Compiler/Driver/CreateILModule.fs @@ -409,7 +409,7 @@ module MainModuleBuilder = yield! codegenResults.ilAssemAttrs if Option.isSome pdbfile then - tcGlobals.mkDebuggableAttributeV2 (tcConfig.jitTracking, disableJitOptimizations, false (* enableEnC *) ) + tcGlobals.mkDebuggableAttributeV2 (tcConfig.jitTracking, disableJitOptimizations) yield! reflectedDefinitionAttrs ] diff --git a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs index 9300385b483..11ba984ca51 100644 --- a/src/Compiler/Driver/GraphChecking/DependencyResolution.fs +++ b/src/Compiler/Driver/GraphChecking/DependencyResolution.fs @@ -1,7 +1,6 @@ module internal FSharp.Compiler.GraphChecking.DependencyResolution open FSharp.Compiler.Syntax -open Internal.Utilities.Library /// Find a path from a starting TrieNode and return the end node or None let queryTriePartial (trie: TrieNode) (path: LongIdentifier) : TrieNode option = @@ -118,6 +117,20 @@ let rec processStateEntry (trie: TrieNode) (state: FileContentQueryState) (entry FoundDependencies = foundDependencies } + | ModuleName name -> + // We need to check if the module name is a hit in the Trie. + let state' = + let queryResult = queryTrie trie [ name ] + processIdentifier queryResult state + + match state.OwnNamespace with + | None -> state' + | Some ns -> + // If there we currently have our own namespace, + // the combination of that namespace + module name should be checked as well. + let queryResult = queryTrieDual trie ns [ name ] + processIdentifier queryResult state' + /// /// For a given file's content, collect all missing ("ghost") file dependencies that the core resolution algorithm didn't return, /// but are required to satisfy the type-checker. diff --git a/src/Compiler/Driver/GraphChecking/FileContentMapping.fs b/src/Compiler/Driver/GraphChecking/FileContentMapping.fs index bfdaf4beea8..15355de5a30 100644 --- a/src/Compiler/Driver/GraphChecking/FileContentMapping.fs +++ b/src/Compiler/Driver/GraphChecking/FileContentMapping.fs @@ -9,15 +9,19 @@ type Continuations = ((FileContentEntry list -> FileContentEntry list) -> FileCo let collectFromOption (mapping: 'T -> 'U list) (t: 'T option) : 'U list = List.collect mapping (Option.toList t) let longIdentToPath (skipLast: bool) (longId: LongIdent) : LongIdentifier = - if skipLast then - List.take (longId.Length - 1) longId - else - longId + match skipLast, longId with + | true, _ :: _ -> List.take (longId.Length - 1) longId + | _ -> longId |> List.map (fun ident -> ident.idText) let synLongIdentToPath (skipLast: bool) (synLongIdent: SynLongIdent) = longIdentToPath skipLast synLongIdent.LongIdent +/// In some rare cases we are interested in the name of a single Ident. +/// For example `nameof ModuleName` in expressions or patterns. +let visitIdentAsPotentialModuleName (moduleNameIdent: Ident) = + FileContentEntry.ModuleName moduleNameIdent.idText + let visitSynLongIdent (lid: SynLongIdent) : FileContentEntry list = visitLongIdent lid.LongIdent let visitLongIdent (lid: LongIdent) = @@ -51,7 +55,7 @@ let visitSynModuleDecl (decl: SynModuleDecl) : FileContentEntry list = | SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = [ ident ]; attributes = attributes); decls = decls) -> yield! visitSynAttributes attributes yield FileContentEntry.NestedModule(ident.idText, List.collect visitSynModuleDecl decls) - | SynModuleDecl.NestedModule _ -> failwith "A nested module cannot have multiple identifiers" + | SynModuleDecl.NestedModule _ -> () // A nested module cannot have multiple identifiers. This will already be a parse error, but we could be working with recovered syntax tree | SynModuleDecl.Let(bindings = bindings) -> yield! List.collect visitBinding bindings | SynModuleDecl.Types(typeDefns = typeDefns) -> yield! List.collect visitSynTypeDefn typeDefns | SynModuleDecl.HashDirective _ -> () @@ -75,7 +79,7 @@ let visitSynModuleSigDecl (md: SynModuleSigDecl) = | SynModuleSigDecl.NestedModule(moduleInfo = SynComponentInfo(longId = [ ident ]; attributes = attributes); moduleDecls = decls) -> yield! visitSynAttributes attributes yield FileContentEntry.NestedModule(ident.idText, List.collect visitSynModuleSigDecl decls) - | SynModuleSigDecl.NestedModule _ -> failwith "A nested module cannot have multiple identifiers" + | SynModuleSigDecl.NestedModule _ -> () // A nested module cannot have multiple identifiers. This will already be a parse error, but we could be working with recovered syntax tree | SynModuleSigDecl.ModuleAbbrev(longId = longId) -> yield! visitLongIdentForModuleAbbrev longId | SynModuleSigDecl.Val(valSig, _) -> yield! visitSynValSig valSig | SynModuleSigDecl.Types(types = types) -> yield! List.collect visitSynTypeDefnSig types @@ -200,7 +204,7 @@ let visitSynMemberDefn (md: SynMemberDefn) : FileContentEntry list = | SynMemberDefn.GetSetMember(memberDefnForGet, memberDefnForSet, _, _) -> yield! collectFromOption visitBinding memberDefnForGet yield! collectFromOption visitBinding memberDefnForSet - | SynMemberDefn.ImplicitCtor(ctorArgs = ctorArgs) -> yield! visitSynSimplePats ctorArgs + | SynMemberDefn.ImplicitCtor(ctorArgs = pat) -> yield! visitPat pat | SynMemberDefn.ImplicitInherit(inheritType, inheritArgs, _, _) -> yield! visitSynType inheritType yield! visitSynExpr inheritArgs @@ -302,9 +306,28 @@ let visitSynTypeConstraint (tc: SynTypeConstraint) : FileContentEntry list = | SynTypeConstraint.WhereTyparIsEnum(typeArgs = typeArgs) -> List.collect visitSynType typeArgs | SynTypeConstraint.WhereTyparIsDelegate(typeArgs = typeArgs) -> List.collect visitSynType typeArgs +[] +let inline (|NameofIdent|_|) (ident: Ident) = + if ident.idText = "nameof" then ValueSome() else ValueNone + +/// Special case of `nameof Module` type of expression +let (|NameofExpr|_|) (e: SynExpr) = + let rec stripParen (e: SynExpr) = + match e with + | SynExpr.Paren(expr = expr) -> stripParen expr + | _ -> e + + match e with + | SynExpr.App(flag = ExprAtomicFlag.NonAtomic; isInfix = false; funcExpr = SynExpr.Ident NameofIdent; argExpr = moduleNameExpr) -> + match stripParen moduleNameExpr with + | SynExpr.Ident moduleNameIdent -> Some moduleNameIdent + | _ -> None + | _ -> None + let visitSynExpr (e: SynExpr) : FileContentEntry list = let rec visit (e: SynExpr) (continuation: FileContentEntry list -> FileContentEntry list) : FileContentEntry list = match e with + | NameofExpr moduleNameIdent -> continuation [ visitIdentAsPotentialModuleName moduleNameIdent ] | SynExpr.Const _ -> continuation [] | SynExpr.Paren(expr = expr) -> visit expr continuation | SynExpr.Quote(operator = operator; quotedExpr = quotedExpr) -> @@ -389,7 +412,7 @@ let visitSynExpr (e: SynExpr) : FileContentEntry list = | SynExpr.IfThenElse(ifExpr = ifExpr; thenExpr = thenExpr; elseExpr = elseExpr) -> let continuations = List.map visit (ifExpr :: thenExpr :: Option.toList elseExpr) Continuation.concatenate continuations continuation - | SynExpr.Typar _ -> continuation [] + | SynExpr.Typar _ | SynExpr.Ident _ -> continuation [] | SynExpr.LongIdent(longDotId = longDotId) -> continuation (visitSynLongIdent longDotId) | SynExpr.LongIdentSet(longDotId, expr, _) -> visit expr (fun nodes -> visitSynLongIdent longDotId @ nodes |> continuation) @@ -517,9 +540,29 @@ let visitSynExpr (e: SynExpr) : FileContentEntry list = visit e id +/// Special case of `| nameof Module ->` type of pattern +let (|NameofPat|_|) (pat: SynPat) = + let rec stripPats p = + match p with + | SynPat.Paren(pat = pat) -> stripPats pat + | _ -> p + + match pat with + | SynPat.LongIdent(longDotId = SynLongIdent(id = [ NameofIdent ]); typarDecls = None; argPats = SynArgPats.Pats [ moduleNamePat ]) -> + match stripPats moduleNamePat with + | SynPat.LongIdent( + longDotId = SynLongIdent.SynLongIdent(id = [ moduleNameIdent ]; dotRanges = []; trivia = [ None ]) + extraId = None + typarDecls = None + argPats = SynArgPats.Pats [] + accessibility = None) -> Some moduleNameIdent + | _ -> None + | _ -> None + let visitPat (p: SynPat) : FileContentEntry list = let rec visit (p: SynPat) (continuation: FileContentEntry list -> FileContentEntry list) : FileContentEntry list = match p with + | NameofPat moduleNameIdent -> continuation [ visitIdentAsPotentialModuleName moduleNameIdent ] | SynPat.Paren(pat = pat) -> visit pat continuation | SynPat.Typed(pat = pat; targetType = t) -> visit pat (fun nodes -> nodes @ visitSynType t) | SynPat.Const _ -> continuation [] diff --git a/src/Compiler/Driver/GraphChecking/Graph.fs b/src/Compiler/Driver/GraphChecking/Graph.fs index dd51ea190a2..dbe4c6b6cc7 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fs +++ b/src/Compiler/Driver/GraphChecking/Graph.fs @@ -27,26 +27,43 @@ module internal Graph = |> Array.map (fun (KeyValue(k, v)) -> k, v) |> readOnlyDict - let transitive<'Node when 'Node: equality> (graph: Graph<'Node>) : Graph<'Node> = - /// Find transitive dependencies of a single node. - let transitiveDeps (node: 'Node) = - let visited = HashSet<'Node>() + let nodes (graph: Graph<'Node>) : Set<'Node> = + graph.Values |> Seq.collect id |> Seq.append graph.Keys |> Set + + /// Find transitive dependencies of a single node. + let transitiveDeps (node: 'Node) (graph: Graph<'Node>) = + let visited = HashSet<'Node>() - let rec dfs (node: 'Node) = - graph[node] - // Add direct dependencies. - // Use HashSet.Add return value semantics to filter out those that were added previously. - |> Array.filter visited.Add - |> Array.iter dfs + let rec dfs (node: 'Node) = + graph[node] + // Add direct dependencies. + // Use HashSet.Add return value semantics to filter out those that were added previously. + |> Array.filter visited.Add + |> Array.iter dfs - dfs node - visited |> Seq.toArray + dfs node + visited |> Seq.toArray + let transitive<'Node when 'Node: equality> (graph: Graph<'Node>) : Graph<'Node> = graph.Keys |> Seq.toArray - |> Array.Parallel.map (fun node -> node, transitiveDeps node) + |> Array.Parallel.map (fun node -> node, graph |> transitiveDeps node) |> readOnlyDict + // TODO: optimize + /// Get subgraph of the given graph that contains only nodes that are reachable from the given node. + let subGraphFor node graph = + let allDeps = graph |> transitiveDeps node + let relevant n = n = node || allDeps |> Array.contains n + + graph + |> Seq.choose (fun (KeyValue(src, deps)) -> + if relevant src then + Some(src, deps |> Array.filter relevant) + else + None) + |> make + /// Create a reverse of the graph let reverse (originalGraph: Graph<'Node>) : Graph<'Node> = originalGraph @@ -69,7 +86,7 @@ module internal Graph = let print (graph: Graph<'Node>) : unit = printCustom graph (fun node -> node.ToString()) - let serialiseToMermaid path (graph: Graph) = + let serialiseToMermaid (graph: Graph) = let sb = StringBuilder() let appendLine (line: string) = sb.AppendLine(line) |> ignore @@ -84,8 +101,10 @@ module internal Graph = appendLine $" %i{idx} --> %i{depIdx}" appendLine "```" + sb.ToString() + let writeMermaidToFile path (graph: Graph) = use out = FileSystem.OpenFileForWriteShim(path, fileMode = System.IO.FileMode.Create) - out.WriteAllText(sb.ToString()) + graph |> serialiseToMermaid |> out.WriteAllText diff --git a/src/Compiler/Driver/GraphChecking/Graph.fsi b/src/Compiler/Driver/GraphChecking/Graph.fsi index 95542470d8a..a93e429d2fe 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fsi +++ b/src/Compiler/Driver/GraphChecking/Graph.fsi @@ -10,12 +10,18 @@ module internal Graph = /// Build the graph. val make: nodeDeps: seq<'Node * 'Node array> -> Graph<'Node> when 'Node: equality val map<'T, 'U when 'U: equality> : f: ('T -> 'U) -> graph: Graph<'T> -> Graph<'U> + /// Get all nodes of the graph. + val nodes: graph: Graph<'Node> -> Set<'Node> /// Create a transitive closure of the graph in O(n^2) time (but parallelize it). /// The resulting graph contains edge A -> C iff the input graph contains a (directed) non-zero length path from A to C. val transitive<'Node when 'Node: equality> : graph: Graph<'Node> -> Graph<'Node> + /// Get a sub-graph of the graph containing only the nodes reachable from the given node. + val subGraphFor: node: 'Node -> graph: Graph<'Node> -> Graph<'Node> when 'Node: equality /// Create a reverse of the graph. val reverse<'Node when 'Node: equality> : originalGraph: Graph<'Node> -> Graph<'Node> /// Print the contents of the graph to the standard output. val print: graph: Graph<'Node> -> unit + /// Create a simple Mermaid graph + val serialiseToMermaid: graph: Graph -> string /// Create a simple Mermaid graph and save it under the path specified. - val serialiseToMermaid: path: string -> graph: Graph -> unit + val writeMermaidToFile: path: string -> graph: Graph -> unit diff --git a/src/Compiler/Driver/GraphChecking/GraphProcessing.fs b/src/Compiler/Driver/GraphChecking/GraphProcessing.fs index 47993e00862..37ecc35041d 100644 --- a/src/Compiler/Driver/GraphChecking/GraphProcessing.fs +++ b/src/Compiler/Driver/GraphChecking/GraphProcessing.fs @@ -1,6 +1,9 @@ module internal FSharp.Compiler.GraphChecking.GraphProcessing open System.Threading +open FSharp.Compiler.GraphChecking +open System.Threading.Tasks +open System /// Information about the node in a graph, describing its relation with other nodes. type NodeInfo<'Item> = @@ -32,6 +35,9 @@ type ProcessedNode<'Item, 'Result> = Result: 'Result } +type GraphProcessingException(msg, ex: System.Exception) = + inherit exn(msg, ex) + let processGraph<'Item, 'Result when 'Item: equality and 'Item: comparison> (graph: Graph<'Item>) (work: ('Item -> ProcessedNode<'Item, 'Result>) -> NodeInfo<'Item> -> 'Result) @@ -150,7 +156,7 @@ let processGraph<'Item, 'Result when 'Item: equality and 'Item: comparison> // If we stopped early due to an exception, reraise it. match getExn () with | None -> () - | Some(item, ex) -> raise (System.Exception($"Encountered exception when processing item '{item}'", ex)) + | Some(item, ex) -> raise (GraphProcessingException($"Encountered exception when processing item '{item}'", ex)) // All calculations succeeded - extract the results and sort in input order. nodes.Values @@ -162,3 +168,135 @@ let processGraph<'Item, 'Result when 'Item: equality and 'Item: comparison> node.Info.Item, result) |> Seq.sortBy fst |> Seq.toArray + +let processGraphAsync<'Item, 'Result when 'Item: equality and 'Item: comparison> + (graph: Graph<'Item>) + (work: ('Item -> ProcessedNode<'Item, 'Result>) -> NodeInfo<'Item> -> Async<'Result>) + : Async<('Item * 'Result)[]> = + async { + let transitiveDeps = graph |> Graph.transitive + let dependants = graph |> Graph.reverse + // Cancellation source used to signal either an exception in one of the items or end of processing. + let! parentCt = Async.CancellationToken + use localCts = new CancellationTokenSource() + + let completionSignal = TaskCompletionSource() + + use _ = parentCt.Register(fun () -> completionSignal.TrySetCanceled() |> ignore) + + use cts = CancellationTokenSource.CreateLinkedTokenSource(parentCt, localCts.Token) + + let makeNode (item: 'Item) : GraphNode<'Item, 'Result> = + let info = + let exists = graph.ContainsKey item + + if + not exists + || not (transitiveDeps.ContainsKey item) + || not (dependants.ContainsKey item) + then + printfn $"Unexpected inconsistent state of the graph for item '{item}'" + + { + Item = item + Deps = graph[item] + TransitiveDeps = transitiveDeps[item] + Dependants = dependants[item] + } + + { + Info = info + Result = None + ProcessedDepsCount = IncrementableInt(0) + } + + let nodes = graph.Keys |> Seq.map (fun item -> item, makeNode item) |> readOnlyDict + + let lookupMany items = + items |> Array.map (fun item -> nodes[item]) + + let leaves = + nodes.Values |> Seq.filter (fun n -> n.Info.Deps.Length = 0) |> Seq.toArray + + let getItemPublicNode item = + let node = nodes[item] + + { + ProcessedNode.Info = node.Info + ProcessedNode.Result = + node.Result + |> Option.defaultWith (fun () -> failwith $"Results for item '{node.Info.Item}' are not yet available") + } + + let processedCount = IncrementableInt(0) + + let handleExn (item, ex: exn) = + try + localCts.Cancel() + with :? ObjectDisposedException -> + // If it's disposed already, it means that the processing has already finished, most likely due to cancellation or failure in another node. + () + + match ex with + | :? OperationCanceledException -> completionSignal.TrySetCanceled() + | _ -> + completionSignal.TrySetException( + GraphProcessingException($"[*] Encountered exception when processing item '{item}': {ex.Message}", ex) + ) + |> ignore + + let incrementProcessedNodesCount () = + if processedCount.Increment() = nodes.Count then + completionSignal.TrySetResult() |> ignore + + let rec queueNode node = + Async.Start( + async { + let! res = processNode node |> Async.Catch + + match res with + | Choice1Of2() -> () + | Choice2Of2 ex -> handleExn (node.Info.Item, ex) + }, + cts.Token + ) + + and processNode (node: GraphNode<'Item, 'Result>) : Async = + async { + + let info = node.Info + + let! singleRes = work getItemPublicNode info + node.Result <- Some singleRes + + let unblockedDependants = + node.Info.Dependants + |> lookupMany + // For every dependant, increment its number of processed dependencies, + // and filter dependants which now have all dependencies processed (but didn't before). + |> Array.filter (fun dependant -> + let pdc = dependant.ProcessedDepsCount.Increment() + // Note: We cannot read 'dependant.ProcessedDepsCount' again to avoid returning the same item multiple times. + pdc = dependant.Info.Deps.Length) + + unblockedDependants |> Array.iter queueNode + incrementProcessedNodesCount () + } + + leaves |> Array.iter queueNode + + // Wait for end of processing, an exception, or an external cancellation request. + do! completionSignal.Task |> Async.AwaitTask + + // All calculations succeeded - extract the results and sort in input order. + return + nodes.Values + |> Seq.map (fun node -> + let result = + node.Result + |> Option.defaultWith (fun () -> failwith $"Unexpected lack of result for item '{node.Info.Item}'") + + node.Info.Item, result) + |> Seq.sortBy fst + |> Seq.toArray + } diff --git a/src/Compiler/Driver/GraphChecking/GraphProcessing.fsi b/src/Compiler/Driver/GraphChecking/GraphProcessing.fsi index cb9a95a59f8..585daa52fd7 100644 --- a/src/Compiler/Driver/GraphChecking/GraphProcessing.fsi +++ b/src/Compiler/Driver/GraphChecking/GraphProcessing.fsi @@ -15,6 +15,10 @@ type ProcessedNode<'Item, 'Result> = { Info: NodeInfo<'Item> Result: 'Result } +type GraphProcessingException = + inherit exn + new: msg: string * ex: System.Exception -> GraphProcessingException + /// /// A generic method to generate results for a graph of work items in parallel. /// Processes leaves first, and after each node has been processed, schedules any now unblocked dependants. @@ -33,3 +37,8 @@ val processGraph<'Item, 'Result when 'Item: equality and 'Item: comparison> : work: (('Item -> ProcessedNode<'Item, 'Result>) -> NodeInfo<'Item> -> 'Result) -> parentCt: CancellationToken -> ('Item * 'Result)[] + +val processGraphAsync<'Item, 'Result when 'Item: equality and 'Item: comparison> : + graph: Graph<'Item> -> + work: (('Item -> ProcessedNode<'Item, 'Result>) -> NodeInfo<'Item> -> Async<'Result>) -> + Async<('Item * 'Result)[]> diff --git a/src/Compiler/Driver/GraphChecking/TrieMapping.fs b/src/Compiler/Driver/GraphChecking/TrieMapping.fs index ff7b32e5640..add261d570c 100644 --- a/src/Compiler/Driver/GraphChecking/TrieMapping.fs +++ b/src/Compiler/Driver/GraphChecking/TrieMapping.fs @@ -123,7 +123,7 @@ let processSynModuleOrNamespace<'Decl> // Only the last node can be a module, depending on the SynModuleOrNamespaceKind. let rec visit continuation (xs: LongIdent) = match xs with - | [] -> failwith "should not be empty" + | [] -> ImmutableDictionary.Empty |> continuation | [ finalPart ] -> let name = finalPart.idText diff --git a/src/Compiler/Driver/GraphChecking/Types.fs b/src/Compiler/Driver/GraphChecking/Types.fs index 00538b6e599..c667a573f69 100644 --- a/src/Compiler/Driver/GraphChecking/Types.fs +++ b/src/Compiler/Driver/GraphChecking/Types.fs @@ -73,6 +73,9 @@ type internal FileContentEntry = /// Being explicit about nested modules allows for easier reasoning what namespaces (paths) are open. /// We can scope an `OpenStatement` to the everything that is happening inside the nested module. | NestedModule of name: string * nestedContent: FileContentEntry list + /// A single identifier that could be the name of a module. + /// Example use-case: `let x = nameof Foo` where `Foo` is a module. + | ModuleName of name: Identifier type internal FileContent = { diff --git a/src/Compiler/Driver/GraphChecking/Types.fsi b/src/Compiler/Driver/GraphChecking/Types.fsi index 468ef65889c..096719b6be7 100644 --- a/src/Compiler/Driver/GraphChecking/Types.fsi +++ b/src/Compiler/Driver/GraphChecking/Types.fsi @@ -67,6 +67,9 @@ type internal FileContentEntry = /// Being explicit about nested modules allows for easier reasoning what namespaces (paths) are open. /// For example we can limit the scope of an `OpenStatement` to symbols defined inside the nested module. | NestedModule of name: string * nestedContent: FileContentEntry list + /// A single identifier that could be the name of a module. + /// Example use-case: `let x = nameof Foo` where `Foo` is a module. + | ModuleName of name: Identifier /// File identifiers and its content extract for dependency resolution type internal FileContent = diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index a9fc59b66d3..5a23c95ca7b 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1483,7 +1483,7 @@ let CheckOneInputWithCallback prefixPathOpt, tcSink, tcState: TcState, - inp: ParsedInput, + input: ParsedInput, _skipImplIfSigExists: bool): (unit -> bool) * TcConfig * TcImports * TcGlobals * LongIdent option * TcResultsSink * TcState * ParsedInput * bool) : Cancellable> = @@ -1491,7 +1491,7 @@ let CheckOneInputWithCallback try CheckSimulateException tcConfig - let m = inp.Range + let m = input.Range let amap = tcImports.GetImportMap() let conditionalDefines = @@ -1500,7 +1500,7 @@ let CheckOneInputWithCallback else Some tcConfig.conditionalDefines - match inp with + match input with | ParsedInput.SigFile file -> let qualNameOfFile = file.QualifiedName @@ -1740,6 +1740,43 @@ module private TypeCheckingGraphProcessing = finalFileResults, state +let TransformDependencyGraph (graph: Graph, filePairs: FilePairMap) = + let mkArtificialImplFile n = NodeToTypeCheck.ArtificialImplFile n + let mkPhysicalFile n = NodeToTypeCheck.PhysicalFile n + + /// Map any signature dependencies to the ArtificialImplFile counterparts, + /// unless the signature dependency is the backing file of the current (implementation) file. + let mapDependencies idx deps = + Array.map + (fun dep -> + if filePairs.IsSignature dep then + let implIdx = filePairs.GetImplementationIndex dep + + if implIdx = idx then + // This is the matching signature for the implementation. + // Retain the direct dependency onto the signature file. + mkPhysicalFile dep + else + mkArtificialImplFile dep + else + mkPhysicalFile dep) + deps + + // Transform the graph to include ArtificialImplFile nodes when necessary. + graph + |> Seq.collect (fun (KeyValue(fileIdx, deps)) -> + if filePairs.IsSignature fileIdx then + // Add an additional ArtificialImplFile node for the signature file. + [| + // Mark the current file as physical and map the dependencies. + mkPhysicalFile fileIdx, mapDependencies fileIdx deps + // Introduce a new node that depends on the signature. + mkArtificialImplFile fileIdx, [| mkPhysicalFile fileIdx |] + |] + else + [| mkPhysicalFile fileIdx, mapDependencies fileIdx deps |]) + |> Graph.make + /// Constructs a file dependency graph and type-checks the files in parallel where possible. let CheckMultipleInputsUsingGraphMode ((ctok, checkForErrors, tcConfig: TcConfig, tcImports: TcImports, tcGlobals, prefixPathOpt, tcState, eagerFormat, inputs): @@ -1768,42 +1805,7 @@ let CheckMultipleInputsUsingGraphMode let filePairs = FilePairMap(sourceFiles) let graph, trie = DependencyResolution.mkGraph filePairs sourceFiles - let nodeGraph = - let mkArtificialImplFile n = NodeToTypeCheck.ArtificialImplFile n - let mkPhysicalFile n = NodeToTypeCheck.PhysicalFile n - - /// Map any signature dependencies to the ArtificialImplFile counterparts, - /// unless the signature dependency is the backing file of the current (implementation) file. - let mapDependencies idx deps = - Array.map - (fun dep -> - if filePairs.IsSignature dep then - let implIdx = filePairs.GetImplementationIndex dep - - if implIdx = idx then - // This is the matching signature for the implementation. - // Retain the direct dependency onto the signature file. - mkPhysicalFile dep - else - mkArtificialImplFile dep - else - mkPhysicalFile dep) - deps - - // Transform the graph to include ArtificialImplFile nodes when necessary. - graph - |> Seq.collect (fun (KeyValue(fileIdx, deps)) -> - if filePairs.IsSignature fileIdx then - // Add an additional ArtificialImplFile node for the signature file. - [| - // Mark the current file as physical and map the dependencies. - mkPhysicalFile fileIdx, mapDependencies fileIdx deps - // Introduce a new node that depends on the signature. - mkArtificialImplFile fileIdx, [| mkPhysicalFile fileIdx |] - |] - else - [| mkPhysicalFile fileIdx, mapDependencies fileIdx deps |]) - |> Graph.make + let nodeGraph = TransformDependencyGraph(graph, filePairs) // Persist the graph to a Mermaid diagram if specified. if tcConfig.typeCheckingConfig.DumpGraph then @@ -1823,7 +1825,7 @@ let CheckMultipleInputsUsingGraphMode .TrimStart([| '\\'; '/' |]) (idx, friendlyFileName)) - |> Graph.serialiseToMermaid graphFile) + |> Graph.writeMermaidToFile graphFile) let _ = ctok // TODO Use it let diagnosticsLogger = DiagnosticsThreadStatics.DiagnosticsLogger diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fsi b/src/Compiler/Driver/ParseAndCheckInputs.fsi index 745afa51be4..875be616a8e 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fsi +++ b/src/Compiler/Driver/ParseAndCheckInputs.fsi @@ -13,12 +13,44 @@ open FSharp.Compiler.CompilerImports open FSharp.Compiler.Diagnostics open FSharp.Compiler.DependencyManager open FSharp.Compiler.DiagnosticsLogger +open FSharp.Compiler.GraphChecking +open FSharp.Compiler.NameResolution open FSharp.Compiler.Syntax open FSharp.Compiler.TcGlobals open FSharp.Compiler.Text open FSharp.Compiler.TypedTree open FSharp.Compiler.UnicodeLexing +/// Auxiliary type for re-using signature information in TcEnvFromImpls. +/// +/// TcState has two typing environments: TcEnvFromSignatures && TcEnvFromImpls +/// When type checking a file, depending on the type (implementation or signature), it will use one of these typing environments (TcEnv). +/// Checking a file will populate the respective TcEnv. +/// +/// When a file has a dependencies, the information of the signature file in case a pair (implementation file backed by a signature) will suffice to type-check that file. +/// Example: if `B.fs` has a dependency on `A`, the information of `A.fsi` is enough for `B.fs` to type-check, on condition that information is available in the TcEnvFromImpls. +/// We introduce a special ArtificialImplFile node in the graph to satisfy this. `B.fs -> [ A.fsi ]` becomes `B.fs -> [ ArtificialImplFile A ]. +/// The `ArtificialImplFile A` node will duplicate the signature information which A.fsi provided earlier. +/// Processing a `ArtificialImplFile` node will add the information from the TcEnvFromSignatures to the TcEnvFromImpls. +/// This means `A` will be known in both TcEnvs and therefor `B.fs` can be type-checked. +/// By doing this, we can speed up the graph processing as type checking a signature file is less expensive than its implementation counterpart. +/// +/// When we need to actually type-check an implementation file backed by a signature, we cannot have the duplicate information of the signature file present in TcEnvFromImpls. +/// Example `A.fs -> [ A.fsi ]`. An implementation file always depends on its signature. +/// Type-checking `A.fs` will add the actual information to TcEnvFromImpls and we do not depend on the `ArtificialImplFile A` for `A.fs`. +/// +/// In order to deal correctly with the `ArtificialImplFile` logic, we need to transform the resolved graph to contain the additional pair nodes. +/// After we have type-checked the graph, we exclude the ArtificialImplFile nodes as they are not actual physical files in our project. +[] +type NodeToTypeCheck = + /// A real physical file in the current project. + /// This can be either an implementation or a signature file. + | PhysicalFile of fileIndex: FileIndex + /// An artificial node that will add the earlier processed signature information to the TcEnvFromImpls. + /// Dependants on this type of node will perceive that a file is known in both TcEnvFromSignatures and TcEnvFromImpls. + /// Even though the actual implementation file was not type-checked. + | ArtificialImplFile of signatureFileIndex: FileIndex + val IsScript: string -> bool val ComputeQualifiedNameOfFileFromUniquePath: range * string list -> QualifiedNameOfFile @@ -131,6 +163,8 @@ type TcState = member CreatesGeneratedProvidedTypes: bool +type PartialResult = TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType + /// Get the initial type checking state for a set of inputs val GetInitialTcState: range * string * TcConfig * TcGlobals * TcImports * TcEnv * OpenDeclaration list -> TcState @@ -151,6 +185,42 @@ val CheckOneInput: input: ParsedInput -> Cancellable<(TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType) * TcState> +val CheckOneInputWithCallback: + node: NodeToTypeCheck -> + checkForErrors: (unit -> bool) * + tcConfig: TcConfig * + tcImports: TcImports * + tcGlobals: TcGlobals * + prefixPathOpt: LongIdent option * + tcSink: TcResultsSink * + tcState: TcState * + input: ParsedInput * + _skipImplIfSigExists: bool -> + Cancellable> + +val AddCheckResultsToTcState: + tcGlobals: TcGlobals * + amap: Import.ImportMap * + hadSig: bool * + prefixPathOpt: LongIdent option * + tcSink: TcResultsSink * + tcImplEnv: TcEnv * + qualNameOfFile: QualifiedNameOfFile * + implFileSigType: ModuleOrNamespaceType -> + tcState: TcState -> + ModuleOrNamespaceType * TcState + +val AddSignatureResultToTcImplEnv: + tcImports: TcImports * + tcGlobals: TcGlobals * + prefixPathOpt: LongIdent option * + tcSink: TcResultsSink * + tcState: TcState * + input: ParsedInput -> + (TcState -> PartialResult * TcState) + +val TransformDependencyGraph: graph: Graph * filePairs: FilePairMap -> Graph + /// Finish the checking of multiple inputs val CheckMultipleInputsFinish: (TcEnv * TopAttribs * 'T option * 'U) list * TcState -> (TcEnv * TopAttribs * 'T list * 'U list) * TcState diff --git a/src/Compiler/Driver/fsc.fs b/src/Compiler/Driver/fsc.fs index eab380bae5c..014ed840222 100644 --- a/src/Compiler/Driver/fsc.fs +++ b/src/Compiler/Driver/fsc.fs @@ -481,14 +481,15 @@ let main1 // See Bug 735819 let lcidFromCodePage = + let thread = Thread.CurrentThread + if (Console.OutputEncoding.CodePage <> 65001) - && (Console.OutputEncoding.CodePage - <> Thread.CurrentThread.CurrentUICulture.TextInfo.OEMCodePage) - && (Console.OutputEncoding.CodePage - <> Thread.CurrentThread.CurrentUICulture.TextInfo.ANSICodePage) + && (Console.OutputEncoding.CodePage <> thread.CurrentUICulture.TextInfo.OEMCodePage) + && (Console.OutputEncoding.CodePage <> thread.CurrentUICulture.TextInfo.ANSICodePage) + && (CultureInfo.InvariantCulture <> thread.CurrentUICulture) then - Thread.CurrentThread.CurrentUICulture <- CultureInfo("en-US") + thread.CurrentUICulture <- CultureInfo("en-US") Some 1033 else None diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index b17d5877ad9..a9fc2692157 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1593,6 +1593,7 @@ featurePreferExtensionMethodOverPlainProperty,"prefer extension method over plai featureWarningIndexedPropertiesGetSetSameType,"Indexed properties getter and setter must have the same type" featureChkTailCallAttrOnNonRec,"Raises warnings if the 'TailCall' attribute is used on non-recursive functions." featureUnionIsPropertiesVisible,"Union case test properties" +featureBooleanReturningAndReturnTypeDirectedPartialActivePattern,"Boolean-returning and return-type-directed partial active patterns" 3354,tcNotAFunctionButIndexerNamedIndexingNotYetEnabled,"This value supports indexing, e.g. '%s.[index]'. The syntax '%s[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation." 3354,tcNotAFunctionButIndexerIndexingNotYetEnabled,"This expression supports indexing, e.g. 'expr.[index]'. The syntax 'expr[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation." 3355,tcNotAnIndexerNamedIndexingNotYetEnabled,"The value '%s' is not a function and does not support index notation." @@ -1740,4 +1741,5 @@ featureReuseSameFieldsInStructUnions,"Share underlying fields in a [] di 3861,chkTailCallAttrOnNonRec,"The TailCall attribute should only be applied to recursive functions." 3862,parsStaticMemberImcompleteSyntax,"Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration." 3863,parsExpectingField,"Expecting record field" -3864,tooManyMethodsInDotNetTypeWritingAssembly,"The type '%s' has too many methods. Found: '%d', maximum: '%d'" \ No newline at end of file +3864,tooManyMethodsInDotNetTypeWritingAssembly,"The type '%s' has too many methods. Found: '%d', maximum: '%d'" +3865,parsOnlySimplePatternsAreAllowedInConstructors,"Only simple patterns are allowed in primary constructors" \ No newline at end of file diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 687bc269233..dd7b6e25c1a 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -21,6 +21,7 @@ $(OtherFlags) --warnon:3218 $(OtherFlags) --warnon:3390 + true $(IntermediateOutputPath)$(TargetFramework)\ $(IntermediateOutputPath)$(TargetFramework)\ @@ -76,7 +77,8 @@ - + + @@ -90,6 +92,7 @@ FSStrings.resx FSStrings.resources + @@ -124,6 +127,8 @@ + + @@ -144,6 +149,8 @@ + + @@ -156,6 +163,8 @@ + + @@ -475,6 +484,10 @@ + + + + @@ -492,7 +505,12 @@ - + + + + + + diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs new file mode 100644 index 00000000000..b780d91ca74 --- /dev/null +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -0,0 +1,615 @@ +namespace Internal.Utilities.Collections + +open System +open System.Collections.Generic +open System.Diagnostics +open System.IO +open System.Threading +open System.Threading.Tasks + +open FSharp.Compiler +open FSharp.Compiler.BuildGraph +open FSharp.Compiler.Diagnostics +open FSharp.Compiler.DiagnosticsLogger +open System.Runtime.CompilerServices + +[] +module internal Utils = + + /// Return file name with one directory above it + let shortPath path = + let dirPath = Path.GetDirectoryName path + + let dir = + dirPath.Split Path.DirectorySeparatorChar + |> Array.tryLast + |> Option.map (sprintf "%s/") + |> Option.defaultValue "" + + $"{dir}{Path.GetFileName path}" + + let replayDiagnostics (logger: DiagnosticsLogger) = Seq.iter ((<|) logger.DiagnosticSink) + + let (|TaskCancelled|_|) (ex: exn) = + match ex with + | :? System.Threading.Tasks.TaskCanceledException as tce -> Some tce + //| :? System.AggregateException as ae -> + // if ae.InnerExceptions |> Seq.forall (fun e -> e :? System.Threading.Tasks.TaskCanceledException) then + // ae.InnerExceptions |> Seq.tryHead |> Option.map (fun e -> e :?> System.Threading.Tasks.TaskCanceledException) + // else + // None + | _ -> None + +type internal StateUpdate<'TValue> = + | CancelRequest + | OriginatorCanceled + | JobCompleted of 'TValue * (PhasedDiagnostic * FSharpDiagnosticSeverity) list + | JobFailed of exn * (PhasedDiagnostic * FSharpDiagnosticSeverity) list + +type internal MemoizeReply<'TValue> = + | New of CancellationToken + | Existing of Task<'TValue> + +type internal MemoizeRequest<'TValue> = GetOrCompute of NodeCode<'TValue> * CancellationToken + +[] +type internal Job<'TValue> = + | Running of TaskCompletionSource<'TValue> * CancellationTokenSource * NodeCode<'TValue> * DateTime * ResizeArray + | Completed of 'TValue * (PhasedDiagnostic * FSharpDiagnosticSeverity) list + | Canceled of DateTime + | Failed of DateTime * exn // TODO: probably we don't need to keep this + + member this.DebuggerDisplay = + match this with + | Running(_, cts, _, ts, _) -> + let cancellation = + if cts.IsCancellationRequested then + " ! Cancellation Requested" + else + "" + + $"Running since {ts.ToShortTimeString()}{cancellation}" + | Completed(value, diags) -> $"Completed {value}" + (if diags.Length > 0 then $" ({diags.Length})" else "") + | Canceled _ -> "Canceled" + | Failed(_, ex) -> $"Failed {ex}" + +type internal JobEvent = + | Requested + | Started + | Restarted + | Finished + | Canceled + | Evicted + | Collected + | Weakened + | Strengthened + | Failed + | Cleared + +type internal ICacheKey<'TKey, 'TVersion> = + abstract member GetKey: unit -> 'TKey + abstract member GetVersion: unit -> 'TVersion + abstract member GetLabel: unit -> string + +[] +type Extensions = + + [] + static member internal WithExtraVersion(cacheKey: ICacheKey<_, _>, extraVersion) = + { new ICacheKey<_, _> with + member _.GetLabel() = cacheKey.GetLabel() + member _.GetKey() = cacheKey.GetKey() + member _.GetVersion() = cacheKey.GetVersion(), extraVersion + } + +type private KeyData<'TKey, 'TVersion> = + { + Label: string + Key: 'TKey + Version: 'TVersion + } + +type internal AsyncLock() = + + let semaphore = new SemaphoreSlim(1, 1) + + member _.Semaphore = semaphore + + member _.Do(f) = + task { + do! semaphore.WaitAsync() + + try + return! f () + finally + semaphore.Release() |> ignore + } + + interface IDisposable with + member _.Dispose() = semaphore.Dispose() + +type internal CachingDiagnosticsLogger(originalLogger: DiagnosticsLogger option) = + inherit DiagnosticsLogger($"CachingDiagnosticsLogger") + + let capturedDiagnostics = ResizeArray() + + override _.ErrorCount = + originalLogger + |> Option.map (fun x -> x.ErrorCount) + |> Option.defaultValue capturedDiagnostics.Count + + override _.DiagnosticSink(diagnostic: PhasedDiagnostic, severity: FSharpDiagnosticSeverity) = + originalLogger |> Option.iter (fun x -> x.DiagnosticSink(diagnostic, severity)) + capturedDiagnostics.Add(diagnostic, severity) + + member _.CapturedDiagnostics = capturedDiagnostics |> Seq.toList + +[] +type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> + (?keepStrongly, ?keepWeakly, ?name: string, ?cancelDuplicateRunningJobs: bool) = + + let name = defaultArg name "N/A" + let cancelDuplicateRunningJobs = defaultArg cancelDuplicateRunningJobs false + + let event = Event<_>() + + let mutable errors = 0 + let mutable hits = 0 + let mutable started = 0 + let mutable completed = 0 + let mutable canceled = 0 + let mutable restarted = 0 + let mutable failed = 0 + let mutable evicted = 0 + let mutable collected = 0 + let mutable strengthened = 0 + let mutable cleared = 0 + + let mutable cancel_ct_registration_original = 0 + let mutable cancel_exception_original = 0 + let mutable cancel_original_processed = 0 + let mutable cancel_ct_registration_subsequent = 0 + let mutable cancel_exception_subsequent = 0 + let mutable cancel_subsequent_processed = 0 + + let failures = ResizeArray() + let mutable avgDurationMs = 0.0 + + let cache = + LruCache<'TKey, 'TVersion, Job<'TValue>>( + keepStrongly = defaultArg keepStrongly 100, + keepWeakly = defaultArg keepWeakly 200, + requiredToKeep = + (function + | Running _ -> true + | Job.Canceled at when at > DateTime.Now.AddMinutes -5.0 -> true + | Job.Failed(at, _) when at > DateTime.Now.AddMinutes -5.0 -> true + | _ -> false), + event = + (function + | CacheEvent.Evicted -> + (fun k -> + Interlocked.Increment &evicted |> ignore + event.Trigger(JobEvent.Evicted, k)) + | CacheEvent.Collected -> + (fun k -> + Interlocked.Increment &collected |> ignore + event.Trigger(JobEvent.Collected, k)) + | CacheEvent.Weakened -> (fun k -> event.Trigger(JobEvent.Weakened, k)) + | CacheEvent.Strengthened -> + (fun k -> + Interlocked.Increment &strengthened |> ignore + event.Trigger(JobEvent.Strengthened, k)) + | CacheEvent.Cleared -> + (fun k -> + Interlocked.Increment &cleared |> ignore + event.Trigger(JobEvent.Cleared, k))) + ) + + let requestCounts = Dictionary, int>() + let cancellationRegistrations = Dictionary<_, _>() + + let saveRegistration key registration = + cancellationRegistrations[key] <- + match cancellationRegistrations.TryGetValue key with + | true, registrations -> registration :: registrations + | _ -> [ registration ] + + let cancelRegistration key = + match cancellationRegistrations.TryGetValue key with + | true, registrations -> + for r: CancellationTokenRegistration in registrations do + r.Dispose() + + cancellationRegistrations.Remove key |> ignore + | _ -> () + + let incrRequestCount key = + requestCounts[key] <- + if requestCounts.ContainsKey key then + requestCounts[key] + 1 + else + 1 + + let decrRequestCount key = + if requestCounts.ContainsKey key then + requestCounts[key] <- requestCounts[key] - 1 + + let log (eventType, keyData: KeyData<_, _>) = + event.Trigger(eventType, (keyData.Label, keyData.Key, keyData.Version)) + + let lock = new AsyncLock() + + let processRequest post (key: KeyData<_, _>, msg) diagnosticLogger = + + lock.Do(fun () -> + task { + + let cached, otherVersions = cache.GetAll(key.Key, key.Version) + + let result = + match msg, cached with + | GetOrCompute _, Some(Completed(result, diags)) -> + Interlocked.Increment &hits |> ignore + diags |> replayDiagnostics diagnosticLogger + Existing(Task.FromResult result) + | GetOrCompute(_, ct), Some(Running(tcs, _, _, _, loggers)) -> + Interlocked.Increment &hits |> ignore + incrRequestCount key + + ct.Register(fun _ -> + let _name = name + Interlocked.Increment &cancel_ct_registration_subsequent |> ignore + post (key, CancelRequest)) + |> saveRegistration key + + loggers.Add diagnosticLogger + + Existing tcs.Task + + | GetOrCompute(computation, ct), None + | GetOrCompute(computation, ct), Some(Job.Canceled _) + | GetOrCompute(computation, ct), Some(Job.Failed _) -> + Interlocked.Increment &started |> ignore + incrRequestCount key + + ct.Register(fun _ -> + let _name = name + Interlocked.Increment &cancel_ct_registration_original |> ignore + post (key, OriginatorCanceled)) + |> saveRegistration key + + let cts = new CancellationTokenSource() + + cache.Set( + key.Key, + key.Version, + key.Label, + (Running(TaskCompletionSource(), cts, computation, DateTime.Now, ResizeArray())) + ) + + otherVersions + |> Seq.choose (function + | v, Running(_tcs, cts, _, _, _) -> Some(v, cts) + | _ -> None) + |> Seq.iter (fun (_v, cts) -> + use _ = Activity.start $"{name}: Duplicate running job" [| "key", key.Label |] + //System.Diagnostics.Trace.TraceWarning($"{name} Duplicate {key.Label}") + if cancelDuplicateRunningJobs then + //System.Diagnostics.Trace.TraceWarning("Canceling") + cts.Cancel()) + + New cts.Token + + log (Requested, key) + return result + }) + + let internalError key message = + let ex = exn (message) + failures.Add(key, ex) + Interlocked.Increment &errors |> ignore + // raise ex -- Suppose there's no need to raise here - where does it even go? + + let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = + task { + do! Task.Delay 0 + + do! + lock.Do(fun () -> + task { + + let cached = cache.TryGet(key.Key, key.Version) + + match action, cached with + + | OriginatorCanceled, Some(Running(tcs, cts, computation, _, _)) -> + + Interlocked.Increment &cancel_original_processed |> ignore + + decrRequestCount key + + if requestCounts[key] < 1 then + cancelRegistration key + cts.Cancel() + tcs.TrySetCanceled() |> ignore + // Remember the job in case it completes after cancellation + cache.Set(key.Key, key.Version, key.Label, Job.Canceled DateTime.Now) + requestCounts.Remove key |> ignore + log (Canceled, key) + Interlocked.Increment &canceled |> ignore + use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] + () + + else + // We need to restart the computation + Task.Run(fun () -> + Async.StartAsTask( + async { + + let cachingLogger = new CachingDiagnosticsLogger(None) + + try + // TODO: Should unify starting and restarting + log (Restarted, key) + Interlocked.Increment &restarted |> ignore + System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}" + let currentLogger = DiagnosticsThreadStatics.DiagnosticsLogger + DiagnosticsThreadStatics.DiagnosticsLogger <- cachingLogger + + try + let! result = computation |> Async.AwaitNodeCode + post (key, (JobCompleted(result, cachingLogger.CapturedDiagnostics))) + return () + finally + DiagnosticsThreadStatics.DiagnosticsLogger <- currentLogger + with + | TaskCancelled _ -> + Interlocked.Increment &cancel_exception_subsequent |> ignore + post (key, CancelRequest) + () + | ex -> post (key, (JobFailed(ex, cachingLogger.CapturedDiagnostics))) + } + ), + cts.Token) + |> ignore + + | CancelRequest, Some(Running(tcs, cts, _c, _, _)) -> + + Interlocked.Increment &cancel_subsequent_processed |> ignore + + decrRequestCount key + + if requestCounts[key] < 1 then + cancelRegistration key + cts.Cancel() + tcs.TrySetCanceled() |> ignore + // Remember the job in case it completes after cancellation + cache.Set(key.Key, key.Version, key.Label, Job.Canceled DateTime.Now) + requestCounts.Remove key |> ignore + log (Canceled, key) + Interlocked.Increment &canceled |> ignore + use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] + () + + // Probably in some cases cancellation can be fired off even after we just unregistered it + | CancelRequest, None + | CancelRequest, Some(Completed _) + | CancelRequest, Some(Job.Canceled _) + | CancelRequest, Some(Job.Failed _) + | OriginatorCanceled, None + | OriginatorCanceled, Some(Completed _) + | OriginatorCanceled, Some(Job.Canceled _) + | OriginatorCanceled, Some(Job.Failed _) -> () + + | JobFailed(ex, diags), Some(Running(tcs, _cts, _c, _ts, loggers)) -> + cancelRegistration key + cache.Set(key.Key, key.Version, key.Label, Job.Failed(DateTime.Now, ex)) + requestCounts.Remove key |> ignore + log (Failed, key) + Interlocked.Increment &failed |> ignore + failures.Add(key.Label, ex) + + for logger in loggers do + diags |> replayDiagnostics logger + + tcs.TrySetException ex |> ignore + + | JobCompleted(result, diags), Some(Running(tcs, _cts, _c, started, loggers)) -> + cancelRegistration key + cache.Set(key.Key, key.Version, key.Label, (Completed(result, diags))) + requestCounts.Remove key |> ignore + log (Finished, key) + Interlocked.Increment &completed |> ignore + let duration = float (DateTime.Now - started).Milliseconds + + avgDurationMs <- + if completed < 2 then + duration + else + avgDurationMs + (duration - avgDurationMs) / float completed + + for logger in loggers do + diags |> replayDiagnostics logger + + if tcs.TrySetResult result = false then + internalError key.Label "Invalid state: Completed job already completed" + + // Sometimes job can be canceled but it still manages to complete (or fail) + | JobFailed _, Some(Job.Canceled _) + | JobCompleted _, Some(Job.Canceled _) -> () + + // Job can't be evicted from cache while it's running because then subsequent requesters would be waiting forever + | JobFailed _, None -> internalError key.Label "Invalid state: Running job missing in cache (failed)" + + | JobCompleted _, None -> internalError key.Label "Invalid state: Running job missing in cache (completed)" + + | JobFailed(ex, _diags), Some(Completed(_job, _diags2)) -> + internalError key.Label $"Invalid state: Failed Completed job \n%A{ex}" + + | JobCompleted(_result, _diags), Some(Completed(_job, _diags2)) -> + internalError key.Label "Invalid state: Double-Completed job" + + | JobFailed(ex, _diags), Some(Job.Failed(_, ex2)) -> + internalError key.Label $"Invalid state: Double-Failed job \n%A{ex} \n%A{ex2}" + + | JobCompleted(_result, _diags), Some(Job.Failed(_, ex2)) -> + internalError key.Label $"Invalid state: Completed Failed job \n%A{ex2}" + }) + } + + let rec post msg = + Task.Run(fun () -> processStateUpdate post msg :> Task) |> ignore + + member this.Get'(key, computation) = + + let wrappedKey = + { new ICacheKey<_, _> with + member _.GetKey() = key + member _.GetVersion() = Unchecked.defaultof<_> + member _.GetLabel() = key.ToString() + } + + this.Get(wrappedKey, computation) + + member _.Get(key: ICacheKey<_, _>, computation) = + + let key = + { + Label = key.GetLabel() + Key = key.GetKey() + Version = key.GetVersion() + } + + node { + let! ct = NodeCode.CancellationToken + + let callerDiagnosticLogger = DiagnosticsThreadStatics.DiagnosticsLogger + + match! + processRequest post (key, GetOrCompute(computation, ct)) callerDiagnosticLogger + |> NodeCode.AwaitTask + with + | New internalCt -> + + let linkedCtSource = CancellationTokenSource.CreateLinkedTokenSource(ct, internalCt) + let cachingLogger = new CachingDiagnosticsLogger(Some callerDiagnosticLogger) + + try + return! + Async.StartAsTask( + async { + // TODO: Should unify starting and restarting + let currentLogger = DiagnosticsThreadStatics.DiagnosticsLogger + DiagnosticsThreadStatics.DiagnosticsLogger <- cachingLogger + + log (Started, key) + + try + let! result = computation |> Async.AwaitNodeCode + post (key, (JobCompleted(result, cachingLogger.CapturedDiagnostics))) + return result + finally + DiagnosticsThreadStatics.DiagnosticsLogger <- currentLogger + }, + cancellationToken = linkedCtSource.Token + ) + |> NodeCode.AwaitTask + with + | TaskCancelled ex -> + // TODO: do we need to do anything else here? Presumably it should be done by the registration on + // the cancellation token or before we triggered our own cancellation + + // Let's send this again just in case. It seems sometimes it's not triggered from the registration? + + Interlocked.Increment &cancel_exception_original |> ignore + + post (key, (OriginatorCanceled)) + return raise ex + | ex -> + post (key, (JobFailed(ex, cachingLogger.CapturedDiagnostics))) + return raise ex + + | Existing job -> return! job |> NodeCode.AwaitTask + + } + + member _.Clear() = cache.Clear() + + member _.Clear predicate = cache.Clear predicate + + member val Event = event.Publish + + member this.OnEvent = this.Event.Add + + member _.Locked = lock.Semaphore.CurrentCount < 1 + + member _.Running = + cache.GetValues() + |> Seq.filter (function + | _, _, Running _ -> true + | _ -> false) + |> Seq.toArray + + member this.DebuggerDisplay = + let locked = if this.Locked then " [LOCKED]" else "" + + let valueStats = + cache.GetValues() + |> Seq.countBy (function + | _, _, Running _ -> "Running" + | _, _, Completed _ -> "Completed" + | _, _, Job.Canceled _ -> "Canceled" + | _, _, Job.Failed _ -> "Failed") + |> Map + + let running = + valueStats.TryFind "Running" + |> Option.map (sprintf " Running: %d ") + |> Option.defaultValue "" + + let avgDuration = avgDurationMs |> sprintf "| Avg: %.0f ms" + + let hitRatio = + if started > 0 then + $" (%.0f{float hits / (float (started + hits)) * 100.0} %%)" + else + "" + + let stats = + [| + if errors + failed > 0 then + " (_!_) " + if errors > 0 then $"| ERRORS: {errors} " else "" + if failed > 0 then $"| FAILED: {failed} " else "" + $"| hits: {hits}{hitRatio} " + if started > 0 then $"| started: {started} " else "" + if completed > 0 then $"| completed: {completed} " else "" + if canceled > 0 then $"| canceled: {canceled} " else "" + if restarted > 0 then $"| restarted: {restarted} " else "" + if evicted > 0 then $"| evicted: {evicted} " else "" + if collected > 0 then $"| collected: {collected} " else "" + if cleared > 0 then $"| cleared: {cleared} " else "" + if strengthened > 0 then + $"| strengthened: {strengthened} " + else + "" + |] + |> String.concat "" + + $"{locked}{running}{cache.DebuggerDisplay} {stats}{avgDuration}" + +/// A drop-in replacement for AsyncMemoize that disables caching and just runs the computation every time. +[] +type internal AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> + (?keepStrongly, ?keepWeakly, ?name: string, ?cancelDuplicateRunningJobs: bool) = + + do ignore (keepStrongly, keepWeakly, name, cancelDuplicateRunningJobs) + + let mutable requests = 0 + + member _.Get(_key: ICacheKey<_, _>, computation) = + Interlocked.Increment &requests |> ignore + computation + + member _.DebuggerDisplay = $"(disabled) requests: {requests}" diff --git a/src/Compiler/Facilities/AsyncMemoize.fsi b/src/Compiler/Facilities/AsyncMemoize.fsi new file mode 100644 index 00000000000..a34588e7af8 --- /dev/null +++ b/src/Compiler/Facilities/AsyncMemoize.fsi @@ -0,0 +1,83 @@ +namespace Internal.Utilities.Collections + +open System.Threading.Tasks +open FSharp.Compiler.BuildGraph + +[] +module internal Utils = + + /// Return file name with one directory above it + val shortPath: path: string -> string + + val (|TaskCancelled|_|): ex: exn -> TaskCanceledException option + +type internal JobEvent = + | Requested + | Started + | Restarted + | Finished + | Canceled + | Evicted + | Collected + | Weakened + | Strengthened + | Failed + | Cleared + +type internal ICacheKey<'TKey, 'TVersion> = + + abstract GetKey: unit -> 'TKey + + abstract GetLabel: unit -> string + + abstract GetVersion: unit -> 'TVersion + +[] +type Extensions = + + [] + static member internal WithExtraVersion: cacheKey: ICacheKey<'a, 'b> * extraVersion: 'c -> ICacheKey<'a, ('b * 'c)> + +type internal AsyncLock = + interface System.IDisposable + + new: unit -> AsyncLock + + member Do: f: (unit -> #Task<'b>) -> Task<'b> + +/// +/// A cache/memoization for computations that makes sure that the same computation wil only be computed once even if it's needed +/// at multiple places/times. +/// +/// Strongly holds at most one result per key. +/// +type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> = + + /// Maximum number of strongly held results to keep in the cache + /// Maximum number of weakly held results to keep in the cache + /// Name of the cache - used in tracing messages + /// If true, when a job is started, all other jobs with the same key will be canceled. + new: + ?keepStrongly: int * ?keepWeakly: int * ?name: string * ?cancelDuplicateRunningJobs: bool -> + AsyncMemoize<'TKey, 'TVersion, 'TValue> + + member Clear: unit -> unit + + member Clear: predicate: ('TKey -> bool) -> unit + + member Get: key: ICacheKey<'TKey, 'TVersion> * computation: NodeCode<'TValue> -> NodeCode<'TValue> + + member Get': key: 'TKey * computation: NodeCode<'TValue> -> NodeCode<'TValue> + + member Event: IEvent + + member OnEvent: ((JobEvent * (string * 'TKey * 'TVersion) -> unit) -> unit) + +/// A drop-in replacement for AsyncMemoize that disables caching and just runs the computation every time. +type internal AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> = + + new: + ?keepStrongly: obj * ?keepWeakly: obj * ?name: string * ?cancelDuplicateRunningJobs: bool -> + AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue> + + member Get: _key: ICacheKey<'a, 'b> * computation: 'c -> 'c diff --git a/src/Compiler/Facilities/BuildGraph.fs b/src/Compiler/Facilities/BuildGraph.fs index 8927862c23c..71f4d3da991 100644 --- a/src/Compiler/Facilities/BuildGraph.fs +++ b/src/Compiler/Facilities/BuildGraph.fs @@ -17,14 +17,12 @@ let wrapThreadStaticInfo computation = async { let diagnosticsLogger = DiagnosticsThreadStatics.DiagnosticsLogger let phase = DiagnosticsThreadStatics.BuildPhase - let ct = Cancellable.Token try return! computation finally DiagnosticsThreadStatics.DiagnosticsLogger <- diagnosticsLogger DiagnosticsThreadStatics.BuildPhase <- phase - Cancellable.Token <- ct } type Async<'T> with @@ -127,7 +125,6 @@ type NodeCode private () = static member RunImmediate(computation: NodeCode<'T>, ct: CancellationToken) = let diagnosticsLogger = DiagnosticsThreadStatics.DiagnosticsLogger let phase = DiagnosticsThreadStatics.BuildPhase - let ct2 = Cancellable.Token try try @@ -135,7 +132,6 @@ type NodeCode private () = async { DiagnosticsThreadStatics.DiagnosticsLogger <- diagnosticsLogger DiagnosticsThreadStatics.BuildPhase <- phase - Cancellable.Token <- ct2 return! computation |> Async.AwaitNodeCode } @@ -143,7 +139,6 @@ type NodeCode private () = finally DiagnosticsThreadStatics.DiagnosticsLogger <- diagnosticsLogger DiagnosticsThreadStatics.BuildPhase <- phase - Cancellable.Token <- ct2 with :? AggregateException as ex when ex.InnerExceptions.Count = 1 -> raise (ex.InnerExceptions[0]) @@ -153,14 +148,12 @@ type NodeCode private () = static member StartAsTask_ForTesting(computation: NodeCode<'T>, ?ct: CancellationToken) = let diagnosticsLogger = DiagnosticsThreadStatics.DiagnosticsLogger let phase = DiagnosticsThreadStatics.BuildPhase - let ct2 = Cancellable.Token try let work = async { DiagnosticsThreadStatics.DiagnosticsLogger <- diagnosticsLogger DiagnosticsThreadStatics.BuildPhase <- phase - Cancellable.Token <- ct2 return! computation |> Async.AwaitNodeCode } @@ -168,7 +161,6 @@ type NodeCode private () = finally DiagnosticsThreadStatics.DiagnosticsLogger <- diagnosticsLogger DiagnosticsThreadStatics.BuildPhase <- phase - Cancellable.Token <- ct2 static member CancellationToken = cancellationToken @@ -201,7 +193,19 @@ type NodeCode private () = } static member Parallel(computations: NodeCode<'T> seq) = - computations |> Seq.map (fun (Node x) -> x) |> Async.Parallel |> Node + let diagnosticsLogger = DiagnosticsThreadStatics.DiagnosticsLogger + let phase = DiagnosticsThreadStatics.BuildPhase + + computations + |> Seq.map (fun (Node x) -> + async { + DiagnosticsThreadStatics.DiagnosticsLogger <- diagnosticsLogger + DiagnosticsThreadStatics.BuildPhase <- phase + return! x + }) + |> Async.Parallel + |> wrapThreadStaticInfo + |> Node [] module GraphNode = diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fs b/src/Compiler/Facilities/DiagnosticsLogger.fs index 24f6ebf2e6d..08a46d1a25d 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fs +++ b/src/Compiler/Facilities/DiagnosticsLogger.fs @@ -374,7 +374,7 @@ type CapturingDiagnosticsLogger(nm, ?eagerFormat) = let errors = diagnostics.ToArray() errors |> Array.iter diagnosticsLogger.DiagnosticSink -/// Type holds thread-static globals for use by the compile. +/// Type holds thread-static globals for use by the compiler. type internal DiagnosticsThreadStatics = [] static val mutable private buildPhase: BuildPhase diff --git a/src/Compiler/Facilities/Hashing.fs b/src/Compiler/Facilities/Hashing.fs new file mode 100644 index 00000000000..2dfbb38b7ee --- /dev/null +++ b/src/Compiler/Facilities/Hashing.fs @@ -0,0 +1,81 @@ +namespace Internal.Utilities.Hashing + +open System +open System.Threading + +/// Tools for hashing things with MD5 into a string that can be used as a cache key. +module internal Md5StringHasher = + + let private md5 = + new ThreadLocal<_>(fun () -> System.Security.Cryptography.MD5.Create()) + + let private computeHash (bytes: byte array) = md5.Value.ComputeHash(bytes) + + let hashString (s: string) = + System.Text.Encoding.UTF8.GetBytes(s) |> computeHash + + let empty = String.Empty + + let addBytes (bytes: byte array) (s: string) = + let sbytes = s |> hashString + + Array.append sbytes bytes + |> computeHash + |> System.BitConverter.ToString + |> (fun x -> x.Replace("-", "")) + + let addString (s: string) (s2: string) = + s |> System.Text.Encoding.UTF8.GetBytes |> addBytes <| s2 + + let addSeq<'item> (items: 'item seq) (addItem: 'item -> string -> string) (s: string) = + items |> Seq.fold (fun s a -> addItem a s) s + + let addStrings strings = addSeq strings addString + + // If we use this make it an extension method? + //let addVersions<'a, 'b when 'a :> ICacheKey<'b, string>> (versions: 'a seq) (s: string) = + // versions |> Seq.map (fun x -> x.GetVersion()) |> addStrings <| s + + let addBool (b: bool) (s: string) = + b |> BitConverter.GetBytes |> addBytes <| s + + let addDateTime (dt: System.DateTime) (s: string) = dt.Ticks.ToString() |> addString <| s + +module internal Md5Hasher = + + let private md5 = + new ThreadLocal<_>(fun () -> System.Security.Cryptography.MD5.Create()) + + let computeHash (bytes: byte array) = md5.Value.ComputeHash(bytes) + + let empty = Array.empty + + let hashString (s: string) = + s |> System.Text.Encoding.UTF8.GetBytes |> computeHash + + let addBytes (bytes: byte array) (s: byte array) = + + Array.append s bytes |> computeHash + + let addString (s: string) (s2: byte array) = + s |> System.Text.Encoding.UTF8.GetBytes |> addBytes <| s2 + + let addSeq<'item> (items: 'item seq) (addItem: 'item -> byte array -> byte array) (s: byte array) = + items |> Seq.fold (fun s a -> addItem a s) s + + let addStrings strings = addSeq strings addString + let addBytes' bytes = addSeq bytes addBytes + + // If we use this make it an extension method? + //let addVersions<'a, 'b when 'a :> ICacheKey<'b, string>> (versions: 'a seq) (s: string) = + // versions |> Seq.map (fun x -> x.GetVersion()) |> addStrings <| s + + let addBool (b: bool) (s: byte array) = + b |> BitConverter.GetBytes |> addBytes <| s + + let addDateTime (dt: System.DateTime) (s: byte array) = + dt.Ticks |> BitConverter.GetBytes |> addBytes <| s + + let addDateTimes (dts: System.DateTime seq) (s: byte array) = s |> addSeq dts addDateTime + + let toString (bytes: byte array) = bytes |> System.BitConverter.ToString diff --git a/src/Compiler/Facilities/Hashing.fsi b/src/Compiler/Facilities/Hashing.fsi new file mode 100644 index 00000000000..121afb29eb2 --- /dev/null +++ b/src/Compiler/Facilities/Hashing.fsi @@ -0,0 +1,46 @@ +namespace Internal.Utilities.Hashing + +/// Tools for hashing things with MD5 into a string that can be used as a cache key. +module internal Md5StringHasher = + + val hashString: s: string -> byte array + + val empty: string + + val addBytes: bytes: byte array -> s: string -> string + + val addString: s: string -> s2: string -> string + + val addSeq: items: 'item seq -> addItem: ('item -> string -> string) -> s: string -> string + + val addStrings: strings: string seq -> (string -> string) + + val addBool: b: bool -> s: string -> string + + val addDateTime: dt: System.DateTime -> s: string -> string + +module internal Md5Hasher = + + val computeHash: bytes: byte array -> byte array + + val empty: 'a array + + val hashString: s: string -> byte array + + val addBytes: bytes: byte array -> s: byte array -> byte array + + val addString: s: string -> s2: byte array -> byte array + + val addSeq: items: 'item seq -> addItem: ('item -> byte array -> byte array) -> s: byte array -> byte array + + val addStrings: strings: string seq -> (byte array -> byte array) + + val addBytes': bytes: byte array seq -> (byte array -> byte array) + + val addBool: b: bool -> s: byte array -> byte array + + val addDateTime: dt: System.DateTime -> s: byte array -> byte array + + val addDateTimes: dts: System.DateTime seq -> s: byte array -> byte array + + val toString: bytes: byte array -> string diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index 643224b904a..e7c2a25ee3d 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -84,6 +84,7 @@ type LanguageFeature = | PreferExtensionMethodOverPlainProperty | WarningIndexedPropertiesGetSetSameType | WarningWhenTailCallAttrOnNonRec + | BooleanReturningAndReturnTypeDirectedPartialActivePattern /// LanguageVersion management type LanguageVersion(versionText) = @@ -195,6 +196,7 @@ type LanguageVersion(versionText) = LanguageFeature.WarningIndexedPropertiesGetSetSameType, previewVersion LanguageFeature.WarningWhenTailCallAttrOnNonRec, previewVersion LanguageFeature.UnionIsPropertiesVisible, previewVersion + LanguageFeature.BooleanReturningAndReturnTypeDirectedPartialActivePattern, previewVersion ] static let defaultLanguageVersion = LanguageVersion("default") @@ -336,6 +338,8 @@ type LanguageVersion(versionText) = | LanguageFeature.PreferExtensionMethodOverPlainProperty -> FSComp.SR.featurePreferExtensionMethodOverPlainProperty () | LanguageFeature.WarningIndexedPropertiesGetSetSameType -> FSComp.SR.featureWarningIndexedPropertiesGetSetSameType () | LanguageFeature.WarningWhenTailCallAttrOnNonRec -> FSComp.SR.featureChkTailCallAttrOnNonRec () + | LanguageFeature.BooleanReturningAndReturnTypeDirectedPartialActivePattern -> + FSComp.SR.featureBooleanReturningAndReturnTypeDirectedPartialActivePattern () /// Get a version string associated with the given feature. static member GetFeatureVersionString feature = diff --git a/src/Compiler/Facilities/LanguageFeatures.fsi b/src/Compiler/Facilities/LanguageFeatures.fsi index 7af2317e3c3..29d6c2c33a3 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fsi +++ b/src/Compiler/Facilities/LanguageFeatures.fsi @@ -75,6 +75,7 @@ type LanguageFeature = | PreferExtensionMethodOverPlainProperty | WarningIndexedPropertiesGetSetSameType | WarningWhenTailCallAttrOnNonRec + | BooleanReturningAndReturnTypeDirectedPartialActivePattern /// LanguageVersion management type LanguageVersion = diff --git a/src/Compiler/Facilities/prim-lexing.fs b/src/Compiler/Facilities/prim-lexing.fs index 5951c8338e4..6b927ef4a96 100644 --- a/src/Compiler/Facilities/prim-lexing.fs +++ b/src/Compiler/Facilities/prim-lexing.fs @@ -6,8 +6,12 @@ namespace FSharp.Compiler.Text open System open System.IO +open System.Collections.Immutable open Internal.Utilities.Library +open Internal.Utilities.Collections +open Internal.Utilities.Hashing + type ISourceText = abstract Item: index: int -> char with get @@ -30,6 +34,11 @@ type ISourceText = abstract GetSubTextFromRange: range: range -> string +type ISourceTextNew = + inherit ISourceText + + abstract GetChecksum: unit -> System.Collections.Immutable.ImmutableArray + [] type StringText(str: string) = @@ -67,7 +76,7 @@ type StringText(str: string) = override _.ToString() = str - interface ISourceText with + interface ISourceTextNew with member _.Item with get index = str[index] @@ -145,9 +154,45 @@ type StringText(str: string) = let lastLine = sourceText.GetLineString(range.EndLine - 1) sb.Append(lastLine.Substring(0, range.EndColumn)).ToString() + member _.GetChecksum() = + str |> Md5Hasher.hashString |> ImmutableArray.Create + module SourceText = let ofString str = StringText(str) :> ISourceText + +module SourceTextNew = + + let ofString str = StringText(str) :> ISourceTextNew + + let ofISourceText (sourceText: ISourceText) = + { new ISourceTextNew with + member _.Item + with get index = sourceText[index] + + member _.GetLineString(x) = sourceText.GetLineString(x) + + member _.GetLineCount() = sourceText.GetLineCount() + + member _.GetLastCharacterPosition() = sourceText.GetLastCharacterPosition() + + member _.GetSubTextString(x, y) = sourceText.GetSubTextString(x, y) + + member _.SubTextEquals(x, y) = sourceText.SubTextEquals(x, y) + + member _.Length = sourceText.Length + + member _.ContentEquals(x) = sourceText.ContentEquals(x) + + member _.CopyTo(a, b, c, d) = sourceText.CopyTo(a, b, c, d) + + member _.GetSubTextFromRange(x) = sourceText.GetSubTextFromRange(x) + + member _.GetChecksum() = + // TODO: something better... + sourceText.ToString() |> Md5Hasher.hashString |> ImmutableArray.Create + } + // NOTE: the code in this file is a drop-in replacement runtime for Lexing.fs from the FsLexYacc repository namespace Internal.Utilities.Text.Lexing diff --git a/src/Compiler/Facilities/prim-lexing.fsi b/src/Compiler/Facilities/prim-lexing.fsi index 6e5f6da4f25..ff13f96c9e1 100644 --- a/src/Compiler/Facilities/prim-lexing.fsi +++ b/src/Compiler/Facilities/prim-lexing.fsi @@ -39,12 +39,23 @@ type ISourceText = /// Throws an exception when the input range is outside the file boundaries. abstract GetSubTextFromRange: range: range -> string +/// Just like ISourceText, but with a checksum. Added as a separate type to avoid breaking changes. +type ISourceTextNew = + inherit ISourceText + + abstract GetChecksum: unit -> System.Collections.Immutable.ImmutableArray + /// Functions related to ISourceText objects module SourceText = /// Creates an ISourceText object from the given string val ofString: string -> ISourceText +module SourceTextNew = + + val ofString: string -> ISourceTextNew + val ofISourceText: ISourceText -> ISourceTextNew + // // NOTE: the code in this file is a drop-in replacement runtime for Lexing.fsi from the FsLexYacc repository // and is referenced by generated code for the three FsLex generated lexers in the F# compiler. diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index ca0e2335064..e5ff5b6c754 100644 --- a/src/Compiler/Interactive/fsi.fs +++ b/src/Compiler/Interactive/fsi.fs @@ -4089,7 +4089,6 @@ type FsiInteractionProcessor ?cancellationToken: CancellationToken ) = let cancellationToken = defaultArg cancellationToken CancellationToken.None - use _ = Cancellable.UsingToken(cancellationToken) if tokenizer.LexBuffer.IsPastEndOfStream then let stepStatus = @@ -4218,7 +4217,6 @@ type FsiInteractionProcessor member _.EvalInteraction(ctok, sourceText, scriptFileName, diagnosticsLogger, ?cancellationToken) = let cancellationToken = defaultArg cancellationToken CancellationToken.None - use _ = Cancellable.UsingToken(cancellationToken) use _ = UseBuildPhase BuildPhase.Interactive use _ = UseDiagnosticsLogger diagnosticsLogger use _scope = SetCurrentUICultureForThread fsiOptions.FsiLCID @@ -4895,7 +4893,6 @@ type FsiEvaluationSession SpawnInteractiveServer(fsi, fsiOptions, fsiConsoleOutput) use _ = UseBuildPhase BuildPhase.Interactive - use _ = Cancellable.UsingToken(CancellationToken.None) if fsiOptions.Interact then // page in the type check env diff --git a/src/Compiler/Optimize/DetupleArgs.fs b/src/Compiler/Optimize/DetupleArgs.fs index 0021357366c..a1655bafb0b 100644 --- a/src/Compiler/Optimize/DetupleArgs.fs +++ b/src/Compiler/Optimize/DetupleArgs.fs @@ -150,14 +150,15 @@ let DetupleRewriteStackGuardDepth = StackGuard.GetDepthOption "DetupleRewrite" // Merge a tyapp node and and app node. +[] let (|TyappAndApp|_|) e = match e with | Expr.App(f, fty, tys, args, m) -> match stripDebugPoints (stripExpr f) with - | Expr.App(f2, fty2, tys2, [], m2) -> Some(f2, fty2, tys2 @ tys, args, m2) - | Expr.App _ -> Some(f, fty, tys, args, m) (* has args, so not combine ty args *) - | f -> Some(f, fty, tys, args, m) - | _ -> None + | Expr.App(f2, fty2, tys2, [], m2) -> ValueSome(f2, fty2, tys2 @ tys, args, m2) + | Expr.App _ -> ValueSome(f, fty, tys, args, m) (* has args, so not combine ty args *) + | f -> ValueSome(f, fty, tys, args, m) + | _ -> ValueNone [] module GlobalUsageAnalysis = diff --git a/src/Compiler/Optimize/LowerComputedCollections.fs b/src/Compiler/Optimize/LowerComputedCollections.fs index f2f3e4f6245..2de57119ff3 100644 --- a/src/Compiler/Optimize/LowerComputedCollections.fs +++ b/src/Compiler/Optimize/LowerComputedCollections.fs @@ -230,27 +230,30 @@ let (|OptionalCoerce|) expr = // Making 'seq' optional means this kicks in for FSharp.Core, see TcArrayOrListComputedExpression // which only adds a 'seq' call outside of FSharp.Core +[] let (|OptionalSeq|_|) g amap expr = match expr with // use 'seq { ... }' as an indicator | Seq g (e, elemTy) -> - Some (e, elemTy) + ValueSome (e, elemTy) | _ -> // search for the relevant element type match tyOfExpr g expr with | SeqElemTy g amap expr.Range elemTy -> - Some (expr, elemTy) - | _ -> None + ValueSome (expr, elemTy) + | _ -> ValueNone +[] let (|SeqToList|_|) g expr = match expr with - | ValApp g g.seq_to_list_vref (_, [seqExpr], m) -> Some (seqExpr, m) - | _ -> None + | ValApp g g.seq_to_list_vref (_, [seqExpr], m) -> ValueSome (seqExpr, m) + | _ -> ValueNone +[] let (|SeqToArray|_|) g expr = match expr with - | ValApp g g.seq_to_array_vref (_, [seqExpr], m) -> Some (seqExpr, m) - | _ -> None + | ValApp g g.seq_to_array_vref (_, [seqExpr], m) -> ValueSome (seqExpr, m) + | _ -> ValueNone let LowerComputedListOrArrayExpr tcVal (g: TcGlobals) amap overallExpr = // If ListCollector is in FSharp.Core then this optimization kicks in diff --git a/src/Compiler/Optimize/LowerSequences.fs b/src/Compiler/Optimize/LowerSequences.fs index 3a7d733ec59..64686d0fe62 100644 --- a/src/Compiler/Optimize/LowerSequences.fs +++ b/src/Compiler/Optimize/LowerSequences.fs @@ -74,15 +74,16 @@ let tyConfirmsToSeq g ty = tyconRefEq g tcref g.tcref_System_Collections_Generic_IEnumerable | _ -> false +[] let (|SeqElemTy|_|) g amap m ty = match SearchEntireHierarchyOfType (tyConfirmsToSeq g) g amap m ty with | None -> // printfn "FAILED - yield! did not yield a sequence! %s" (stringOfRange m) - None + ValueNone | Some seqTy -> // printfn "found yield!" let inpElemTy = List.head (argsOfAppTy g seqTy) - Some inpElemTy + ValueSome inpElemTy /// Analyze a TAST expression to detect the elaborated form of a sequence expression. /// Then compile it to a state machine represented as a TAST containing goto, return and label nodes. diff --git a/src/Compiler/Optimize/LowerSequences.fsi b/src/Compiler/Optimize/LowerSequences.fsi index aa675cda5c0..61ed7d87766 100644 --- a/src/Compiler/Optimize/LowerSequences.fsi +++ b/src/Compiler/Optimize/LowerSequences.fsi @@ -9,7 +9,8 @@ open FSharp.Compiler.TypedTree open FSharp.Compiler.Text /// Detect a 'seq' type -val (|SeqElemTy|_|): TcGlobals -> ImportMap -> range -> TType -> TType option +[] +val (|SeqElemTy|_|): TcGlobals -> ImportMap -> range -> TType -> TType voption val callNonOverloadedILMethod: g: TcGlobals -> amap: ImportMap -> m: range -> methName: string -> ty: TType -> args: Exprs -> Expr diff --git a/src/Compiler/Optimize/LowerStateMachines.fs b/src/Compiler/Optimize/LowerStateMachines.fs index ef578e86064..97d212f8854 100644 --- a/src/Compiler/Optimize/LowerStateMachines.fs +++ b/src/Compiler/Optimize/LowerStateMachines.fs @@ -377,6 +377,7 @@ type LowerStateMachine(g: TcGlobals) = | None -> env2, expr2 // Detect a state machine with a single method override + [] let (|ExpandedStateMachineInContext|_|) inputExpr = // All expanded resumable code state machines e.g. 'task { .. }' begin with a bind of @builder or 'defn' let env, expr = BindResumableCodeDefinitions env.Empty inputExpr @@ -405,9 +406,9 @@ type LowerStateMachine(g: TcGlobals) = (moveNextThisVar, moveNextExprR), (setStateMachineThisVar, setStateMachineStateVar, setStateMachineBodyR), (afterCodeThisVar, afterCodeBodyR)) - Some (env, remake2, moveNextBody) + ValueSome (env, remake2, moveNextBody) | _ -> - None + ValueNone // A utility to add a jump table an expression let addPcJumpTable m (pcs: int list) (pc2lab: Map) pcExpr expr = diff --git a/src/Compiler/Optimize/Optimizer.fs b/src/Compiler/Optimize/Optimizer.fs index e1eaddef8a8..fd3c85dd60b 100644 --- a/src/Compiler/Optimize/Optimizer.fs +++ b/src/Compiler/Optimize/Optimizer.fs @@ -230,20 +230,23 @@ type Summary<'Info> = // Note, this is a different notion of "size" to the one used for inlining heuristics //------------------------------------------------------------------------- -let rec SizeOfValueInfos (arr:_[]) = - if arr.Length <= 0 then 0 else max 0 (SizeOfValueInfo arr[0]) - -and SizeOfValueInfo x = - match x with - | SizeValue (vdepth, _v) -> vdepth // terminate recursion at CACHED size nodes - | ConstValue (_x, _) -> 1 - | UnknownValue -> 1 - | ValValue (_vr, vinfo) -> SizeOfValueInfo vinfo + 1 - | TupleValue vinfos - | RecdValue (_, vinfos) - | UnionCaseValue (_, vinfos) -> 1 + SizeOfValueInfos vinfos - | CurriedLambdaValue _ -> 1 - | ConstExprValue (_size, _) -> 1 +let SizeOfValueInfo valueInfo = + let rec loop acc valueInfo = + match valueInfo with + | SizeValue (vdepth, _v) -> assert (vdepth >= 0); acc + vdepth // terminate recursion at CACHED size nodes + | CurriedLambdaValue _ + | ConstExprValue _ + | ConstValue _ + | UnknownValue -> acc + 1 + | TupleValue vinfos + | RecdValue (_, vinfos) + | UnionCaseValue (_, vinfos) when vinfos.Length = 0 -> acc + 1 + | TupleValue vinfos + | RecdValue (_, vinfos) + | UnionCaseValue (_, vinfos) -> loop (acc + 1) vinfos[0] + | ValValue (_vr, vinfo) -> loop (acc + 1) vinfo + + loop 0 valueInfo let [] minDepthForASizeNode = 5 // for small vinfos do not record size info, save space @@ -700,15 +703,17 @@ let rec stripValue = function | SizeValue(_, details) -> stripValue details (* step through SizeValue "aliases" *) | vinfo -> vinfo +[] let (|StripConstValue|_|) ev = match stripValue ev with - | ConstValue(c, _) -> Some c - | _ -> None + | ConstValue(c, _) -> ValueSome c + | _ -> ValueNone +[] let (|StripLambdaValue|_|) ev = match stripValue ev with - | CurriedLambdaValue (id, arity, sz, expr, ty) -> Some (id, arity, sz, expr, ty) - | _ -> None + | CurriedLambdaValue (id, arity, sz, expr, ty) -> ValueSome (id, arity, sz, expr, ty) + | _ -> ValueNone let destTupleValue ev = match stripValue ev with @@ -720,10 +725,11 @@ let destRecdValue ev = | RecdValue (_tcref, info) -> Some info | _ -> None +[] let (|StripUnionCaseValue|_|) ev = match stripValue ev with - | UnionCaseValue (c, info) -> Some (c, info) - | _ -> None + | UnionCaseValue (c, info) -> ValueSome (c, info) + | _ -> ValueNone let mkBoolVal (g: TcGlobals) n = ConstValue(Const.Bool n, g.bool_ty) @@ -1454,11 +1460,11 @@ let AbstractExprInfoByVars (boundVars: Val list, boundTyVars) ivalue = | UnknownValue -> ivalue | SizeValue (_vdepth, vinfo) -> MakeSizedValueInfo (abstractExprInfo vinfo) - and abstractValInfo v = + let abstractValInfo v = { ValExprInfo=abstractExprInfo v.ValExprInfo ValMakesNoCriticalTailcalls=v.ValMakesNoCriticalTailcalls } - and abstractModulInfo ss = + let rec abstractModulInfo ss = { ModuleOrNamespaceInfos = ss.ModuleOrNamespaceInfos |> NameMap.map (InterruptibleLazy.force >> abstractModulInfo >> notlazy) ValInfos = ss.ValInfos.Map (fun (vref, e) -> check vref (abstractValInfo e) ) } @@ -1589,7 +1595,7 @@ let ValueIsUsedOrHasEffect cenv fvs (b: Binding, binfo) = // No discarding for things that are used Zset.contains v (fvs()) -let rec SplitValuesByIsUsedOrHasEffect cenv fvs x = +let SplitValuesByIsUsedOrHasEffect cenv fvs x = x |> List.filter (ValueIsUsedOrHasEffect cenv fvs) |> List.unzip let IlAssemblyCodeInstrHasEffect i = @@ -1761,26 +1767,29 @@ let TryEliminateLet cenv env bind e2 m = | None -> mkLetBind m bind e2, 0 /// Detect the application of a value to an arbitrary number of arguments +[] let rec (|KnownValApp|_|) expr = match stripDebugPoints expr with - | Expr.Val (vref, _, _) -> Some(vref, [], []) - | Expr.App (KnownValApp(vref, typeArgs1, otherArgs1), _, typeArgs2, otherArgs2, _) -> Some(vref, typeArgs1@typeArgs2, otherArgs1@otherArgs2) - | _ -> None + | Expr.Val (vref, _, _) -> ValueSome(vref, [], []) + | Expr.App (KnownValApp(vref, typeArgs1, otherArgs1), _, typeArgs2, otherArgs2, _) -> ValueSome(vref, typeArgs1@typeArgs2, otherArgs1@otherArgs2) + | _ -> ValueNone /// Matches boolean decision tree: /// check single case with bool const. +[] let (|TDBoolSwitch|_|) dtree = match dtree with | TDSwitch(expr, [TCase (DecisionTreeTest.Const(Const.Bool testBool), caseTree )], Some defaultTree, range) -> - Some (expr, testBool, caseTree, defaultTree, range) + ValueSome (expr, testBool, caseTree, defaultTree, range) | _ -> - None + ValueNone /// Check target that have a constant bool value +[] let (|ConstantBoolTarget|_|) target = match target with - | TTarget([], Expr.Const (Const.Bool b, _, _), _) -> Some b - | _ -> None + | TTarget([], Expr.Const (Const.Bool b, _, _), _) -> ValueSome b + | _ -> ValueNone /// Is this a tree, where each decision is a two-way switch (to prevent later duplication of trees), and each branch returns or true/false, /// apart from one branch which defers to another expression @@ -2013,7 +2022,7 @@ let TryRewriteBranchingTupleBinding g (v: Val) rhs tgtSeqPtOpt body m = mkLetsBind m binds rhsAndTupleBinding |> Some | _ -> None -let rec ExpandStructuralBinding cenv expr = +let ExpandStructuralBinding cenv expr = let g = cenv.g assert cenv.settings.ExpandStructuralValues() @@ -2050,50 +2059,59 @@ let rec ExpandStructuralBinding cenv expr = ExpandStructuralBindingRaw cenv e /// Detect a query { ... } +[] let (|QueryRun|_|) g expr = match expr with | Expr.App (Expr.Val (vref, _, _), _, _, [_builder; arg], _) when valRefEq g vref g.query_run_value_vref -> - Some (arg, None) + ValueSome (arg, None) | Expr.App (Expr.Val (vref, _, _), _, [ elemTy ], [_builder; arg], _) when valRefEq g vref g.query_run_enumerable_vref -> - Some (arg, Some elemTy) + ValueSome (arg, Some elemTy) | _ -> - None + ValueNone let (|MaybeRefTupled|) e = tryDestRefTupleExpr e +[] let (|AnyInstanceMethodApp|_|) e = match e with - | Expr.App (Expr.Val (vref, _, _), _, tyargs, [obj; MaybeRefTupled args], _) -> Some (vref, tyargs, obj, args) - | _ -> None + | Expr.App (Expr.Val (vref, _, _), _, tyargs, [obj; MaybeRefTupled args], _) -> ValueSome (vref, tyargs, obj, args) + | _ -> ValueNone +[] let (|InstanceMethodApp|_|) g (expectedValRef: ValRef) e = match e with - | AnyInstanceMethodApp (vref, tyargs, obj, args) when valRefEq g vref expectedValRef -> Some (tyargs, obj, args) - | _ -> None + | AnyInstanceMethodApp (vref, tyargs, obj, args) when valRefEq g vref expectedValRef -> ValueSome (tyargs, obj, args) + | _ -> ValueNone +[] let (|QuerySourceEnumerable|_|) g = function - | InstanceMethodApp g g.query_source_vref ([resTy], _builder, [res]) -> Some (resTy, res) - | _ -> None + | InstanceMethodApp g g.query_source_vref ([resTy], _builder, [res]) -> ValueSome (resTy, res) + | _ -> ValueNone +[] let (|QueryFor|_|) g = function - | InstanceMethodApp g g.query_for_vref ([srcTy;qTy;resTy;_qInnerTy], _builder, [src;selector]) -> Some (qTy, srcTy, resTy, src, selector) - | _ -> None + | InstanceMethodApp g g.query_for_vref ([srcTy;qTy;resTy;_qInnerTy], _builder, [src;selector]) -> ValueSome (qTy, srcTy, resTy, src, selector) + | _ -> ValueNone +[] let (|QueryYield|_|) g = function - | InstanceMethodApp g g.query_yield_vref ([resTy;qTy], _builder, [res]) -> Some (qTy, resTy, res) - | _ -> None + | InstanceMethodApp g g.query_yield_vref ([resTy;qTy], _builder, [res]) -> ValueSome (qTy, resTy, res) + | _ -> ValueNone +[] let (|QueryYieldFrom|_|) g = function - | InstanceMethodApp g g.query_yield_from_vref ([resTy;qTy], _builder, [res]) -> Some (qTy, resTy, res) - | _ -> None + | InstanceMethodApp g g.query_yield_from_vref ([resTy;qTy], _builder, [res]) -> ValueSome (qTy, resTy, res) + | _ -> ValueNone +[] let (|QuerySelect|_|) g = function - | InstanceMethodApp g g.query_select_vref ([srcTy;qTy;resTy], _builder, [src;selector]) -> Some (qTy, srcTy, resTy, src, selector) - | _ -> None + | InstanceMethodApp g g.query_select_vref ([srcTy;qTy;resTy], _builder, [src;selector]) -> ValueSome (qTy, srcTy, resTy, src, selector) + | _ -> ValueNone +[] let (|QueryZero|_|) g = function - | InstanceMethodApp g g.query_zero_vref ([resTy;qTy], _builder, _) -> Some (qTy, resTy) - | _ -> None + | InstanceMethodApp g g.query_zero_vref ([resTy;qTy], _builder, _) -> ValueSome (qTy, resTy) + | _ -> ValueNone /// Look for a possible tuple and transform let (|AnyRefTupleTrans|) e = @@ -2102,11 +2120,12 @@ let (|AnyRefTupleTrans|) e = | _ -> [e], (function [e] -> e | _ -> assert false; failwith "unreachable") /// Look for any QueryBuilder.* operation and transform +[] let (|AnyQueryBuilderOpTrans|_|) g = function | Expr.App (Expr.Val (vref, _, _) as v, vty, tyargs, [builder; AnyRefTupleTrans( src :: rest, replaceArgs) ], m) when (match vref.ApparentEnclosingEntity with Parent tcref -> tyconRefEq g tcref g.query_builder_tcref | ParentNone -> false) -> - Some (src, (fun newSource -> Expr.App (v, vty, tyargs, [builder; replaceArgs(newSource :: rest)], m))) - | _ -> None + ValueSome (src, (fun newSource -> Expr.App (v, vty, tyargs, [builder; replaceArgs(newSource :: rest)], m))) + | _ -> ValueNone /// If this returns "Some" then the source is not IQueryable. // := diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs new file mode 100644 index 00000000000..f9f952dde70 --- /dev/null +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -0,0 +1,1680 @@ +namespace FSharp.Compiler.CodeAnalysis + +open FSharp.Compiler.Text +open FSharp.Compiler.BuildGraph + +open System +open System.Diagnostics +open System.IO +open System.Reflection +open System.Reflection.Emit +open System.Threading +open Internal.Utilities.Collections +open Internal.Utilities.Library +open Internal.Utilities.Library.Extras +open FSharp.Compiler +open FSharp.Compiler.AbstractIL +open FSharp.Compiler.AbstractIL.IL +open FSharp.Compiler.AbstractIL.ILBinaryReader +open FSharp.Compiler.AbstractIL.ILDynamicAssemblyWriter +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CompilerConfig +open FSharp.Compiler.CompilerDiagnostics +open FSharp.Compiler.CompilerImports +open FSharp.Compiler.CompilerOptions +open FSharp.Compiler.DependencyManager +open FSharp.Compiler.Diagnostics +open FSharp.Compiler.Driver +open FSharp.Compiler.DiagnosticsLogger +open FSharp.Compiler.IO +open FSharp.Compiler.ParseAndCheckInputs +open FSharp.Compiler.ScriptClosure +open FSharp.Compiler.Symbols +open FSharp.Compiler.Syntax +open FSharp.Compiler.Tokenization +open FSharp.Compiler.Text +open FSharp.Compiler.Text.Range +open FSharp.Compiler.TcGlobals +open FSharp.Compiler.BuildGraph +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot + +type SourceTextHash = int64 +type CacheStamp = int64 +type FileName = string +type FilePath = string +type ProjectPath = string +type FileVersion = int + +type FSharpProjectSnapshot = FSharp.Compiler.CodeAnalysis.ProjectSnapshot.FSharpProjectSnapshot + +type internal IBackgroundCompiler = + + /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. + abstract member CheckFileInProject: + parseResults: FSharpParseFileResults * + fileName: string * + fileVersion: int * + sourceText: ISourceText * + options: FSharpProjectOptions * + userOpName: string -> + NodeCode + + /// Type-check the result obtained by parsing, but only if the antecedent type checking context is available. + abstract member CheckFileInProjectAllowingStaleCachedResults: + parseResults: FSharpParseFileResults * + fileName: string * + fileVersion: int * + sourceText: ISourceText * + options: FSharpProjectOptions * + userOpName: string -> + NodeCode + + abstract member ClearCache: options: seq * userOpName: string -> unit + + abstract member ClearCache: projects: ProjectSnapshot.FSharpProjectIdentifier seq * userOpName: string -> unit + + abstract member ClearCaches: unit -> unit + + abstract member DownsizeCaches: unit -> unit + + abstract member FindReferencesInFile: + fileName: string * + options: FSharpProjectOptions * + symbol: FSharp.Compiler.Symbols.FSharpSymbol * + canInvalidateProject: bool * + userOpName: string -> + NodeCode> + + abstract member FindReferencesInFile: + fileName: string * projectSnapshot: FSharpProjectSnapshot * symbol: FSharp.Compiler.Symbols.FSharpSymbol * userOpName: string -> + NodeCode> + + abstract member GetAssemblyData: + options: FSharpProjectOptions * outputFileName: string * userOpName: string -> + NodeCode + + abstract member GetAssemblyData: + projectSnapshot: FSharpProjectSnapshot * outputFileName: string * userOpName: string -> + NodeCode + + /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) + abstract member GetBackgroundCheckResultsForFileInProject: + fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode + + /// Fetch the parse information from the background compiler (which checks w.r.t. the FileSystem API) + abstract member GetBackgroundParseResultsForFileInProject: + fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode + + abstract member GetCachedCheckFileResult: + builder: IncrementalBuilder * fileName: string * sourceText: ISourceText * options: FSharpProjectOptions -> + NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> + + abstract member GetProjectOptionsFromScript: + fileName: string * + sourceText: ISourceText * + previewEnabled: bool option * + loadedTimeStamp: System.DateTime option * + otherFlags: string array option * + useFsiAuxLib: bool option * + useSdkRefs: bool option * + sdkDirOverride: string option * + assumeDotNetFramework: bool option * + optionsStamp: int64 option * + userOpName: string -> + Async + + abstract member GetSemanticClassificationForFile: + fileName: string * options: FSharpProjectOptions * userOpName: string -> + NodeCode + + abstract member GetSemanticClassificationForFile: + fileName: string * snapshot: FSharpProjectSnapshot * userOpName: string -> + NodeCode + + abstract member InvalidateConfiguration: options: FSharpProjectOptions * userOpName: string -> unit + + abstract member NotifyFileChanged: fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode + + abstract member NotifyProjectCleaned: options: FSharpProjectOptions * userOpName: string -> Async + + /// Parses and checks the source file and returns untyped AST and check results. + abstract member ParseAndCheckFileInProject: + fileName: string * fileVersion: int * sourceText: ISourceText * options: FSharpProjectOptions * userOpName: string -> + NodeCode + + abstract member ParseAndCheckFileInProject: + fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> + NodeCode + + /// Parse and typecheck the whole project. + abstract member ParseAndCheckProject: options: FSharpProjectOptions * userOpName: string -> NodeCode + + abstract member ParseAndCheckProject: projectSnapshot: FSharpProjectSnapshot * userOpName: string -> NodeCode + + abstract member ParseFile: + fileName: string * sourceText: ISourceText * options: FSharpParsingOptions * cache: bool * flatErrors: bool * userOpName: string -> + Async + + abstract member ParseFile: + fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> Async + + /// Try to get recent approximate type check results for a file. + abstract member TryGetRecentCheckResultsForFile: + fileName: string * options: FSharpProjectOptions * sourceText: ISourceText option * userOpName: string -> + (FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash) option + + abstract member BeforeBackgroundFileCheck: IEvent + + abstract member FileChecked: IEvent + + abstract member FileParsed: IEvent + + abstract member FrameworkImportsCache: FrameworkImportsCache + + abstract member ProjectChecked: IEvent + +type internal ParseCacheLockToken() = + interface LockToken + +type CheckFileCacheKey = FileName * SourceTextHash * FSharpProjectOptions +type CheckFileCacheValue = FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash * DateTime + +[] +module internal EnvMisc = + let braceMatchCacheSize = GetEnvInteger "FCS_BraceMatchCacheSize" 5 + let parseFileCacheSize = GetEnvInteger "FCS_ParseFileCacheSize" 2 + let checkFileInProjectCacheSize = GetEnvInteger "FCS_CheckFileInProjectCacheSize" 10 + + let projectCacheSizeDefault = GetEnvInteger "FCS_ProjectCacheSizeDefault" 3 + + let frameworkTcImportsCacheStrongSize = + GetEnvInteger "FCS_frameworkTcImportsCacheStrongSizeDefault" 8 + +[] +module internal Helpers = + + /// Determine whether two (fileName,options) keys are identical w.r.t. affect on checking + let AreSameForChecking2 ((fileName1: string, options1: FSharpProjectOptions), (fileName2, options2)) = + (fileName1 = fileName2) + && FSharpProjectOptions.AreSameForChecking(options1, options2) + + /// Determine whether two (fileName,options) keys should be identical w.r.t. resource usage + let AreSubsumable2 ((fileName1: string, o1: FSharpProjectOptions), (fileName2: string, o2: FSharpProjectOptions)) = + (fileName1 = fileName2) && FSharpProjectOptions.UseSameProject(o1, o2) + + /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. parsing + let AreSameForParsing ((fileName1: string, source1Hash: int64, options1), (fileName2, source2Hash, options2)) = + fileName1 = fileName2 && options1 = options2 && source1Hash = source2Hash + + let AreSimilarForParsing ((fileName1, _, _), (fileName2, _, _)) = fileName1 = fileName2 + + /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. checking + let AreSameForChecking3 ((fileName1: string, source1Hash: int64, options1: FSharpProjectOptions), (fileName2, source2Hash, options2)) = + (fileName1 = fileName2) + && FSharpProjectOptions.AreSameForChecking(options1, options2) + && source1Hash = source2Hash + + /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. resource usage + let AreSubsumable3 ((fileName1: string, _, o1: FSharpProjectOptions), (fileName2: string, _, o2: FSharpProjectOptions)) = + (fileName1 = fileName2) && FSharpProjectOptions.UseSameProject(o1, o2) + + /// If a symbol is an attribute check if given set of names contains its name without the Attribute suffix + let rec NamesContainAttribute (symbol: FSharpSymbol) names = + match symbol with + | :? FSharpMemberOrFunctionOrValue as mofov -> + mofov.DeclaringEntity + |> Option.map (fun entity -> NamesContainAttribute entity names) + |> Option.defaultValue false + | :? FSharpEntity as entity when entity.IsAttributeType && symbol.DisplayNameCore.EndsWithOrdinal "Attribute" -> + let nameWithoutAttribute = String.dropSuffix symbol.DisplayNameCore "Attribute" + names |> Set.contains nameWithoutAttribute + | _ -> false + +// There is only one instance of this type, held in FSharpChecker +type internal BackgroundCompiler + ( + legacyReferenceResolver, + projectCacheSize, + keepAssemblyContents, + keepAllBackgroundResolutions, + tryGetMetadataSnapshot, + suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + parallelReferenceResolution, + captureIdentifiersWhenParsing, + getSource: (string -> Async) option, + useChangeNotifications, + useSyntaxTreeCache + ) as self = + + let beforeFileChecked = Event() + let fileParsed = Event() + let fileChecked = Event() + let projectChecked = Event() + + // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.scriptClosureCache + /// Information about the derived script closure. + let scriptClosureCache = + MruCache( + projectCacheSize, + areSame = FSharpProjectOptions.AreSameForChecking, + areSimilar = FSharpProjectOptions.UseSameProject + ) + + let frameworkTcImportsCache = + FrameworkImportsCache(frameworkTcImportsCacheStrongSize) + + // We currently share one global dependency provider for all scripts for the FSharpChecker. + // For projects, one is used per project. + // + // Sharing one for all scripts is necessary for good performance from GetProjectOptionsFromScript, + // which requires a dependency provider to process through the project options prior to working out + // if the cached incremental builder can be used for the project. + let dependencyProviderForScripts = new DependencyProvider() + + let getProjectReferences (options: FSharpProjectOptions) userOpName = + [ + for r in options.ReferencedProjects do + + match r with + | FSharpReferencedProject.FSharpReference(nm, opts) -> + // Don't use cross-project references for FSharp.Core, since various bits of code + // require a concrete FSharp.Core to exist on-disk. The only solutions that have + // these cross-project references to FSharp.Core are VisualFSharp.sln and FSharp.sln. The ramification + // of this is that you need to build FSharp.Core to get intellisense in those projects. + + if + (try + Path.GetFileNameWithoutExtension(nm) + with _ -> + "") + <> GetFSharpCoreLibraryName() + then + { new IProjectReference with + member x.EvaluateRawContents() = + node { + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) + return! self.GetAssemblyData(opts, userOpName + ".CheckReferencedProject(" + nm + ")") + } + + member x.TryGetLogicalTimeStamp(cache) = + self.TryGetLogicalTimeStampForProject(cache, opts) + + member x.FileName = nm + } + + | FSharpReferencedProject.PEReference(getStamp, delayedReader) -> + { new IProjectReference with + member x.EvaluateRawContents() = + node { + let! ilReaderOpt = delayedReader.TryGetILModuleReader() |> NodeCode.FromCancellable + + match ilReaderOpt with + | Some ilReader -> + let ilModuleDef, ilAsmRefs = ilReader.ILModuleDef, ilReader.ILAssemblyRefs + let data = RawFSharpAssemblyData(ilModuleDef, ilAsmRefs) :> IRawFSharpAssemblyData + return ProjectAssemblyDataResult.Available data + | _ -> + // Note 'false' - if a PEReference doesn't find an ILModuleReader then we don't + // continue to try to use an on-disk DLL + return ProjectAssemblyDataResult.Unavailable false + } + + member x.TryGetLogicalTimeStamp _ = getStamp () |> Some + member x.FileName = delayedReader.OutputFile + } + + | FSharpReferencedProject.ILModuleReference(nm, getStamp, getReader) -> + { new IProjectReference with + member x.EvaluateRawContents() = + cancellable { + let ilReader = getReader () + let ilModuleDef, ilAsmRefs = ilReader.ILModuleDef, ilReader.ILAssemblyRefs + let data = RawFSharpAssemblyData(ilModuleDef, ilAsmRefs) :> IRawFSharpAssemblyData + return ProjectAssemblyDataResult.Available data + } + |> NodeCode.FromCancellable + + member x.TryGetLogicalTimeStamp _ = getStamp () |> Some + member x.FileName = nm + } + ] + + /// CreateOneIncrementalBuilder (for background type checking). Note that fsc.fs also + /// creates an incremental builder used by the command line compiler. + let CreateOneIncrementalBuilder (options: FSharpProjectOptions, userOpName) = + node { + use _ = + Activity.start "BackgroundCompiler.CreateOneIncrementalBuilder" [| Activity.Tags.project, options.ProjectFileName |] + + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CreateOneIncrementalBuilder", options.ProjectFileName) + let projectReferences = getProjectReferences options userOpName + + let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) + + let dependencyProvider = + if options.UseScriptResolutionRules then + Some dependencyProviderForScripts + else + None + + let! builderOpt, diagnostics = + IncrementalBuilder.TryCreateIncrementalBuilderForProjectOptions( + legacyReferenceResolver, + FSharpCheckerResultsSettings.defaultFSharpBinariesDir, + frameworkTcImportsCache, + loadClosure, + Array.toList options.SourceFiles, + Array.toList options.OtherOptions, + projectReferences, + options.ProjectDirectory, + options.UseScriptResolutionRules, + keepAssemblyContents, + keepAllBackgroundResolutions, + tryGetMetadataSnapshot, + suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + dependencyProvider, + parallelReferenceResolution, + captureIdentifiersWhenParsing, + getSource, + useChangeNotifications, + useSyntaxTreeCache + ) + + match builderOpt with + | None -> () + | Some builder -> + +#if !NO_TYPEPROVIDERS + // Register the behaviour that responds to CCUs being invalidated because of type + // provider Invalidate events. This invalidates the configuration in the build. + builder.ImportsInvalidatedByTypeProvider.Add(fun () -> self.InvalidateConfiguration(options, userOpName)) +#endif + + // Register the callback called just before a file is typechecked by the background builder (without recording + // errors or intellisense information). + // + // This indicates to the UI that the file type check state is dirty. If the file is open and visible then + // the UI will sooner or later request a typecheck of the file, recording errors and intellisense information. + builder.BeforeFileChecked.Add(fun file -> beforeFileChecked.Trigger(file, options)) + builder.FileParsed.Add(fun file -> fileParsed.Trigger(file, options)) + builder.FileChecked.Add(fun file -> fileChecked.Trigger(file, options)) + builder.ProjectChecked.Add(fun () -> projectChecked.Trigger options) + + return (builderOpt, diagnostics) + } + + let parseCacheLock = Lock() + + // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.parseFileInProjectCache. Most recently used cache for parsing files. + let parseFileCache = + MruCache( + parseFileCacheSize, + areSimilar = AreSimilarForParsing, + areSame = AreSameForParsing + ) + + // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.checkFileInProjectCache + // + /// Cache which holds recently seen type-checks. + /// This cache may hold out-of-date entries, in two senses + /// - there may be a more recent antecedent state available because the background build has made it available + /// - the source for the file may have changed + + // Also keyed on source. This can only be out of date if the antecedent is out of date + let checkFileInProjectCache = + MruCache>( + keepStrongly = checkFileInProjectCacheSize, + areSame = AreSameForChecking3, + areSimilar = AreSubsumable3 + ) + + // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.incrementalBuildersCache. This root typically holds more + // live information than anything else in the F# Language Service, since it holds up to 3 (projectCacheStrongSize) background project builds + // strongly. + // + /// Cache of builds keyed by options. + let gate = obj () + + let incrementalBuildersCache = + MruCache>( + keepStrongly = projectCacheSize, + keepMax = projectCacheSize, + areSame = FSharpProjectOptions.AreSameForChecking, + areSimilar = FSharpProjectOptions.UseSameProject + ) + + let tryGetBuilderNode options = + incrementalBuildersCache.TryGet(AnyCallerThread, options) + + let tryGetBuilder options : NodeCode option = + tryGetBuilderNode options |> Option.map (fun x -> x.GetOrComputeValue()) + + let tryGetSimilarBuilder options : NodeCode option = + incrementalBuildersCache.TryGetSimilar(AnyCallerThread, options) + |> Option.map (fun x -> x.GetOrComputeValue()) + + let tryGetAnyBuilder options : NodeCode option = + incrementalBuildersCache.TryGetAny(AnyCallerThread, options) + |> Option.map (fun x -> x.GetOrComputeValue()) + + let createBuilderNode (options, userOpName, ct: CancellationToken) = + lock gate (fun () -> + if ct.IsCancellationRequested then + GraphNode.FromResult(None, [||]) + else + let getBuilderNode = GraphNode(CreateOneIncrementalBuilder(options, userOpName)) + incrementalBuildersCache.Set(AnyCallerThread, options, getBuilderNode) + getBuilderNode) + + let createAndGetBuilder (options, userOpName) = + node { + let! ct = NodeCode.CancellationToken + let getBuilderNode = createBuilderNode (options, userOpName, ct) + return! getBuilderNode.GetOrComputeValue() + } + + let getOrCreateBuilder (options, userOpName) : NodeCode = + match tryGetBuilder options with + | Some getBuilder -> + node { + match! getBuilder with + | builderOpt, creationDiags when builderOpt.IsNone || not builderOpt.Value.IsReferencesInvalidated -> + return builderOpt, creationDiags + | _ -> + // The builder could be re-created, + // clear the check file caches that are associated with it. + // We must do this in order to not return stale results when references + // in the project get changed/added/removed. + parseCacheLock.AcquireLock(fun ltok -> + options.SourceFiles + |> Array.iter (fun sourceFile -> + let key = (sourceFile, 0L, options) + checkFileInProjectCache.RemoveAnySimilar(ltok, key))) + + return! createAndGetBuilder (options, userOpName) + } + | _ -> createAndGetBuilder (options, userOpName) + + let getSimilarOrCreateBuilder (options, userOpName) = + match tryGetSimilarBuilder options with + | Some res -> res + // The builder does not exist at all. Create it. + | None -> getOrCreateBuilder (options, userOpName) + + let getOrCreateBuilderWithInvalidationFlag (options, canInvalidateProject, userOpName) = + if canInvalidateProject then + getOrCreateBuilder (options, userOpName) + else + getSimilarOrCreateBuilder (options, userOpName) + + let getAnyBuilder (options, userOpName) = + match tryGetAnyBuilder options with + | Some getBuilder -> getBuilder + | _ -> getOrCreateBuilder (options, userOpName) + + static let mutable actualParseFileCount = 0 + + static let mutable actualCheckFileCount = 0 + + /// Should be a fast operation. Ensures that we have only one async lazy object per file and its hash. + let getCheckFileNode (parseResults, sourceText, fileName, options, _fileVersion, builder, tcPrior, tcInfo, creationDiags) = + + // Here we lock for the creation of the node, not its execution + parseCacheLock.AcquireLock(fun ltok -> + let key = (fileName, sourceText.GetHashCode() |> int64, options) + + match checkFileInProjectCache.TryGet(ltok, key) with + | Some res -> res + | _ -> + let res = + GraphNode( + node { + let! res = + self.CheckOneFileImplAux( + parseResults, + sourceText, + fileName, + options, + builder, + tcPrior, + tcInfo, + creationDiags + ) + + Interlocked.Increment(&actualCheckFileCount) |> ignore + return res + } + ) + + checkFileInProjectCache.Set(ltok, key, res) + res) + + member _.ParseFile + ( + fileName: string, + sourceText: ISourceText, + options: FSharpParsingOptions, + cache: bool, + flatErrors: bool, + userOpName: string + ) = + async { + use _ = + Activity.start + "BackgroundCompiler.ParseFile" + [| + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + Activity.Tags.cache, cache.ToString() + |] + + if cache then + let hash = sourceText.GetHashCode() |> int64 + + match parseCacheLock.AcquireLock(fun ltok -> parseFileCache.TryGet(ltok, (fileName, hash, options))) with + | Some res -> return res + | None -> + Interlocked.Increment(&actualParseFileCount) |> ignore + let! ct = Async.CancellationToken + + let parseDiagnostics, parseTree, anyErrors = + ParseAndCheckFile.parseFile ( + sourceText, + fileName, + options, + userOpName, + suggestNamesForErrors, + flatErrors, + captureIdentifiersWhenParsing, + ct + ) + + let res = + FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, options.SourceFiles) + + parseCacheLock.AcquireLock(fun ltok -> parseFileCache.Set(ltok, (fileName, hash, options), res)) + return res + else + let! ct = Async.CancellationToken + + let parseDiagnostics, parseTree, anyErrors = + ParseAndCheckFile.parseFile ( + sourceText, + fileName, + options, + userOpName, + false, + flatErrors, + captureIdentifiersWhenParsing, + ct + ) + + return FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, options.SourceFiles) + } + + /// Fetch the parse information from the background compiler (which checks w.r.t. the FileSystem API) + member _.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName) = + node { + use _ = + Activity.start + "BackgroundCompiler.GetBackgroundParseResultsForFileInProject" + [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, userOpName |] + + let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> + let parseTree = EmptyParsedInput(fileName, (false, false)) + return FSharpParseFileResults(creationDiags, parseTree, true, [||]) + | Some builder -> + let parseTree, _, _, parseDiagnostics = builder.GetParseResultsForFile fileName + + let parseDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + builder.TcConfig.diagnosticsOptions, + false, + fileName, + parseDiagnostics, + suggestNamesForErrors, + builder.TcConfig.flatErrors, + None + ) + + let diagnostics = [| yield! creationDiags; yield! parseDiagnostics |] + + let parseResults = + FSharpParseFileResults( + diagnostics = diagnostics, + input = parseTree, + parseHadErrors = false, + dependencyFiles = builder.AllDependenciesDeprecated + ) + + return parseResults + } + + member _.GetCachedCheckFileResult(builder: IncrementalBuilder, fileName, sourceText: ISourceText, options) = + node { + use _ = + Activity.start "BackgroundCompiler.GetCachedCheckFileResult" [| Activity.Tags.fileName, fileName |] + + let hash = sourceText.GetHashCode() |> int64 + let key = (fileName, hash, options) + + let cachedResultsOpt = + parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.TryGet(ltok, key)) + + match cachedResultsOpt with + | Some cachedResults -> + match! cachedResults.GetOrComputeValue() with + | parseResults, checkResults, _, priorTimeStamp when + (match builder.GetCheckResultsBeforeFileInProjectEvenIfStale fileName with + | None -> false + | Some(tcPrior) -> + tcPrior.ProjectTimeStamp = priorTimeStamp + && builder.AreCheckResultsBeforeFileInProjectReady(fileName)) + -> + return Some(parseResults, checkResults) + | _ -> + parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.RemoveAnySimilar(ltok, key)) + return None + | _ -> return None + } + + member private _.CheckOneFileImplAux + ( + parseResults: FSharpParseFileResults, + sourceText: ISourceText, + fileName: string, + options: FSharpProjectOptions, + builder: IncrementalBuilder, + tcPrior: PartialCheckResults, + tcInfo: TcInfo, + creationDiags: FSharpDiagnostic[] + ) : NodeCode = + + node { + // Get additional script #load closure information if applicable. + // For scripts, this will have been recorded by GetProjectOptionsFromScript. + let tcConfig = tcPrior.TcConfig + let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) + + let! checkAnswer = + FSharpCheckFileResults.CheckOneFile( + parseResults, + sourceText, + fileName, + options.ProjectFileName, + tcConfig, + tcPrior.TcGlobals, + tcPrior.TcImports, + tcInfo.tcState, + tcInfo.moduleNamesDict, + loadClosure, + tcInfo.TcDiagnostics, + options.IsIncompleteTypeCheckEnvironment, + options, + Some builder, + Array.ofList tcInfo.tcDependencyFiles, + creationDiags, + parseResults.Diagnostics, + keepAssemblyContents, + suggestNamesForErrors + ) + |> NodeCode.FromCancellable + + GraphNode.SetPreferredUILang tcConfig.preferredUiLang + return (parseResults, checkAnswer, sourceText.GetHashCode() |> int64, tcPrior.ProjectTimeStamp) + } + + member private bc.CheckOneFileImpl + ( + parseResults: FSharpParseFileResults, + sourceText: ISourceText, + fileName: string, + options: FSharpProjectOptions, + fileVersion: int, + builder: IncrementalBuilder, + tcPrior: PartialCheckResults, + tcInfo: TcInfo, + creationDiags: FSharpDiagnostic[] + ) = + + node { + match! bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) with + | Some(_, results) -> return FSharpCheckFileAnswer.Succeeded results + | _ -> + let lazyCheckFile = + getCheckFileNode (parseResults, sourceText, fileName, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) + + let! _, results, _, _ = lazyCheckFile.GetOrComputeValue() + return FSharpCheckFileAnswer.Succeeded results + } + + /// Type-check the result obtained by parsing, but only if the antecedent type checking context is available. + member bc.CheckFileInProjectAllowingStaleCachedResults + ( + parseResults: FSharpParseFileResults, + fileName, + fileVersion, + sourceText: ISourceText, + options, + userOpName + ) = + node { + use _ = + Activity.start + "BackgroundCompiler.CheckFileInProjectAllowingStaleCachedResults" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + |] + + let! cachedResults = + node { + let! builderOpt, creationDiags = getAnyBuilder (options, userOpName) + + match builderOpt with + | Some builder -> + match! bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) with + | Some(_, checkResults) -> return Some(builder, creationDiags, Some(FSharpCheckFileAnswer.Succeeded checkResults)) + | _ -> return Some(builder, creationDiags, None) + | _ -> return None // the builder wasn't ready + } + + match cachedResults with + | None -> return None + | Some(_, _, Some x) -> return Some x + | Some(builder, creationDiags, None) -> + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CheckFileInProjectAllowingStaleCachedResults.CacheMiss", fileName) + + match builder.GetCheckResultsBeforeFileInProjectEvenIfStale fileName with + | Some tcPrior -> + match tcPrior.TryPeekTcInfo() with + | Some tcInfo -> + let! checkResults = + bc.CheckOneFileImpl( + parseResults, + sourceText, + fileName, + options, + fileVersion, + builder, + tcPrior, + tcInfo, + creationDiags + ) + + return Some checkResults + | None -> return None + | None -> return None // the incremental builder was not up to date + } + + /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. + member bc.CheckFileInProject + ( + parseResults: FSharpParseFileResults, + fileName, + fileVersion, + sourceText: ISourceText, + options, + userOpName + ) = + node { + use _ = + Activity.start + "BackgroundCompiler.CheckFileInProject" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + |] + + let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> + return FSharpCheckFileAnswer.Succeeded(FSharpCheckFileResults.MakeEmpty(fileName, creationDiags, keepAssemblyContents)) + | Some builder -> + // Check the cache. We can only use cached results when there is no work to do to bring the background builder up-to-date + let! cachedResults = bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) + + match cachedResults with + | Some(_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults + | _ -> + let! tcPrior = builder.GetCheckResultsBeforeFileInProject fileName + let! tcInfo = tcPrior.GetOrComputeTcInfo() + + return! + bc.CheckOneFileImpl( + parseResults, + sourceText, + fileName, + options, + fileVersion, + builder, + tcPrior, + tcInfo, + creationDiags + ) + } + + /// Parses and checks the source file and returns untyped AST and check results. + member bc.ParseAndCheckFileInProject + ( + fileName: string, + fileVersion, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName + ) = + node { + use _ = + Activity.start + "BackgroundCompiler.ParseAndCheckFileInProject" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + |] + + let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> + let parseTree = EmptyParsedInput(fileName, (false, false)) + let parseResults = FSharpParseFileResults(creationDiags, parseTree, true, [||]) + return (parseResults, FSharpCheckFileAnswer.Aborted) + + | Some builder -> + let! cachedResults = bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) + + match cachedResults with + | Some(parseResults, checkResults) -> return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) + | _ -> + let! tcPrior = builder.GetCheckResultsBeforeFileInProject fileName + let! tcInfo = tcPrior.GetOrComputeTcInfo() + // Do the parsing. + let parsingOptions = + FSharpParsingOptions.FromTcConfig( + builder.TcConfig, + Array.ofList builder.SourceFiles, + options.UseScriptResolutionRules + ) + + GraphNode.SetPreferredUILang tcPrior.TcConfig.preferredUiLang + let! ct = NodeCode.CancellationToken + + let parseDiagnostics, parseTree, anyErrors = + ParseAndCheckFile.parseFile ( + sourceText, + fileName, + parsingOptions, + userOpName, + suggestNamesForErrors, + builder.TcConfig.flatErrors, + captureIdentifiersWhenParsing, + ct + ) + + let parseResults = + FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, builder.AllDependenciesDeprecated) + + let! checkResults = + bc.CheckOneFileImpl( + parseResults, + sourceText, + fileName, + options, + fileVersion, + builder, + tcPrior, + tcInfo, + creationDiags + ) + + return (parseResults, checkResults) + } + + member _.NotifyFileChanged(fileName, options, userOpName) = + node { + use _ = + Activity.start + "BackgroundCompiler.NotifyFileChanged" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + |] + + let! builderOpt, _ = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> return () + | Some builder -> do! builder.NotifyFileChanged(fileName, DateTime.UtcNow) + } + + /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) + member _.GetBackgroundCheckResultsForFileInProject(fileName, options, userOpName) = + node { + use _ = + Activity.start + "BackgroundCompiler.ParseAndCheckFileInProject" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + |] + + let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> + let parseTree = EmptyParsedInput(fileName, (false, false)) + let parseResults = FSharpParseFileResults(creationDiags, parseTree, true, [||]) + let typedResults = FSharpCheckFileResults.MakeEmpty(fileName, creationDiags, true) + return (parseResults, typedResults) + | Some builder -> + let parseTree, _, _, parseDiagnostics = builder.GetParseResultsForFile fileName + let! tcProj = builder.GetFullCheckResultsAfterFileInProject fileName + + let! tcInfo, tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() + + let tcResolutions = tcInfoExtras.tcResolutions + let tcSymbolUses = tcInfoExtras.tcSymbolUses + let tcOpenDeclarations = tcInfoExtras.tcOpenDeclarations + let latestCcuSigForFile = tcInfo.latestCcuSigForFile + let tcState = tcInfo.tcState + let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile + let latestImplementationFile = tcInfoExtras.latestImplFile + let tcDependencyFiles = tcInfo.tcDependencyFiles + let tcDiagnostics = tcInfo.TcDiagnostics + let diagnosticsOptions = builder.TcConfig.diagnosticsOptions + + let symbolEnv = + SymbolEnv(tcProj.TcGlobals, tcInfo.tcState.Ccu, Some tcInfo.tcState.CcuSig, tcProj.TcImports) + |> Some + + let parseDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + diagnosticsOptions, + false, + fileName, + parseDiagnostics, + suggestNamesForErrors, + builder.TcConfig.flatErrors, + None + ) + + let parseDiagnostics = [| yield! creationDiags; yield! parseDiagnostics |] + + let tcDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + diagnosticsOptions, + false, + fileName, + tcDiagnostics, + suggestNamesForErrors, + builder.TcConfig.flatErrors, + symbolEnv + ) + + let tcDiagnostics = [| yield! creationDiags; yield! tcDiagnostics |] + + let parseResults = + FSharpParseFileResults( + diagnostics = parseDiagnostics, + input = parseTree, + parseHadErrors = false, + dependencyFiles = builder.AllDependenciesDeprecated + ) + + let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) + + let typedResults = + FSharpCheckFileResults.Make( + fileName, + options.ProjectFileName, + tcProj.TcConfig, + tcProj.TcGlobals, + options.IsIncompleteTypeCheckEnvironment, + Some builder, + options, + Array.ofList tcDependencyFiles, + creationDiags, + parseResults.Diagnostics, + tcDiagnostics, + keepAssemblyContents, + Option.get latestCcuSigForFile, + tcState.Ccu, + tcProj.TcImports, + tcEnvAtEnd.AccessRights, + tcResolutions, + tcSymbolUses, + tcEnvAtEnd.NameEnv, + loadClosure, + latestImplementationFile, + tcOpenDeclarations + ) + + return (parseResults, typedResults) + } + + member _.FindReferencesInFile + ( + fileName: string, + options: FSharpProjectOptions, + symbol: FSharpSymbol, + canInvalidateProject: bool, + userOpName: string + ) = + node { + use _ = + Activity.start + "BackgroundCompiler.FindReferencesInFile" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + "symbol", symbol.FullName + |] + + let! builderOpt, _ = getOrCreateBuilderWithInvalidationFlag (options, canInvalidateProject, userOpName) + + match builderOpt with + | None -> return Seq.empty + | Some builder -> + if builder.ContainsFile fileName then + let! checkResults = builder.GetFullCheckResultsAfterFileInProject fileName + let! keyStoreOpt = checkResults.GetOrComputeItemKeyStoreIfEnabled() + + match keyStoreOpt with + | None -> return Seq.empty + | Some reader -> return reader.FindAll symbol.Item + else + return Seq.empty + } + + member _.GetSemanticClassificationForFile(fileName: string, options: FSharpProjectOptions, userOpName: string) = + node { + use _ = + Activity.start + "BackgroundCompiler.GetSemanticClassificationForFile" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + |] + + let! builderOpt, _ = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> return None + | Some builder -> + let! checkResults = builder.GetFullCheckResultsAfterFileInProject fileName + let! scopt = checkResults.GetOrComputeSemanticClassificationIfEnabled() + + match scopt with + | None -> return None + | Some sc -> return Some(sc.GetView()) + } + + /// Try to get recent approximate type check results for a file. + member _.TryGetRecentCheckResultsForFile + ( + fileName: string, + options: FSharpProjectOptions, + sourceText: ISourceText option, + _userOpName: string + ) = + use _ = + Activity.start + "BackgroundCompiler.GetSemanticClassificationForFile" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, _userOpName + |] + + match sourceText with + | Some sourceText -> + let hash = sourceText.GetHashCode() |> int64 + + let resOpt = + parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.TryGet(ltok, (fileName, hash, options))) + + match resOpt with + | Some res -> + match res.TryPeekValue() with + | ValueSome(a, b, c, _) -> Some(a, b, c) + | ValueNone -> None + | None -> None + | None -> None + + /// Parse and typecheck the whole project (the implementation, called recursively as project graph is evaluated) + member private _.ParseAndCheckProjectImpl(options, userOpName) = + node { + + let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> + let emptyResults = + FSharpCheckProjectResults(options.ProjectFileName, None, keepAssemblyContents, creationDiags, None) + + return emptyResults + | Some builder -> + let! tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt = builder.GetFullCheckResultsAndImplementationsForProject() + let diagnosticsOptions = tcProj.TcConfig.diagnosticsOptions + let fileName = DummyFileNameForRangesWithoutASpecificLocation + + // Although we do not use 'tcInfoExtras', computing it will make sure we get an extra info. + let! tcInfo, _tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() + + let topAttribs = tcInfo.topAttribs + let tcState = tcInfo.tcState + let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile + let tcDiagnostics = tcInfo.TcDiagnostics + let tcDependencyFiles = tcInfo.tcDependencyFiles + + let symbolEnv = + SymbolEnv(tcProj.TcGlobals, tcInfo.tcState.Ccu, Some tcInfo.tcState.CcuSig, tcProj.TcImports) + |> Some + + let tcDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + diagnosticsOptions, + true, + fileName, + tcDiagnostics, + suggestNamesForErrors, + builder.TcConfig.flatErrors, + symbolEnv + ) + + let diagnostics = [| yield! creationDiags; yield! tcDiagnostics |] + + let getAssemblyData () = + match tcAssemblyDataOpt with + | ProjectAssemblyDataResult.Available data -> Some data + | _ -> None + + let details = + (tcProj.TcGlobals, + tcProj.TcImports, + tcState.Ccu, + tcState.CcuSig, + Choice1Of2 builder, + topAttribs, + getAssemblyData, + ilAssemRef, + tcEnvAtEnd.AccessRights, + tcAssemblyExprOpt, + Array.ofList tcDependencyFiles, + options) + + let results = + FSharpCheckProjectResults( + options.ProjectFileName, + Some tcProj.TcConfig, + keepAssemblyContents, + diagnostics, + Some details + ) + + return results + } + + member _.GetAssemblyData(options, userOpName) = + node { + use _ = + Activity.start + "BackgroundCompiler.GetAssemblyData" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.userOpName, userOpName + |] + + let! builderOpt, _ = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> return ProjectAssemblyDataResult.Unavailable true + | Some builder -> + let! _, _, tcAssemblyDataOpt, _ = builder.GetCheckResultsAndImplementationsForProject() + return tcAssemblyDataOpt + } + + /// Get the timestamp that would be on the output if fully built immediately + member private _.TryGetLogicalTimeStampForProject(cache, options) = + match tryGetBuilderNode options with + | Some lazyWork -> + match lazyWork.TryPeekValue() with + | ValueSome(Some builder, _) -> Some(builder.GetLogicalTimeStampForProject(cache)) + | _ -> None + | _ -> None + + /// Parse and typecheck the whole project. + member bc.ParseAndCheckProject(options, userOpName) = + use _ = + Activity.start + "BackgroundCompiler.ParseAndCheckProject" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.userOpName, userOpName + |] + + bc.ParseAndCheckProjectImpl(options, userOpName) + + member _.GetProjectOptionsFromScript + ( + fileName, + sourceText, + previewEnabled, + loadedTimeStamp, + otherFlags, + useFsiAuxLib: bool option, + useSdkRefs: bool option, + sdkDirOverride: string option, + assumeDotNetFramework: bool option, + optionsStamp: int64 option, + _userOpName + ) = + use _ = + Activity.start + "BackgroundCompiler.GetProjectOptionsFromScript" + [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, _userOpName |] + + cancellable { + // Do we add a reference to FSharp.Compiler.Interactive.Settings by default? + let useFsiAuxLib = defaultArg useFsiAuxLib true + let useSdkRefs = defaultArg useSdkRefs true + let reduceMemoryUsage = ReduceMemoryFlag.Yes + let previewEnabled = defaultArg previewEnabled false + + // Do we assume .NET Framework references for scripts? + let assumeDotNetFramework = defaultArg assumeDotNetFramework true + + let! ct = Cancellable.token () + use _ = Cancellable.UsingToken(ct) + + let extraFlags = + if previewEnabled then + [| "--langversion:preview" |] + else + [||] + + let otherFlags = defaultArg otherFlags extraFlags + + use diagnostics = new DiagnosticsScope(otherFlags |> Array.contains "--flaterrors") + + let useSimpleResolution = + otherFlags |> Array.exists (fun x -> x = "--simpleresolution") + + let loadedTimeStamp = defaultArg loadedTimeStamp DateTime.MaxValue // Not 'now', we don't want to force reloading + + let applyCompilerOptions tcConfigB = + let fsiCompilerOptions = GetCoreFsiCompilerOptions tcConfigB + ParseCompilerOptions(ignore, fsiCompilerOptions, Array.toList otherFlags) + + let loadClosure = + LoadClosure.ComputeClosureOfScriptText( + legacyReferenceResolver, + FSharpCheckerResultsSettings.defaultFSharpBinariesDir, + fileName, + sourceText, + CodeContext.Editing, + useSimpleResolution, + useFsiAuxLib, + useSdkRefs, + sdkDirOverride, + Lexhelp.LexResourceManager(), + applyCompilerOptions, + assumeDotNetFramework, + tryGetMetadataSnapshot, + reduceMemoryUsage, + dependencyProviderForScripts + ) + + let otherFlags = + [| + yield "--noframework" + yield "--warn:3" + yield! otherFlags + for r in loadClosure.References do + yield "-r:" + fst r + for code, _ in loadClosure.NoWarns do + yield "--nowarn:" + code + |] + + let options = + { + ProjectFileName = fileName + ".fsproj" // Make a name that is unique in this directory. + ProjectId = None + SourceFiles = loadClosure.SourceFiles |> List.map fst |> List.toArray + OtherOptions = otherFlags + ReferencedProjects = [||] + IsIncompleteTypeCheckEnvironment = false + UseScriptResolutionRules = true + LoadTime = loadedTimeStamp + UnresolvedReferences = Some(FSharpUnresolvedReferencesSet(loadClosure.UnresolvedReferences)) + OriginalLoadReferences = loadClosure.OriginalLoadReferences + Stamp = optionsStamp + } + + scriptClosureCache.Set(AnyCallerThread, options, loadClosure) // Save the full load closure for later correlation. + + let diags = + loadClosure.LoadClosureRootFileDiagnostics + |> List.map (fun (exn, isError) -> + FSharpDiagnostic.CreateFromException( + exn, + isError, + range.Zero, + false, + options.OtherOptions |> Array.contains "--flaterrors", + None + )) + + return options, (diags @ diagnostics.Diagnostics) + } + |> Cancellable.toAsync + + member bc.InvalidateConfiguration(options: FSharpProjectOptions, userOpName) = + use _ = + Activity.start + "BackgroundCompiler.InvalidateConfiguration" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.userOpName, userOpName + |] + + if incrementalBuildersCache.ContainsSimilarKey(AnyCallerThread, options) then + parseCacheLock.AcquireLock(fun ltok -> + for sourceFile in options.SourceFiles do + checkFileInProjectCache.RemoveAnySimilar(ltok, (sourceFile, 0L, options))) + + let _ = createBuilderNode (options, userOpName, CancellationToken.None) + () + + member bc.ClearCache(options: seq, _userOpName) = + use _ = + Activity.start "BackgroundCompiler.ClearCache" [| Activity.Tags.userOpName, _userOpName |] + + lock gate (fun () -> + options + |> Seq.iter (fun options -> + incrementalBuildersCache.RemoveAnySimilar(AnyCallerThread, options) + + parseCacheLock.AcquireLock(fun ltok -> + for sourceFile in options.SourceFiles do + checkFileInProjectCache.RemoveAnySimilar(ltok, (sourceFile, 0L, options))))) + + member _.NotifyProjectCleaned(options: FSharpProjectOptions, userOpName) = + use _ = + Activity.start + "BackgroundCompiler.NotifyProjectCleaned" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.userOpName, userOpName + |] + + async { + + let! ct = Async.CancellationToken + // If there was a similar entry (as there normally will have been) then re-establish an empty builder . This + // is a somewhat arbitrary choice - it will have the effect of releasing memory associated with the previous + // builder, but costs some time. + if incrementalBuildersCache.ContainsSimilarKey(AnyCallerThread, options) then + let _ = createBuilderNode (options, userOpName, ct) + () + } + + member _.BeforeBackgroundFileCheck = beforeFileChecked.Publish + + member _.FileParsed = fileParsed.Publish + + member _.FileChecked = fileChecked.Publish + + member _.ProjectChecked = projectChecked.Publish + + member _.ClearCaches() = + use _ = Activity.startNoTags "BackgroundCompiler.ClearCaches" + + lock gate (fun () -> + parseCacheLock.AcquireLock(fun ltok -> + checkFileInProjectCache.Clear(ltok) + parseFileCache.Clear(ltok)) + + incrementalBuildersCache.Clear(AnyCallerThread) + frameworkTcImportsCache.Clear() + scriptClosureCache.Clear AnyCallerThread) + + member _.DownsizeCaches() = + use _ = Activity.startNoTags "BackgroundCompiler.DownsizeCaches" + + lock gate (fun () -> + parseCacheLock.AcquireLock(fun ltok -> + checkFileInProjectCache.Resize(ltok, newKeepStrongly = 1) + parseFileCache.Resize(ltok, newKeepStrongly = 1)) + + incrementalBuildersCache.Resize(AnyCallerThread, newKeepStrongly = 1, newKeepMax = 1) + frameworkTcImportsCache.Downsize() + scriptClosureCache.Resize(AnyCallerThread, newKeepStrongly = 1, newKeepMax = 1)) + + member _.FrameworkImportsCache = frameworkTcImportsCache + + static member ActualParseFileCount = actualParseFileCount + + static member ActualCheckFileCount = actualCheckFileCount + + interface IBackgroundCompiler with + + member _.BeforeBackgroundFileCheck = self.BeforeBackgroundFileCheck + + member _.CheckFileInProject + ( + parseResults: FSharpParseFileResults, + fileName: string, + fileVersion: int, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + self.CheckFileInProject(parseResults, fileName, fileVersion, sourceText, options, userOpName) + + member _.CheckFileInProjectAllowingStaleCachedResults + ( + parseResults: FSharpParseFileResults, + fileName: string, + fileVersion: int, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + self.CheckFileInProjectAllowingStaleCachedResults(parseResults, fileName, fileVersion, sourceText, options, userOpName) + + member _.ClearCache(options: seq, userOpName: string) : unit = self.ClearCache(options, userOpName) + + member _.ClearCache(projects: ProjectSnapshot.FSharpProjectIdentifier seq, userOpName: string) = ignore (projects, userOpName) + + member _.ClearCaches() : unit = self.ClearCaches() + member _.DownsizeCaches() : unit = self.DownsizeCaches() + member _.FileChecked: IEvent = self.FileChecked + member _.FileParsed: IEvent = self.FileParsed + + member _.FindReferencesInFile + ( + fileName: string, + options: FSharpProjectOptions, + symbol: FSharpSymbol, + canInvalidateProject: bool, + userOpName: string + ) : NodeCode> = + self.FindReferencesInFile(fileName, options, symbol, canInvalidateProject, userOpName) + + member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = + this.FindReferencesInFile(fileName, projectSnapshot.ToOptions(), symbol, true, userOpName) + + member _.FrameworkImportsCache: FrameworkImportsCache = self.FrameworkImportsCache + + member _.GetAssemblyData + ( + options: FSharpProjectOptions, + _fileName: string, + userOpName: string + ) : NodeCode = + self.GetAssemblyData(options, userOpName) + + member _.GetAssemblyData + ( + projectSnapshot: FSharpProjectSnapshot, + _fileName: string, + userOpName: string + ) : NodeCode = + self.GetAssemblyData(projectSnapshot.ToOptions(), userOpName) + + member _.GetBackgroundCheckResultsForFileInProject + ( + fileName: string, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + self.GetBackgroundCheckResultsForFileInProject(fileName, options, userOpName) + + member _.GetBackgroundParseResultsForFileInProject + ( + fileName: string, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + self.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName) + + member _.GetCachedCheckFileResult + ( + builder: IncrementalBuilder, + fileName: string, + sourceText: ISourceText, + options: FSharpProjectOptions + ) : NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> = + self.GetCachedCheckFileResult(builder, fileName, sourceText, options) + + member _.GetProjectOptionsFromScript + ( + fileName: string, + sourceText: ISourceText, + previewEnabled: bool option, + loadedTimeStamp: DateTime option, + otherFlags: string array option, + useFsiAuxLib: bool option, + useSdkRefs: bool option, + sdkDirOverride: string option, + assumeDotNetFramework: bool option, + optionsStamp: int64 option, + userOpName: string + ) : Async = + self.GetProjectOptionsFromScript( + fileName, + sourceText, + previewEnabled, + loadedTimeStamp, + otherFlags, + useFsiAuxLib, + useSdkRefs, + sdkDirOverride, + assumeDotNetFramework, + optionsStamp, + userOpName + ) + + member _.GetSemanticClassificationForFile + ( + fileName: string, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + self.GetSemanticClassificationForFile(fileName, options, userOpName) + + member _.GetSemanticClassificationForFile + ( + fileName: string, + snapshot: FSharpProjectSnapshot, + userOpName: string + ) : NodeCode = + self.GetSemanticClassificationForFile(fileName, snapshot.ToOptions(), userOpName) + + member _.InvalidateConfiguration(options: FSharpProjectOptions, userOpName: string) : unit = + self.InvalidateConfiguration(options, userOpName) + + member _.NotifyFileChanged(fileName: string, options: FSharpProjectOptions, userOpName: string) : NodeCode = + self.NotifyFileChanged(fileName, options, userOpName) + + member _.NotifyProjectCleaned(options: FSharpProjectOptions, userOpName: string) : Async = + self.NotifyProjectCleaned(options, userOpName) + + member _.ParseAndCheckFileInProject + ( + fileName: string, + fileVersion: int, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + self.ParseAndCheckFileInProject(fileName, fileVersion, sourceText, options, userOpName) + + member _.ParseAndCheckFileInProject + ( + fileName: string, + projectSnapshot: FSharpProjectSnapshot, + userOpName: string + ) : NodeCode = + node { + let fileSnapshot = + projectSnapshot.ProjectSnapshot.SourceFiles + |> Seq.find (fun f -> f.FileName = fileName) + + let! sourceText = fileSnapshot.GetSource() |> NodeCode.AwaitTask + let options = projectSnapshot.ToOptions() + + return! self.ParseAndCheckFileInProject(fileName, 0, sourceText, options, userOpName) + } + + member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = + self.ParseAndCheckProject(options, userOpName) + + member _.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = + self.ParseAndCheckProject(projectSnapshot.ToOptions(), userOpName) + + member _.ParseFile + ( + fileName: string, + sourceText: ISourceText, + options: FSharpParsingOptions, + cache: bool, + flatErrors: bool, + userOpName: string + ) = + self.ParseFile(fileName, sourceText, options, cache, flatErrors, userOpName) + + member _.ParseFile(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = + let options = projectSnapshot.ToOptions() + + self.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName) + |> Async.AwaitNodeCode + + member _.ProjectChecked: IEvent = self.ProjectChecked + + member _.TryGetRecentCheckResultsForFile + ( + fileName: string, + options: FSharpProjectOptions, + sourceText: ISourceText option, + userOpName: string + ) : (FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash) option = + self.TryGetRecentCheckResultsForFile(fileName, options, sourceText, userOpName) diff --git a/src/Compiler/Service/BackgroundCompiler.fsi b/src/Compiler/Service/BackgroundCompiler.fsi new file mode 100644 index 00000000000..f3bf3c96ccc --- /dev/null +++ b/src/Compiler/Service/BackgroundCompiler.fsi @@ -0,0 +1,224 @@ +namespace FSharp.Compiler.CodeAnalysis + +open FSharp.Compiler.Text +open FSharp.Compiler.BuildGraph + +open System.Reflection +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CompilerConfig +open FSharp.Compiler.Diagnostics + +type SourceTextHash = int64 + +type CacheStamp = int64 + +type FileName = string + +type FilePath = string + +type ProjectPath = string + +type FileVersion = int + +type FSharpProjectSnapshot = ProjectSnapshot.FSharpProjectSnapshot + +type internal IBackgroundCompiler = + + /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. + abstract CheckFileInProject: + parseResults: FSharpParseFileResults * + fileName: string * + fileVersion: int * + sourceText: ISourceText * + options: FSharpProjectOptions * + userOpName: string -> + NodeCode + + /// Type-check the result obtained by parsing, but only if the antecedent type checking context is available. + abstract CheckFileInProjectAllowingStaleCachedResults: + parseResults: FSharpParseFileResults * + fileName: string * + fileVersion: int * + sourceText: ISourceText * + options: FSharpProjectOptions * + userOpName: string -> + NodeCode + + abstract ClearCache: options: FSharpProjectOptions seq * userOpName: string -> unit + + abstract ClearCache: projects: ProjectSnapshot.FSharpProjectIdentifier seq * userOpName: string -> unit + + abstract ClearCaches: unit -> unit + + abstract DownsizeCaches: unit -> unit + + abstract FindReferencesInFile: + fileName: string * + projectSnapshot: FSharpProjectSnapshot * + symbol: FSharp.Compiler.Symbols.FSharpSymbol * + userOpName: string -> + NodeCode + + abstract FindReferencesInFile: + fileName: string * + options: FSharpProjectOptions * + symbol: FSharp.Compiler.Symbols.FSharpSymbol * + canInvalidateProject: bool * + userOpName: string -> + NodeCode + + abstract GetAssemblyData: + projectSnapshot: FSharpProjectSnapshot * outputFileName: string * userOpName: string -> + NodeCode + + abstract GetAssemblyData: + options: FSharpProjectOptions * outputFileName: string * userOpName: string -> + NodeCode + + /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) + abstract GetBackgroundCheckResultsForFileInProject: + fileName: string * options: FSharpProjectOptions * userOpName: string -> + NodeCode + + /// Fetch the parse information from the background compiler (which checks w.r.t. the FileSystem API) + abstract GetBackgroundParseResultsForFileInProject: + fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode + + abstract GetCachedCheckFileResult: + builder: IncrementalBuilder * fileName: string * sourceText: ISourceText * options: FSharpProjectOptions -> + NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> + + abstract GetProjectOptionsFromScript: + fileName: string * + sourceText: ISourceText * + previewEnabled: bool option * + loadedTimeStamp: System.DateTime option * + otherFlags: string array option * + useFsiAuxLib: bool option * + useSdkRefs: bool option * + sdkDirOverride: string option * + assumeDotNetFramework: bool option * + optionsStamp: int64 option * + userOpName: string -> + Async + + abstract GetSemanticClassificationForFile: + fileName: string * snapshot: FSharpProjectSnapshot * userOpName: string -> + NodeCode + + abstract GetSemanticClassificationForFile: + fileName: string * options: FSharpProjectOptions * userOpName: string -> + NodeCode + + abstract InvalidateConfiguration: options: FSharpProjectOptions * userOpName: string -> unit + + abstract NotifyFileChanged: fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode + + abstract NotifyProjectCleaned: options: FSharpProjectOptions * userOpName: string -> Async + + abstract ParseAndCheckFileInProject: + fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> + NodeCode + + /// Parses and checks the source file and returns untyped AST and check results. + abstract ParseAndCheckFileInProject: + fileName: string * + fileVersion: int * + sourceText: ISourceText * + options: FSharpProjectOptions * + userOpName: string -> + NodeCode + + abstract ParseAndCheckProject: + projectSnapshot: FSharpProjectSnapshot * userOpName: string -> NodeCode + + /// Parse and typecheck the whole project. + abstract ParseAndCheckProject: + options: FSharpProjectOptions * userOpName: string -> NodeCode + + abstract ParseFile: + fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> Async + + abstract ParseFile: + fileName: string * + sourceText: ISourceText * + options: FSharpParsingOptions * + cache: bool * + flatErrors: bool * + userOpName: string -> + Async + + /// Try to get recent approximate type check results for a file. + abstract TryGetRecentCheckResultsForFile: + fileName: string * options: FSharpProjectOptions * sourceText: ISourceText option * userOpName: string -> + (FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash) option + + abstract BeforeBackgroundFileCheck: IEvent + + abstract FileChecked: IEvent + + abstract FileParsed: IEvent + + abstract FrameworkImportsCache: FrameworkImportsCache + + abstract ProjectChecked: IEvent + +[] +module internal EnvMisc = + + val braceMatchCacheSize: int + + val parseFileCacheSize: int + + val checkFileInProjectCacheSize: int + + val projectCacheSizeDefault: int + + val frameworkTcImportsCacheStrongSize: int + +[] +module internal Helpers = + + /// Determine whether two (fileName,options) keys are identical w.r.t. affect on checking + val AreSameForChecking2: (string * FSharpProjectOptions) * (string * FSharpProjectOptions) -> bool + + /// Determine whether two (fileName,options) keys should be identical w.r.t. resource usage + val AreSubsumable2: (string * FSharpProjectOptions) * (string * FSharpProjectOptions) -> bool + + /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. parsing + val AreSameForParsing: (string * int64 * 'a) * (string * int64 * 'a) -> bool when 'a: equality + + val AreSimilarForParsing: ('a * 'b * 'c) * ('a * 'd * 'e) -> bool when 'a: equality + + /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. checking + val AreSameForChecking3: (string * int64 * FSharpProjectOptions) * (string * int64 * FSharpProjectOptions) -> bool + + /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. resource usage + val AreSubsumable3: (string * 'a * FSharpProjectOptions) * (string * 'b * FSharpProjectOptions) -> bool + + /// If a symbol is an attribute check if given set of names contains its name without the Attribute suffix + val NamesContainAttribute: symbol: FSharp.Compiler.Symbols.FSharpSymbol -> names: Set -> bool + +type internal BackgroundCompiler = + interface IBackgroundCompiler + + new: + legacyReferenceResolver: LegacyReferenceResolver * + projectCacheSize: int * + keepAssemblyContents: bool * + keepAllBackgroundResolutions: bool * + tryGetMetadataSnapshot: FSharp.Compiler.AbstractIL.ILBinaryReader.ILReaderTryGetMetadataSnapshot * + suggestNamesForErrors: bool * + keepAllBackgroundSymbolUses: bool * + enableBackgroundItemKeyStoreAndSemanticClassification: bool * + enablePartialTypeChecking: bool * + parallelReferenceResolution: ParallelReferenceResolution * + captureIdentifiersWhenParsing: bool * + getSource: (string -> Async) option * + useChangeNotifications: bool * + useSyntaxTreeCache: bool -> + BackgroundCompiler + + static member ActualCheckFileCount: int + + static member ActualParseFileCount: int diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 37ae0083298..f55010a9025 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -6,6 +6,7 @@ namespace FSharp.Compiler.CodeAnalysis open System +open System.Collections.Generic open System.Diagnostics open System.IO open System.Reflection @@ -53,6 +54,9 @@ open FSharp.Compiler.TypedTreeOps open Internal.Utilities open Internal.Utilities.Collections open FSharp.Compiler.AbstractIL.ILBinaryReader +open System.Threading.Tasks +open System.Runtime.CompilerServices +open Internal.Utilities.Hashing type FSharpUnresolvedReferencesSet = FSharpUnresolvedReferencesSet of UnresolvedAssemblyReference list @@ -1801,9 +1805,9 @@ type internal TypeCheckInfo |> List.sortBy (fun d -> let n = match d.Item with - | Item.Types(_, AbbrevOrAppTy tcref :: _) -> 1 + tcref.TyparsNoRange.Length + | Item.Types(_, AbbrevOrAppTy(tcref, _) :: _) -> 1 + tcref.TyparsNoRange.Length // Put delegate ctors after types, sorted by #typars. RemoveDuplicateItems will remove FakeInterfaceCtor and DelegateCtor if an earlier type is also reported with this name - | Item.DelegateCtor(AbbrevOrAppTy tcref) -> 1000 + tcref.TyparsNoRange.Length + | Item.DelegateCtor(AbbrevOrAppTy(tcref, _)) -> 1000 + tcref.TyparsNoRange.Length // Put type ctors after types, sorted by #typars. RemoveDuplicateItems will remove DefaultStructCtors if a type is also reported with this name | Item.CtorGroup(_, cinfo :: _) -> 1000 + 10 * cinfo.DeclaringTyconRef.TyparsNoRange.Length | _ -> 0 @@ -1819,10 +1823,10 @@ type internal TypeCheckInfo items |> List.groupBy (fun d -> match d.Item with - | Item.Types(_, AbbrevOrAppTy tcref :: _) + | Item.Types(_, AbbrevOrAppTy(tcref, _) :: _) | Item.ExnCase tcref -> tcref.LogicalName | Item.UnqualifiedType(tcref :: _) - | Item.DelegateCtor(AbbrevOrAppTy tcref) -> tcref.CompiledName + | Item.DelegateCtor(AbbrevOrAppTy(tcref, _)) -> tcref.CompiledName | Item.CtorGroup(_, cinfo :: _) -> cinfo.ApparentEnclosingTyconRef.CompiledName | _ -> d.Item.DisplayName) @@ -2515,6 +2519,11 @@ module internal ParseAndCheckFile = member _.AnyErrors = errorCount > 0 + member _.CollectedPhasedDiagnostics = + [| + for struct (diagnostic, severity) in diagnosticsCollector -> diagnostic, severity + |] + member _.CollectedDiagnostics(symbolEnv: SymbolEnv option) = [| for struct (diagnostic, severity) in diagnosticsCollector do @@ -3270,7 +3279,7 @@ type FSharpCheckFileResults tcConfig, tcGlobals, isIncompleteTypeCheckEnvironment: bool, - builder: IncrementalBuilder, + builder: IncrementalBuilder option, projectOptions, dependencyFiles, creationErrors: FSharpDiagnostic[], @@ -3311,7 +3320,7 @@ type FSharpCheckFileResults let errors = FSharpCheckFileResults.JoinErrors(isIncompleteTypeCheckEnvironment, creationErrors, parseErrors, tcErrors) - FSharpCheckFileResults(mainInputFileName, errors, Some tcFileInfo, dependencyFiles, Some builder, keepAssemblyContents) + FSharpCheckFileResults(mainInputFileName, errors, Some tcFileInfo, dependencyFiles, builder, keepAssemblyContents) static member CheckOneFile ( @@ -3328,7 +3337,7 @@ type FSharpCheckFileResults backgroundDiagnostics: (PhasedDiagnostic * FSharpDiagnosticSeverity)[], isIncompleteTypeCheckEnvironment: bool, projectOptions: FSharpProjectOptions, - builder: IncrementalBuilder, + builder: IncrementalBuilder option, dependencyFiles: string[], creationErrors: FSharpDiagnostic[], parseErrors: FSharpDiagnostic[], @@ -3357,7 +3366,7 @@ type FSharpCheckFileResults FSharpCheckFileResults.JoinErrors(isIncompleteTypeCheckEnvironment, creationErrors, parseErrors, tcErrors) let results = - FSharpCheckFileResults(mainInputFileName, errors, Some tcFileInfo, dependencyFiles, Some builder, keepAssemblyContents) + FSharpCheckFileResults(mainInputFileName, errors, Some tcFileInfo, dependencyFiles, builder, keepAssemblyContents) return results } @@ -3375,7 +3384,7 @@ type FSharpCheckProjectResults TcImports * CcuThunk * ModuleOrNamespaceType * - Choice * + Choice> * TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * @@ -3413,6 +3422,7 @@ type FSharpCheckProjectResults FSharpAssemblySignature(tcGlobals, thisCcu, ccuSig, tcImports, topAttribs, ccuSig) + // TODO: Looks like we don't need this member _.TypedImplementationFiles = if not keepAssemblyContents then invalidOp @@ -3473,6 +3483,7 @@ type FSharpCheckProjectResults FSharpAssemblyContents(tcGlobals, thisCcu, Some ccuSig, tcImports, mimpls) // Not, this does not have to be a SyncOp, it can be called from any thread + // TODO: this should be async member _.GetUsesOfSymbol(symbol: FSharpSymbol, ?cancellationToken: CancellationToken) = let _, _, _, _, builderOrSymbolUses, _, _, _, _, _, _, _ = getDetails () @@ -3488,7 +3499,20 @@ type FSharpCheckProjectResults | Some(_, tcInfoExtras) -> tcInfoExtras.TcSymbolUses.GetUsesOfSymbol symbol.Item | _ -> [||] | _ -> [||]) - | Choice2Of2 tcSymbolUses -> tcSymbolUses.GetUsesOfSymbol symbol.Item + |> Array.toSeq + | Choice2Of2 task -> + Async.RunSynchronously( + async { + let! tcSymbolUses = task + + return + seq { + for symbolUses in tcSymbolUses do + yield! symbolUses.GetUsesOfSymbol symbol.Item + } + }, + ?cancellationToken = cancellationToken + ) results |> Seq.filter (fun symbolUse -> symbolUse.ItemOccurence <> ItemOccurence.RelatedText) @@ -3500,6 +3524,7 @@ type FSharpCheckProjectResults |> Seq.toArray // Not, this does not have to be a SyncOp, it can be called from any thread + // TODO: this should be async member _.GetAllUsesOfAllSymbols(?cancellationToken: CancellationToken) = let tcGlobals, tcImports, thisCcu, ccuSig, builderOrSymbolUses, _, _, _, _, _, _, _ = getDetails () @@ -3518,7 +3543,8 @@ type FSharpCheckProjectResults | Some(_, tcInfoExtras) -> tcInfoExtras.TcSymbolUses | _ -> TcSymbolUses.Empty | _ -> TcSymbolUses.Empty) - | Choice2Of2 tcSymbolUses -> [| tcSymbolUses |] + |> Array.toSeq + | Choice2Of2 tcSymbolUses -> Async.RunSynchronously(tcSymbolUses, ?cancellationToken = cancellationToken) [| for r in tcSymbolUses do @@ -3559,9 +3585,6 @@ type FsiInteractiveChecker(legacyReferenceResolver, tcConfig: TcConfig, tcGlobal member _.ParseAndCheckInteraction(sourceText: ISourceText, ?userOpName: string) = cancellable { - let! ct = Cancellable.token () - use _ = Cancellable.UsingToken(ct) - let userOpName = defaultArg userOpName "Unknown" let fileName = Path.Combine(tcConfig.implicitIncludeDir, "stdin.fsx") let suggestNamesForErrors = true // Will always be true, this is just for readability @@ -3657,7 +3680,7 @@ type FsiInteractiveChecker(legacyReferenceResolver, tcConfig: TcConfig, tcGlobal tcImports, tcFileInfo.ThisCcu, tcFileInfo.CcuSigForFile, - Choice2Of2 tcFileInfo.ScopeSymbolUses, + Choice2Of2(tcFileInfo.ScopeSymbolUses |> Seq.singleton |> async.Return), None, (fun () -> None), mkSimpleAssemblyRef "stdin", diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 8cdb304c18a..26781c4356e 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -3,8 +3,10 @@ namespace FSharp.Compiler.CodeAnalysis open System +open System.Collections.Generic open System.IO open System.Threading +open System.Threading.Tasks open Internal.Utilities.Library open FSharp.Compiler.AbstractIL.IL open FSharp.Compiler.AbstractIL.ILBinaryReader @@ -26,6 +28,8 @@ open FSharp.Compiler.TypedTreeOps open FSharp.Compiler.TcGlobals open FSharp.Compiler.Text +open Internal.Utilities.Collections + /// Delays the creation of an ILModuleReader [] type DelayedILModuleReader = @@ -443,7 +447,7 @@ type public FSharpCheckFileResults = tcConfig: TcConfig * tcGlobals: TcGlobals * isIncompleteTypeCheckEnvironment: bool * - builder: IncrementalBuilder * + builder: IncrementalBuilder option * projectOptions: FSharpProjectOptions * dependencyFiles: string[] * creationErrors: FSharpDiagnostic[] * @@ -477,7 +481,7 @@ type public FSharpCheckFileResults = backgroundDiagnostics: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] * isIncompleteTypeCheckEnvironment: bool * projectOptions: FSharpProjectOptions * - builder: IncrementalBuilder * + builder: IncrementalBuilder option * dependencyFiles: string[] * creationErrors: FSharpDiagnostic[] * parseErrors: FSharpDiagnostic[] * @@ -537,7 +541,7 @@ type public FSharpCheckProjectResults = TcImports * CcuThunk * ModuleOrNamespaceType * - Choice * + Choice> * TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * @@ -569,6 +573,29 @@ module internal ParseAndCheckFile = ct: CancellationToken -> (range * range)[] + /// Diagnostics handler for parsing & type checking while processing a single file + type DiagnosticsHandler = + new: + reportErrors: bool * + mainInputFileName: string * + diagnosticsOptions: FSharpDiagnosticOptions * + sourceText: ISourceText * + suggestNamesForErrors: bool * + flatErrors: bool -> + DiagnosticsHandler + + member DiagnosticsLogger: DiagnosticsLogger + + member ErrorCount: int + + member DiagnosticOptions: FSharpDiagnosticOptions with set + + member AnyErrors: bool + + member CollectedPhasedDiagnostics: (PhasedDiagnostic * FSharpDiagnosticSeverity) array + + member CollectedDiagnostics: symbolEnv: SymbolEnv option -> FSharpDiagnostic array + // An object to typecheck source in a given typechecking environment. // Used internally to provide intellisense over F# Interactive. type internal FsiInteractiveChecker = diff --git a/src/Compiler/Service/FSharpParseFileResults.fs b/src/Compiler/Service/FSharpParseFileResults.fs index 46a5753ba3f..baea32da816 100644 --- a/src/Compiler/Service/FSharpParseFileResults.fs +++ b/src/Compiler/Service/FSharpParseFileResults.fs @@ -6,7 +6,6 @@ open System open System.IO open System.Collections.Generic open System.Diagnostics -open Internal.Utilities.Library open FSharp.Compiler.Diagnostics open FSharp.Compiler.EditorServices open FSharp.Compiler.Syntax @@ -115,201 +114,118 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput, | _ -> Some workingRange - let visitor = - { new SyntaxVisitorBase<_>() with - override _.VisitExpr(_, _, defaultTraverse, expr) = defaultTraverse expr - - override _.VisitBinding(_path, defaultTraverse, binding) = - match binding with - | SynBinding(valData = SynValData(memberFlags = None); expr = expr) as b when - rangeContainsPos b.RangeOfBindingWithRhs pos - -> - match tryGetIdentRangeFromBinding b with - | Some range -> walkBinding expr range - | None -> None - | _ -> defaultTraverse binding - } - - SyntaxTraversal.Traverse(pos, input, visitor) + (pos, input) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynBinding(SynBinding(valData = SynValData(memberFlags = None); expr = expr) as b) when + rangeContainsPos b.RangeOfBindingWithRhs pos + -> + match tryGetIdentRangeFromBinding b with + | Some range -> walkBinding expr range + | None -> None + | _ -> None) member _.TryIdentOfPipelineContainingPosAndNumArgsApplied pos = - let visitor = - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_, _, defaultTraverse, expr) = - match expr with - | SynExpr.App(_, _, SynExpr.App(_, true, SynExpr.LongIdent(longDotId = SynLongIdent(id = [ ident ])), _, _), argExpr, _) when - rangeContainsPos argExpr.Range pos - -> - match argExpr with - | SynExpr.App(_, _, _, SynExpr.Paren(expr, _, _, _), _) when rangeContainsPos expr.Range pos -> None - | _ -> - if ident.idText = "op_PipeRight" then Some(ident, 1) - elif ident.idText = "op_PipeRight2" then Some(ident, 2) - elif ident.idText = "op_PipeRight3" then Some(ident, 3) - else None - | _ -> defaultTraverse expr - } - - SyntaxTraversal.Traverse(pos, input, visitor) + (pos, input) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynExpr(SynExpr.App( + funcExpr = SynExpr.App(_, true, SynExpr.LongIdent(longDotId = SynLongIdent(id = [ ident ])), _, _); argExpr = argExpr)) when + rangeContainsPos argExpr.Range pos + -> + match argExpr with + | SynExpr.App(_, _, _, SynExpr.Paren(expr, _, _, _), _) when rangeContainsPos expr.Range pos -> None + | _ -> + if ident.idText = "op_PipeRight" then Some(ident, 1) + elif ident.idText = "op_PipeRight2" then Some(ident, 2) + elif ident.idText = "op_PipeRight3" then Some(ident, 3) + else None + | _ -> None) member _.IsPosContainedInApplication pos = - let visitor = - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_, traverseSynExpr, defaultTraverse, expr) = - match expr with - | SynExpr.TypeApp(_, _, _, _, _, _, range) when rangeContainsPos range pos -> Some range - | SynExpr.App(_, _, _, SynExpr.ComputationExpr(_, expr, _), range) when rangeContainsPos range pos -> - traverseSynExpr expr - | SynExpr.App(_, _, _, _, range) when rangeContainsPos range pos -> Some range - | _ -> defaultTraverse expr - } - - let result = SyntaxTraversal.Traverse(pos, input, visitor) - result.IsSome + (pos, input) + ||> ParsedInput.exists (fun _path node -> + match node with + | SyntaxNode.SynExpr(SynExpr.App(argExpr = SynExpr.ComputationExpr _) | SynExpr.TypeApp(expr = SynExpr.ComputationExpr _)) -> + false + | SyntaxNode.SynExpr(SynExpr.App(range = range) | SynExpr.TypeApp(range = range)) when rangeContainsPos range pos -> true + | _ -> false) member _.IsTypeName(range: range) = - let visitor = - { new SyntaxVisitorBase<_>() with - member _.VisitModuleDecl(_, _, synModuleDecl) = - match synModuleDecl with - | SynModuleDecl.Types(typeDefns, _) -> - typeDefns - |> Seq.exists (fun (SynTypeDefn(typeInfo, _, _, _, _, _)) -> typeInfo.Range = range) - |> Some - | _ -> None - } - - let result = SyntaxTraversal.Traverse(range.Start, input, visitor) - result |> Option.contains true + (range.Start, input) + ||> ParsedInput.exists (fun _path node -> + match node with + | SyntaxNode.SynTypeDefn(SynTypeDefn(typeInfo = typeInfo)) -> typeInfo.Range = range + | _ -> false) member _.TryRangeOfFunctionOrMethodBeingApplied pos = - let rec getIdentRangeForFuncExprInApp traverseSynExpr expr pos = - match expr with - | SynExpr.Ident ident -> Some ident.idRange - - | SynExpr.LongIdent(_, _, _, range) -> Some range - - | SynExpr.Paren(expr, _, _, range) when rangeContainsPos range pos -> getIdentRangeForFuncExprInApp traverseSynExpr expr pos - - | SynExpr.TypeApp(expr, _, _, _, _, _, _) -> getIdentRangeForFuncExprInApp traverseSynExpr expr pos - - | SynExpr.App(_, _, funcExpr, argExpr, _) -> - match argExpr with - | SynExpr.App(_, _, _, _, range) when rangeContainsPos range pos -> - getIdentRangeForFuncExprInApp traverseSynExpr argExpr pos - - // Special case: `async { ... }` is actually a ComputationExpr inside of the argExpr of a SynExpr.App - | SynExpr.ComputationExpr(_, expr, range) - | SynExpr.Paren(expr, _, _, range) when rangeContainsPos range pos -> getIdentRangeForFuncExprInApp traverseSynExpr expr pos - - // Yielding values in an array or list that is used as an argument: List.sum [ getVal a b; getVal b c ] - | SynExpr.ArrayOrListComputed(_, expr, range) when rangeContainsPos range pos -> - if rangeContainsPos expr.Range pos then - getIdentRangeForFuncExprInApp traverseSynExpr expr pos - else - (* - In cases like - - let test () = div [] [ - str "" - ; | - ] - - `ProvideParametersAsyncAux` currently works with the wrong symbol or - doesn't detect the previously applied arguments. - Until that is fixed, don't show any tooltips rather than the wrong signature. - *) - None - - | _ -> - match funcExpr with - | SynExpr.App(_, true, _, _, _) when rangeContainsPos argExpr.Range pos -> - // x |> List.map - // Don't dive into the funcExpr (the operator expr) - // because we dont want to offer sig help for that! - getIdentRangeForFuncExprInApp traverseSynExpr argExpr pos - | _ -> - // Generally, we want to dive into the func expr to get the range - // of the identifier of the function we're after - getIdentRangeForFuncExprInApp traverseSynExpr funcExpr pos - - | SynExpr.Sequential(_, _, expr1, expr2, range) when rangeContainsPos range pos -> - if rangeContainsPos expr1.Range pos then - getIdentRangeForFuncExprInApp traverseSynExpr expr1 pos - else - getIdentRangeForFuncExprInApp traverseSynExpr expr2 pos - - | SynExpr.LetOrUse(bindings = bindings; body = body; range = range) when rangeContainsPos range pos -> - let binding = - bindings |> List.tryFind (fun x -> rangeContainsPos x.RangeOfBindingWithRhs pos) - - match binding with - | Some(SynBinding.SynBinding(expr = expr)) -> getIdentRangeForFuncExprInApp traverseSynExpr expr pos - | None -> getIdentRangeForFuncExprInApp traverseSynExpr body pos - - | SynExpr.IfThenElse(ifExpr = ifExpr; thenExpr = thenExpr; elseExpr = elseExpr; range = range) when rangeContainsPos range pos -> - if rangeContainsPos ifExpr.Range pos then - getIdentRangeForFuncExprInApp traverseSynExpr ifExpr pos - elif rangeContainsPos thenExpr.Range pos then - getIdentRangeForFuncExprInApp traverseSynExpr thenExpr pos - else - match elseExpr with - | None -> None - | Some expr -> getIdentRangeForFuncExprInApp traverseSynExpr expr pos + let rec (|FuncIdent|_|) (node, path) = + match node, path with + | SyntaxNode.SynExpr(DeepestIdentifiedFuncInAppChain range), _ -> Some range + | SyntaxNode.SynExpr PossibleBareArg, DeepestIdentifiedFuncInPath range -> Some range + | SyntaxNode.SynExpr(Identifier range), _ -> Some range + | _ -> None - | SynExpr.Match(expr = expr; clauses = clauses; range = range) when rangeContainsPos range pos -> + and (|DeepestIdentifiedFuncInAppChain|_|) expr = + let (|Contains|_|) pos (expr: SynExpr) = if rangeContainsPos expr.Range pos then - getIdentRangeForFuncExprInApp traverseSynExpr expr pos + Some Contains else - let clause = - clauses |> List.tryFind (fun clause -> rangeContainsPos clause.Range pos) - - match clause with - | None -> None - | Some clause -> - match clause with - | SynMatchClause.SynMatchClause(whenExpr = whenExprOpt; resultExpr = resultExpr) -> - match whenExprOpt with - | None -> getIdentRangeForFuncExprInApp traverseSynExpr resultExpr pos - | Some whenExpr -> - if rangeContainsPos whenExpr.Range pos then - getIdentRangeForFuncExprInApp traverseSynExpr whenExpr pos - else - getIdentRangeForFuncExprInApp traverseSynExpr resultExpr pos - - // Ex: C.M(x, y, ...) <--- We want to find where in the tupled application the call is being made - | SynExpr.Tuple(_, exprs, _, tupRange) when rangeContainsPos tupRange pos -> - let expr = exprs |> List.tryFind (fun expr -> rangeContainsPos expr.Range pos) - - match expr with - | None -> None - | Some expr -> getIdentRangeForFuncExprInApp traverseSynExpr expr pos - - // Capture the body of a lambda, often nested in a call to a collection function - | SynExpr.Lambda(body = body) when rangeContainsPos body.Range pos -> getIdentRangeForFuncExprInApp traverseSynExpr body pos + None - | SynExpr.DotLambda(expr = body) when rangeContainsPos body.Range pos -> getIdentRangeForFuncExprInApp traverseSynExpr body pos - - | SynExpr.Do(expr, range) when rangeContainsPos range pos -> getIdentRangeForFuncExprInApp traverseSynExpr expr pos + match expr with + | SynExpr.App(argExpr = Contains pos & DeepestIdentifiedFuncInAppChain range) -> Some range + | SynExpr.App(isInfix = false; funcExpr = Identifier range | DeepestIdentifiedFuncInAppChain range) -> Some range + | SynExpr.TypeApp(expr = Identifier range) -> Some range + | SynExpr.Paren(expr = Contains pos & DeepestIdentifiedFuncInAppChain range) -> Some range + | _ -> None - | SynExpr.Assert(expr, range) when rangeContainsPos range pos -> getIdentRangeForFuncExprInApp traverseSynExpr expr pos + and (|DeepestIdentifiedFuncInPath|_|) path = + match path with + | SyntaxNode.SynExpr(DeepestIdentifiedFuncInAppChain range) :: _ + | SyntaxNode.SynExpr PossibleBareArg :: DeepestIdentifiedFuncInPath range -> Some range + | _ -> None - | SynExpr.ArbitraryAfterError(_debugStr, range) when rangeContainsPos range pos -> Some range + and (|Identifier|_|) expr = + let (|Ident|) (ident: Ident) = ident.idRange - | expr -> traverseSynExpr expr + match expr with + | SynExpr.Ident(ident = Ident range) + | SynExpr.LongIdent(range = range) + | SynExpr.ArbitraryAfterError(range = range) -> Some range + | _ -> None - let visitor = - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_, traverseSynExpr, defaultTraverse, expr) = - match expr with - | SynExpr.TypeApp(expr, _, _, _, _, _, range) when rangeContainsPos range pos -> - getIdentRangeForFuncExprInApp traverseSynExpr expr pos - | SynExpr.App(_, _, _funcExpr, _, range) as app when rangeContainsPos range pos -> - getIdentRangeForFuncExprInApp traverseSynExpr app pos - | _ -> defaultTraverse expr - } + and (|PossibleBareArg|_|) expr = + match expr with + | SynExpr.App _ + | SynExpr.TypeApp _ + | SynExpr.Ident _ + | SynExpr.LongIdent _ + | SynExpr.Const _ + | SynExpr.Null _ + | SynExpr.InterpolatedString _ -> Some PossibleBareArg + + // f (g ‸) + | SynExpr.Paren(expr = SynExpr.Ident _ | SynExpr.LongIdent _; range = parenRange) when + rangeContainsPos parenRange pos + && not (expr.Range.End.IsAdjacentTo parenRange.End) + -> + None + + | SynExpr.Paren _ -> Some PossibleBareArg + | _ -> None - SyntaxTraversal.Traverse(pos, input, visitor) + match input |> ParsedInput.tryNode pos with + | Some(FuncIdent range) -> Some range + | Some _ -> None + | None -> + // The cursor is outside any existing node's range, + // so try to drill down into the nearest one. + (pos, input) + ||> ParsedInput.tryPickLast (fun path node -> + match node, path with + | FuncIdent range -> Some range + | _ -> None) member _.GetAllArgumentsForFunctionApplicationAtPosition pos = SynExprAppLocationsImpl.getAllCurriedArgsAtPosition pos input @@ -326,242 +242,161 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput, false, SynExpr.App(ExprAtomicFlag.NonAtomic, true, Ident "op_EqualsGreater", actualParamListExpr, _), actualLambdaBodyExpr, - _) -> Some(actualParamListExpr, actualLambdaBodyExpr) + range) -> Some(range, actualParamListExpr, actualLambdaBodyExpr) | _ -> None - SyntaxTraversal.Traverse( - opGreaterEqualPos, - input, - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_, _, defaultTraverse, expr) = - match expr with - | SynExpr.Paren(InfixAppOfOpEqualsGreater(lambdaArgs, lambdaBody) as app, _, _, _) -> - Some(app.Range, lambdaArgs.Range, lambdaBody.Range) - | _ -> defaultTraverse expr - - member _.VisitBinding(_path, defaultTraverse, binding) = - match binding with - | SynBinding(kind = SynBindingKind.Normal; expr = InfixAppOfOpEqualsGreater(lambdaArgs, lambdaBody) as app) -> - Some(app.Range, lambdaArgs.Range, lambdaBody.Range) - | _ -> defaultTraverse binding - } - ) + (opGreaterEqualPos, input) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynExpr(SynExpr.Paren(expr = InfixAppOfOpEqualsGreater(range, lambdaArgs, lambdaBody))) + | SyntaxNode.SynBinding(SynBinding( + kind = SynBindingKind.Normal; expr = InfixAppOfOpEqualsGreater(range, lambdaArgs, lambdaBody))) -> + Some(range, lambdaArgs.Range, lambdaBody.Range) + | _ -> None) member _.TryRangeOfStringInterpolationContainingPos pos = - let visitor = - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_, _, defaultTraverse, expr) = - match expr with - | SynExpr.InterpolatedString(range = range) when rangeContainsPos range pos -> Some range - | _ -> defaultTraverse expr - } - - SyntaxTraversal.Traverse(pos, input, visitor) + (pos, input) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynExpr(SynExpr.InterpolatedString(range = range)) when rangeContainsPos range pos -> Some range + | _ -> None) member _.TryRangeOfExprInYieldOrReturn pos = - let visitor = - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_path, _, defaultTraverse, expr) = - match expr with - | SynExpr.YieldOrReturn(_, expr, range) - | SynExpr.YieldOrReturnFrom(_, expr, range) when rangeContainsPos range pos -> Some expr.Range - | _ -> defaultTraverse expr - } - - SyntaxTraversal.Traverse(pos, input, visitor) + (pos, input) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynExpr(SynExpr.YieldOrReturn(expr = expr; range = range) | SynExpr.YieldOrReturnFrom(expr = expr; range = range)) when + rangeContainsPos range pos + -> + Some expr.Range + | _ -> None) member _.TryRangeOfRecordExpressionContainingPos pos = - let visitor = - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_, _, defaultTraverse, expr) = - match expr with - | SynExpr.Record(_, _, _, range) when rangeContainsPos range pos -> Some range - | _ -> defaultTraverse expr - } - - SyntaxTraversal.Traverse(pos, input, visitor) + (pos, input) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynExpr(SynExpr.Record(range = range)) when rangeContainsPos range pos -> Some range + | _ -> None) member _.TryRangeOfRefCellDereferenceContainingPos expressionPos = - let visitor = - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_, _, defaultTraverse, expr) = - match expr with - | SynExpr.App(_, false, SynExpr.LongIdent(longDotId = SynLongIdent(id = [ funcIdent ])), expr, _) -> - if funcIdent.idText = "op_Dereference" && rangeContainsPos expr.Range expressionPos then - Some funcIdent.idRange - else - None - | _ -> defaultTraverse expr - } - - SyntaxTraversal.Traverse(expressionPos, input, visitor) + (expressionPos, input) + ||> ParsedInput.tryPick (fun _path node -> + let (|Ident|) (ident: Ident) = ident.idText + + match node with + | SyntaxNode.SynExpr(SynExpr.App( + isInfix = false + funcExpr = SynExpr.LongIdent(longDotId = SynLongIdent(id = [ funcIdent & Ident "op_Dereference" ])) + argExpr = argExpr)) when rangeContainsPos argExpr.Range expressionPos -> Some funcIdent.idRange + | _ -> None) member _.TryRangeOfExpressionBeingDereferencedContainingPos expressionPos = - let visitor = - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_, _, defaultTraverse, expr) = - match expr with - | SynExpr.App(_, false, SynExpr.LongIdent(longDotId = SynLongIdent(id = [ funcIdent ])), expr, _) -> - if funcIdent.idText = "op_Dereference" && rangeContainsPos expr.Range expressionPos then - Some expr.Range - else - None - | _ -> defaultTraverse expr - } - - SyntaxTraversal.Traverse(expressionPos, input, visitor) + (expressionPos, input) + ||> ParsedInput.tryPick (fun _path node -> + let (|Ident|) (ident: Ident) = ident.idText + + match node with + | SyntaxNode.SynExpr(SynExpr.App( + isInfix = false; funcExpr = SynExpr.LongIdent(longDotId = SynLongIdent(id = [ Ident "op_Dereference" ])); argExpr = argExpr)) when + rangeContainsPos argExpr.Range expressionPos + -> + Some argExpr.Range + | _ -> None) member _.TryRangeOfReturnTypeHint(symbolUseStart: pos, ?skipLambdas) = let skipLambdas = defaultArg skipLambdas true - SyntaxTraversal.Traverse( - symbolUseStart, - input, - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_path, _traverseSynExpr, defaultTraverse, expr) = defaultTraverse expr + (symbolUseStart, input) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynBinding(SynBinding(expr = SynExpr.Lambda _)) + | SyntaxNode.SynBinding(SynBinding(expr = SynExpr.DotLambda _)) when skipLambdas -> None - override _.VisitBinding(_path, defaultTraverse, binding) = - match binding with - | SynBinding(expr = SynExpr.Lambda _) when skipLambdas -> defaultTraverse binding - | SynBinding(expr = SynExpr.DotLambda _) when skipLambdas -> defaultTraverse binding + // Skip manually type-annotated bindings + | SyntaxNode.SynBinding(SynBinding(returnInfo = Some(SynBindingReturnInfo _))) -> None - // Skip manually type-annotated bindings - | SynBinding(returnInfo = Some(SynBindingReturnInfo _)) -> defaultTraverse binding + // Let binding + | SyntaxNode.SynBinding(SynBinding(trivia = { EqualsRange = Some equalsRange }; range = range)) when + range.Start = symbolUseStart + -> + Some equalsRange.StartRange - // Let binding - | SynBinding(trivia = { EqualsRange = Some equalsRange }; range = range) when range.Start = symbolUseStart -> - Some equalsRange.StartRange + // Member binding + | SyntaxNode.SynBinding(SynBinding( + headPat = SynPat.LongIdent(longDotId = SynLongIdent(id = _ :: ident :: _)); trivia = { EqualsRange = Some equalsRange })) when + ident.idRange.Start = symbolUseStart + -> + Some equalsRange.StartRange - // Member binding - | SynBinding( - headPat = SynPat.LongIdent(longDotId = SynLongIdent(id = _ :: ident :: _)) - trivia = { EqualsRange = Some equalsRange }) when ident.idRange.Start = symbolUseStart -> - Some equalsRange.StartRange - - | _ -> defaultTraverse binding - } - ) + | _ -> None) member _.FindParameterLocations pos = ParameterLocations.Find(pos, input) member _.IsPositionContainedInACurriedParameter pos = - let visitor = - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_path, traverseSynExpr, defaultTraverse, expr) = defaultTraverse (expr) - - override _.VisitBinding(_path, _, binding) = - match binding with - | SynBinding(valData = valData; range = range) when rangeContainsPos range pos -> - let info = valData.SynValInfo.CurriedArgInfos - let mutable found = false - - for group in info do - for arg in group do - match arg.Ident with - | Some ident when rangeContainsPos ident.idRange pos -> found <- true - | _ -> () - - if found then Some range else None - | _ -> None - } - - let result = SyntaxTraversal.Traverse(pos, input, visitor) - result.IsSome + (pos, input) + ||> ParsedInput.exists (fun _path node -> + match node with + | SyntaxNode.SynBinding(SynBinding(valData = valData; range = range)) when rangeContainsPos range pos -> + valData.SynValInfo.CurriedArgInfos + |> List.exists ( + List.exists (function + | SynArgInfo(ident = Some ident) -> rangeContainsPos ident.idRange pos + | _ -> false) + ) + + | _ -> false) member _.IsTypeAnnotationGivenAtPosition pos = - let visitor = - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_path, _traverseSynExpr, defaultTraverse, expr) = - match expr with - | SynExpr.Typed(_expr, _typeExpr, range) when Position.posEq range.Start pos -> Some range - | _ -> defaultTraverse expr - - override _.VisitSimplePats(_path, pats) = - match pats with - | [] -> None - | _ -> - let exprFunc pat = - match pat with - // (s: string) - | SynSimplePat.Typed(_pat, _targetExpr, range) when Position.posEq range.Start pos -> Some range - | _ -> None - - pats |> List.tryPick exprFunc - - override _.VisitPat(_path, defaultTraverse, pat) = - // (s: string) - match pat with - | SynPat.Typed(_pat, _targetType, range) when Position.posEq range.Start pos -> Some range - | _ -> defaultTraverse pat + (pos, input) + ||> ParsedInput.exists (fun _path node -> + let rec (|Typed|_|) (pat: SynPat) = + if not (rangeContainsPos pat.Range pos) then + None + else + let (|AnyTyped|_|) = List.tryPick (|Typed|_|) - override _.VisitBinding(_path, defaultTraverse, binding) = - // let x : int = 12 - match binding with - | SynBinding( - headPat = SynPat.Named(range = patRange); returnInfo = Some(SynBindingReturnInfo(typeName = SynType.LongIdent _))) -> - Some patRange - | _ -> defaultTraverse binding - } + match pat with + | SynPat.Typed(range = range) when Position.posEq range.Start pos -> Some Typed + | SynPat.Paren(pat = Typed) -> Some Typed + | SynPat.Tuple(elementPats = AnyTyped) -> Some Typed + | _ -> None - let result = SyntaxTraversal.Traverse(pos, input, visitor) - result.IsSome + match node with + | SyntaxNode.SynExpr(SynExpr.Typed(range = range)) + | SyntaxNode.SynPat(SynPat.Typed(range = range)) -> Position.posEq range.Start pos + | SyntaxNode.SynTypeDefn(SynTypeDefn(implicitConstructor = Some(SynMemberDefn.ImplicitCtor(ctorArgs = Typed)))) + | SyntaxNode.SynBinding(SynBinding( + headPat = SynPat.Named _; returnInfo = Some(SynBindingReturnInfo(typeName = SynType.LongIdent _)))) -> true + | _ -> false) member _.IsPositionWithinTypeDefinition pos = - let visitor = - { new SyntaxVisitorBase<_>() with - override _.VisitComponentInfo(path, _) = - let typeDefs = - path - |> List.filter (function - | SyntaxNode.SynModule(SynModuleDecl.Types _) -> true - | _ -> false) - - match typeDefs with - | [] -> None - | _ -> Some true - } - - let result = SyntaxTraversal.Traverse(pos, input, visitor) - result.IsSome + (pos, input) + ||> ParsedInput.exists (fun _path node -> + match node with + | SyntaxNode.SynTypeDefn _ -> true + | _ -> false) member _.IsBindingALambdaAtPosition pos = - let visitor = - { new SyntaxVisitorBase<_>() with - member _.VisitExpr(_path, _traverseSynExpr, defaultTraverse, expr) = defaultTraverse expr - - override _.VisitBinding(_path, defaultTraverse, binding) = - match binding with - | SynBinding.SynBinding(expr = expr; range = range) when Position.posEq range.Start pos -> - match expr with - | SynExpr.Lambda _ -> Some range - | SynExpr.DotLambda _ -> Some range - | _ -> None - | _ -> defaultTraverse binding - } - - let result = SyntaxTraversal.Traverse(pos, input, visitor) - result.IsSome + (pos, input) + ||> ParsedInput.exists (fun _path node -> + match node with + | SyntaxNode.SynBinding(SynBinding(expr = SynExpr.Lambda _; range = range)) + | SyntaxNode.SynBinding(SynBinding(expr = SynExpr.DotLambda _; range = range)) -> Position.posEq range.Start pos + | _ -> false) member _.IsPositionWithinRecordDefinition pos = let isWithin left right middle = Position.posGt right left && Position.posLt middle right - let visitor = - { new SyntaxVisitorBase<_>() with - override _.VisitRecordDefn(_, _, range) = - if pos |> isWithin range.Start range.End then - Some true - else - None - - override _.VisitTypeAbbrev(_, synType, range) = - match synType with - | SynType.AnonRecd _ when pos |> isWithin range.Start range.End -> Some true - | _ -> None - } - - let result = SyntaxTraversal.Traverse(pos, input, visitor) - result.IsSome + (pos, input) + ||> ParsedInput.exists (fun _path node -> + match node with + | SyntaxNode.SynTypeDefn(SynTypeDefn(typeRepr = SynTypeDefnRepr.Simple(SynTypeDefnSimpleRepr.Record _, range))) + | SyntaxNode.SynTypeDefn(SynTypeDefn(typeRepr = SynTypeDefnRepr.Simple(SynTypeDefnSimpleRepr.TypeAbbrev _, range))) when + pos |> isWithin range.Start range.End + -> + true + | _ -> false) /// Get declared items and the selected item at the specified location member _.GetNavigationItemsImpl() = diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs new file mode 100644 index 00000000000..148d39da787 --- /dev/null +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -0,0 +1,660 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module FSharp.Compiler.CodeAnalysis.ProjectSnapshot + +open System +open System.Collections.Generic +open System.IO +open System.Reflection +open FSharp.Compiler.IO +open Internal.Utilities.Library.Extras +open FSharp.Core.Printf +open FSharp.Compiler.Text + +open Internal.Utilities.Collections +open System.Threading.Tasks +open Internal.Utilities.Hashing +open System.Collections.Immutable +open System.Runtime.CompilerServices +open FSharp.Compiler.Syntax +open FSharp.Compiler.Diagnostics +open FSharp.Compiler.DiagnosticsLogger + +type internal ProjectIdentifier = string * string + +/// A common interface for an F# source file snapshot that can be used accross all stages (lazy, source loaded, parsed) +type internal IFileSnapshot = + abstract member FileName: string + abstract member Version: byte array + abstract member IsSignatureFile: bool + +[] +module internal Helpers = + + let isSignatureFile (fileName: string) = + // TODO: is this robust enough? + fileName[fileName.Length - 1] = 'i' + + let addFileName (file: IFileSnapshot) = Md5Hasher.addString file.FileName + + let addFileNameAndVersion (file: IFileSnapshot) = + addFileName file >> Md5Hasher.addBytes file.Version + + let signatureHash projectCoreVersion (sourceFiles: IFileSnapshot seq) = + let mutable lastFile = "" + + ((projectCoreVersion, Set.empty), sourceFiles) + ||> Seq.fold (fun (res, sigs) file -> + if file.IsSignatureFile then + lastFile <- file.FileName + res |> addFileNameAndVersion file, sigs |> Set.add file.FileName + else + let sigFileName = $"{file.FileName}i" + + if sigs.Contains sigFileName then + res |> addFileName file, sigs |> Set.remove sigFileName + else + lastFile <- file.FileName + res |> addFileNameAndVersion file, sigs) + |> fst, + lastFile + + let findOutputFileName options = + options + |> Seq.tryFind (fun (x: string) -> x.StartsWith("-o:")) + |> Option.map (fun x -> x.Substring(3)) + +/// A snapshot of an F# source file. +[] +type FSharpFileSnapshot(FileName: string, Version: string, GetSource: unit -> Task) = + + static member Create(fileName: string, version: string, getSource: unit -> Task) = + FSharpFileSnapshot(fileName, version, getSource) + + static member CreateFromFileSystem(fileName: string) = + FSharpFileSnapshot( + fileName, + FileSystem.GetLastWriteTimeShim(fileName).Ticks.ToString(), + fun () -> + FileSystem.OpenFileForReadShim(fileName).ReadAllText() + |> SourceTextNew.ofString + |> Task.FromResult + ) + + member public _.FileName = FileName + member _.Version = Version + member _.GetSource() = GetSource() + + member val IsSignatureFile = FileName |> isSignatureFile + + member _.GetFileName() = FileName + + override this.Equals(o) = + match o with + | :? FSharpFileSnapshot as o -> o.FileName = this.FileName && o.Version = this.Version + | _ -> false + + override this.GetHashCode() = + this.FileName.GetHashCode() + this.Version.GetHashCode() + + interface IFileSnapshot with + member this.FileName = this.FileName + member this.Version = this.Version |> System.Text.Encoding.UTF8.GetBytes + member this.IsSignatureFile = this.IsSignatureFile + +/// A source file snapshot with loaded source text. +type internal FSharpFileSnapshotWithSource + (FileName: string, SourceHash: ImmutableArray, Source: ISourceText, IsLastCompiland: bool, IsExe: bool) = + + let version = lazy (SourceHash.ToBuilder().ToArray()) + let stringVersion = lazy (version.Value |> BitConverter.ToString) + + member val Version = version.Value + member val StringVersion = stringVersion.Value + member val IsSignatureFile = FileName |> isSignatureFile + + member _.FileName = FileName + member _.Source = Source + member _.IsLastCompiland = IsLastCompiland + member _.IsExe = IsExe + + interface IFileSnapshot with + member this.FileName = this.FileName + member this.Version = this.Version + member this.IsSignatureFile = this.IsSignatureFile + +/// A source file snapshot with parsed syntax tree +type internal FSharpParsedFile + ( + FileName: string, + SyntaxTreeHash: byte array, + SourceText: ISourceText, + ParsedInput: ParsedInput, + ParseErrors: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] + ) = + + member _.FileName = FileName + member _.SourceText = SourceText + member _.ParsedInput = ParsedInput + member _.ParseErrors = ParseErrors + + member val IsSignatureFile = FileName |> isSignatureFile + + interface IFileSnapshot with + member this.FileName = this.FileName + member this.Version = SyntaxTreeHash + member this.IsSignatureFile = this.IsSignatureFile + +/// An on-disk reference needed for project compilation. +[] +type ReferenceOnDisk = + { Path: string; LastModified: DateTime } + +/// A snapshot of an F# project. The source file type can differ based on which stage of compilation the snapshot is used for. +type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: ProjectCore, sourceFiles: 'T list) = + + let noFileVersionsHash = + lazy + (projectCore.Version + |> Md5Hasher.addStrings (sourceFiles |> Seq.map (fun x -> x.FileName))) + + let noFileVersionsKey = + lazy + ({ new ICacheKey<_, _> with + member _.GetLabel() = projectCore.Label + member _.GetKey() = projectCore.Identifier + + member _.GetVersion() = + noFileVersionsHash.Value |> Md5Hasher.toString + + }) + + let fullHash = + lazy + (projectCore.Version + |> Md5Hasher.addStrings ( + sourceFiles + |> Seq.collect (fun x -> + seq { + x.FileName + x.Version |> Md5Hasher.toString + }) + )) + + let fullKey = + lazy + ({ new ICacheKey<_, _> with + member _.GetLabel() = projectCore.Label + member _.GetKey() = projectCore.Identifier + member _.GetVersion() = fullHash.Value |> Md5Hasher.toString + }) + + let addHash (file: 'T) hash = + hash |> Md5Hasher.addString file.FileName |> Md5Hasher.addBytes file.Version + + let signatureHash = + lazy (signatureHash projectCore.Version (sourceFiles |> Seq.map (fun x -> x :> IFileSnapshot))) + + let signatureKey = + lazy (projectCore.CacheKeyWith("Signature", signatureHash.Value |> fst |> Md5Hasher.toString)) + + let lastFileHash = + lazy + (let lastFile = sourceFiles |> List.last + let sigHash, f = signatureHash.Value + + (if f = lastFile.FileName then + sigHash + else + sigHash |> Md5Hasher.addBytes lastFile.Version), + lastFile) + + let lastFileKey = + lazy + (let hash, f = lastFileHash.Value + + { new ICacheKey<_, _> with + member _.GetLabel() = $"{f.FileName} ({projectCore.Label})" + member _.GetKey() = f.FileName, projectCore.Identifier + member _.GetVersion() = hash |> Md5Hasher.toString + }) + + let sourceFileNames = lazy (sourceFiles |> List.map (fun x -> x.FileName)) + + member _.ProjectFileName = projectCore.ProjectFileName + member _.ProjectId = projectCore.ProjectId + member _.Identifier = projectCore.Identifier + member _.ReferencesOnDisk = projectCore.ReferencesOnDisk + member _.OtherOptions = projectCore.OtherOptions + member _.ReferencedProjects = projectCore.ReferencedProjects + + member _.IsIncompleteTypeCheckEnvironment = + projectCore.IsIncompleteTypeCheckEnvironment + + member _.UseScriptResolutionRules = projectCore.UseScriptResolutionRules + member _.LoadTime = projectCore.LoadTime + member _.UnresolvedReferences = projectCore.UnresolvedReferences + member _.OriginalLoadReferences = projectCore.OriginalLoadReferences + member _.Stamp = projectCore.Stamp + member _.CommandLineOptions = projectCore.CommandLineOptions + member _.ProjectDirectory = projectCore.ProjectDirectory + + member _.OutputFileName = projectCore.OutputFileName + + member _.ProjectCore = projectCore + + member _.SourceFiles = sourceFiles + + member _.SourceFileNames = sourceFileNames.Value + + member _.Label = projectCore.Label + + member _.IndexOf fileName = + sourceFiles + |> List.tryFindIndex (fun x -> x.FileName = fileName) + |> Option.defaultWith (fun () -> failwith (sprintf "Unable to find file %s in project %s" fileName projectCore.ProjectFileName)) + + member private _.With(sourceFiles: 'T list) = + ProjectSnapshotBase(projectCore, sourceFiles) + + /// Create a new snapshot with given source files replacing files in this snapshot with the same name. Other files remain unchanged. + member this.Replace(changedSourceFiles: 'T list) = + // TODO: validate if changed files are not present in the original list? + + let sourceFiles = + sourceFiles + |> List.map (fun x -> + match changedSourceFiles |> List.tryFind (fun y -> y.FileName = x.FileName) with + | Some y -> y + | None -> x) + + this.With sourceFiles + + /// Create a new snapshot with source files only up to the given index (inclusive) + member this.UpTo fileIndex = this.With sourceFiles[..fileIndex] + + /// Create a new snapshot with source files only up to the given file name (inclusive) + member this.UpTo fileName = this.UpTo(this.IndexOf fileName) + + /// Create a new snapshot with only source files at the given indexes + member this.OnlyWith fileIndexes = + this.With( + fileIndexes + |> Set.toList + |> List.sort + |> List.choose (fun x -> sourceFiles |> List.tryItem x) + ) + + override this.ToString() = + Path.GetFileNameWithoutExtension this.ProjectFileName + |> sprintf "FSharpProjectSnapshot(%s)" + + /// The newest last modified time of any file in this snapshot including the project file + member _.GetLastModifiedTimeOnDisk() = + seq { + projectCore.ProjectFileName + + yield! + sourceFiles + |> Seq.filter (fun x -> not (x.FileName.EndsWith(".AssemblyInfo.fs"))) // TODO: is this safe? any better way of doing this? + |> Seq.filter (fun x -> not (x.FileName.EndsWith(".AssemblyAttributes.fs"))) + |> Seq.map (fun x -> x.FileName) + } + |> Seq.map FileSystem.GetLastWriteTimeShim + |> Seq.max + + member _.FullVersion = fullHash.Value + member _.SignatureVersion = signatureHash.Value |> fst + member _.LastFileVersion = lastFileHash.Value |> fst + + /// Version for parsing - doesn't include any references because they don't affect parsing (...right?) + member _.ParsingVersion = projectCore.VersionForParsing |> Md5Hasher.toString + + /// A key for this snapshot but without file versions. So it will be the same across any in-file changes. + member _.NoFileVersionsKey = noFileVersionsKey.Value + + /// A full key for this snapshot, any change will cause this to change. + member _.FullKey = fullKey.Value + + /// A key including the public surface or signature for this snapshot + member _.SignatureKey = signatureKey.Value + + /// A key including the public surface or signature for this snapshot and the last file (even if it's not a signature file) + member _.LastFileKey = lastFileKey.Value + + //TODO: cache it here? + member this.FileKey(fileName: string) = this.UpTo(fileName).LastFileKey + member this.FileKey(index: FileIndex) = this.UpTo(index).LastFileKey + +/// Project snapshot with filenames and versions given as initial input +and internal ProjectSnapshot = ProjectSnapshotBase + +/// Project snapshot with file sources loaded +and internal ProjectSnapshotWithSources = ProjectSnapshotBase + +/// All required information for compiling a project except the source files. It's kept separate so it can be reused +/// for different stages of a project snapshot and also between changes to the source files. +and internal ProjectCore + ( + ProjectFileName: string, + ProjectId: string option, + ReferencesOnDisk: ReferenceOnDisk list, + OtherOptions: string list, + ReferencedProjects: FSharpReferencedProjectSnapshot list, + IsIncompleteTypeCheckEnvironment: bool, + UseScriptResolutionRules: bool, + LoadTime: DateTime, + UnresolvedReferences: FSharpUnresolvedReferencesSet option, + OriginalLoadReferences: (range * string * string) list, + Stamp: int64 option + ) as self = + + let hashForParsing = + lazy + (Md5Hasher.empty + |> Md5Hasher.addString ProjectFileName + |> Md5Hasher.addStrings OtherOptions + |> Md5Hasher.addBool IsIncompleteTypeCheckEnvironment + |> Md5Hasher.addBool UseScriptResolutionRules) + + let fullHash = + lazy + (hashForParsing.Value + |> Md5Hasher.addStrings (ReferencesOnDisk |> Seq.map (fun r -> r.Path)) + |> Md5Hasher.addDateTimes (ReferencesOnDisk |> Seq.map (fun r -> r.LastModified)) + |> Md5Hasher.addBytes' ( + ReferencedProjects + |> Seq.map (function + | FSharpReference(_name, p) -> p.ProjectSnapshot.SignatureVersion + | PEReference(getStamp, _) -> Md5Hasher.empty |> Md5Hasher.addDateTime (getStamp ()) + | ILModuleReference(_name, getStamp, _) -> Md5Hasher.empty |> Md5Hasher.addDateTime (getStamp ())) + )) + + let fullHashString = lazy (fullHash.Value |> Md5Hasher.toString) + + let commandLineOptions = + lazy + (seq { + for r in ReferencesOnDisk do + $"-r:{r.Path}" + + yield! OtherOptions + } + |> Seq.toList) + + let outputFileName = lazy (OtherOptions |> findOutputFileName) + + let key = lazy (ProjectFileName, outputFileName.Value |> Option.defaultValue "") + + let cacheKey = + lazy + ({ new ICacheKey<_, _> with + member _.GetLabel() = self.Label + member _.GetKey() = self.Identifier + member _.GetVersion() = fullHashString.Value + }) + + member val ProjectDirectory = Path.GetDirectoryName(ProjectFileName) + member _.OutputFileName = outputFileName.Value + member _.Identifier: ProjectIdentifier = key.Value + member _.Version = fullHash.Value + member _.Label = ProjectFileName |> shortPath + member _.VersionForParsing = hashForParsing.Value + + member _.CommandLineOptions = commandLineOptions.Value + + member _.ProjectFileName = ProjectFileName + member _.ProjectId = ProjectId + member _.ReferencesOnDisk = ReferencesOnDisk + member _.OtherOptions = OtherOptions + member _.ReferencedProjects = ReferencedProjects + member _.IsIncompleteTypeCheckEnvironment = IsIncompleteTypeCheckEnvironment + member _.UseScriptResolutionRules = UseScriptResolutionRules + member _.LoadTime = LoadTime + member _.UnresolvedReferences = UnresolvedReferences + member _.OriginalLoadReferences = OriginalLoadReferences + member _.Stamp = Stamp + + member _.CacheKeyWith(label, version) = + { new ICacheKey<_, _> with + member _.GetLabel() = $"{label} ({self.Label})" + member _.GetKey() = self.Identifier + member _.GetVersion() = fullHashString.Value, version + } + + member _.CacheKeyWith(label, key, version) = + { new ICacheKey<_, _> with + member _.GetLabel() = $"{label} ({self.Label})" + member _.GetKey() = key, self.Identifier + member _.GetVersion() = fullHashString.Value, version + } + + member _.CacheKey = cacheKey.Value + +and [] FSharpReferencedProjectSnapshot = + /// + /// A reference to an F# project. The physical data for it is stored/cached inside of the compiler service. + /// + /// The fully qualified path to the output of the referenced project. This should be the same value as the -r reference in the project options for this referenced project. + /// Snapshot of the referenced F# project + | FSharpReference of projectOutputFile: string * snapshot: FSharpProjectSnapshot + /// + /// A reference to any portable executable, including F#. The stream is owned by this reference. + /// The stream will be automatically disposed when there are no references to FSharpReferencedProject and is GC collected. + /// Once the stream is evaluated, the function that constructs the stream will no longer be referenced by anything. + /// If the stream evaluation throws an exception, it will be automatically handled. + /// + /// A function that calculates a last-modified timestamp for this reference. This will be used to determine if the reference is up-to-date. + /// A function that opens a Portable Executable data stream for reading. + | PEReference of getStamp: (unit -> DateTime) * delayedReader: DelayedILModuleReader + + /// + /// A reference to an ILModuleReader. + /// + /// The fully qualified path to the output of the referenced project. This should be the same value as the -r reference in the project options for this referenced project. + /// A function that calculates a last-modified timestamp for this reference. This will be used to determine if the reference is up-to-date. + /// A function that creates an ILModuleReader for reading module data. + | ILModuleReference of + projectOutputFile: string * + getStamp: (unit -> DateTime) * + getReader: (unit -> FSharp.Compiler.AbstractIL.ILBinaryReader.ILModuleReader) + + /// + /// The fully qualified path to the output of the referenced project. This should be the same value as the -r + /// reference in the project options for this referenced project. + /// + member this.OutputFile = + match this with + | FSharpReference(projectOutputFile = projectOutputFile) + | ILModuleReference(projectOutputFile = projectOutputFile) -> projectOutputFile + | PEReference(delayedReader = reader) -> reader.OutputFile + + /// + /// Creates a reference for an F# project. The physical data for it is stored/cached inside of the compiler service. + /// + /// The fully qualified path to the output of the referenced project. This should be the same value as the -r reference in the project options for this referenced project. + /// The project snapshot for this F# project + static member CreateFSharp(projectOutputFile, snapshot: FSharpProjectSnapshot) = + FSharpReference(projectOutputFile, snapshot) + + override this.Equals(o) = + match o with + | :? FSharpReferencedProjectSnapshot as o -> + match this, o with + | FSharpReference(projectOutputFile1, options1), FSharpReference(projectOutputFile2, options2) -> + projectOutputFile1 = projectOutputFile2 && options1 = options2 + | PEReference(getStamp1, reader1), PEReference(getStamp2, reader2) -> + reader1.OutputFile = reader2.OutputFile && (getStamp1 ()) = (getStamp2 ()) + | ILModuleReference(projectOutputFile1, getStamp1, _), ILModuleReference(projectOutputFile2, getStamp2, _) -> + projectOutputFile1 = projectOutputFile2 && (getStamp1 ()) = (getStamp2 ()) + | _ -> false + + | _ -> false + + override this.GetHashCode() = this.OutputFile.GetHashCode() + +/// An identifier of an F# project. This serves to identify the same project as it changes over time and enables us to clear obsolete data from caches. +and [] FSharpProjectIdentifier = + | FSharpProjectIdentifier of projectFileName: string * outputFileName: string + +/// A snapshot of an F# project. This type contains all the necessary information for type checking a project. +and [] FSharpProjectSnapshot internal (projectSnapshot) = + + member internal _.ProjectSnapshot: ProjectSnapshot = projectSnapshot + + /// Create a new snapshot with given source files replacing files in this snapshot with the same name. Other files remain unchanged. + member _.Replace(changedSourceFiles: FSharpFileSnapshot list) = + projectSnapshot.Replace(changedSourceFiles) |> FSharpProjectSnapshot + + member _.Label = projectSnapshot.Label + member _.Identifier = FSharpProjectIdentifier projectSnapshot.ProjectCore.Identifier + + static member Create + ( + projectFileName: string, + projectId: string option, + sourceFiles: FSharpFileSnapshot list, + referencesOnDisk: ReferenceOnDisk list, + otherOptions: string list, + referencedProjects: FSharpReferencedProjectSnapshot list, + isIncompleteTypeCheckEnvironment: bool, + useScriptResolutionRules: bool, + loadTime: DateTime, + unresolvedReferences: FSharpUnresolvedReferencesSet option, + originalLoadReferences: (range * string * string) list, + stamp: int64 option + ) = + + let projectCore = + ProjectCore( + projectFileName, + projectId, + referencesOnDisk, + otherOptions, + referencedProjects, + isIncompleteTypeCheckEnvironment, + useScriptResolutionRules, + loadTime, + unresolvedReferences, + originalLoadReferences, + stamp + ) + + ProjectSnapshotBase(projectCore, sourceFiles) |> FSharpProjectSnapshot + + static member FromOptions(options: FSharpProjectOptions, getFileSnapshot, ?snapshotAccumulator) = + let snapshotAccumulator = defaultArg snapshotAccumulator (Dictionary()) + + async { + + // TODO: check if options is a good key here + if not (snapshotAccumulator.ContainsKey options) then + + let! sourceFiles = options.SourceFiles |> Seq.map (getFileSnapshot options) |> Async.Parallel + + let! referencedProjects = + options.ReferencedProjects + |> Seq.map (function + | FSharpReferencedProject.FSharpReference(outputName, options) -> + async { + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, snapshotAccumulator) + + return FSharpReferencedProjectSnapshot.FSharpReference(outputName, snapshot) + } + | FSharpReferencedProject.PEReference(getStamp, reader) -> + async.Return <| FSharpReferencedProjectSnapshot.PEReference(getStamp, reader) + | FSharpReferencedProject.ILModuleReference(outputName, getStamp, getReader) -> + async.Return + <| FSharpReferencedProjectSnapshot.ILModuleReference(outputName, getStamp, getReader)) + + |> Async.Sequential + + let referencesOnDisk, otherOptions = + options.OtherOptions + |> Array.partition (fun x -> x.StartsWith("-r:")) + |> map1Of2 ( + Array.map (fun x -> + let path = x.Substring(3) + + { + Path = path + LastModified = FileSystem.GetLastWriteTimeShim(path) + }) + ) + + let snapshot = + FSharpProjectSnapshot.Create( + projectFileName = options.ProjectFileName, + projectId = options.ProjectId, + sourceFiles = (sourceFiles |> List.ofArray), + referencesOnDisk = (referencesOnDisk |> List.ofArray), + otherOptions = (otherOptions |> List.ofArray), + referencedProjects = (referencedProjects |> List.ofArray), + isIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment, + useScriptResolutionRules = options.UseScriptResolutionRules, + loadTime = options.LoadTime, + unresolvedReferences = options.UnresolvedReferences, + originalLoadReferences = options.OriginalLoadReferences, + stamp = options.Stamp + ) + + snapshotAccumulator.Add(options, snapshot) + + return snapshotAccumulator[options] + } + + static member internal GetFileSnapshotFromDisk _ fileName = + FSharpFileSnapshot.CreateFromFileSystem fileName |> async.Return + + static member FromOptions(options: FSharpProjectOptions) = + FSharpProjectSnapshot.FromOptions(options, FSharpProjectSnapshot.GetFileSnapshotFromDisk) + + static member FromOptions(options: FSharpProjectOptions, fileName: string, fileVersion: int, sourceText: ISourceText) = + + let getFileSnapshot _ fName = + if fName = fileName then + FSharpFileSnapshot.Create( + fileName, + $"{fileVersion}{sourceText.GetHashCode().ToString()}", + fun () -> Task.FromResult(SourceTextNew.ofISourceText sourceText) + ) + else + FSharpFileSnapshot.CreateFromFileSystem fName + |> async.Return + + FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + +let rec internal snapshotToOptions (projectSnapshot: ProjectSnapshotBase<_>) = + { + ProjectFileName = projectSnapshot.ProjectFileName + ProjectId = projectSnapshot.ProjectId + SourceFiles = projectSnapshot.SourceFiles |> Seq.map (fun x -> x.FileName) |> Seq.toArray + OtherOptions = projectSnapshot.CommandLineOptions |> List.toArray + ReferencedProjects = + projectSnapshot.ReferencedProjects + |> Seq.map (function + | FSharpReference(name, opts) -> FSharpReferencedProject.FSharpReference(name, opts.ProjectSnapshot |> snapshotToOptions) + | PEReference(getStamp, reader) -> FSharpReferencedProject.PEReference(getStamp, reader) + | ILModuleReference(name, getStamp, getReader) -> FSharpReferencedProject.ILModuleReference(name, getStamp, getReader)) + |> Seq.toArray + IsIncompleteTypeCheckEnvironment = projectSnapshot.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = projectSnapshot.UseScriptResolutionRules + LoadTime = projectSnapshot.LoadTime + UnresolvedReferences = projectSnapshot.UnresolvedReferences + OriginalLoadReferences = projectSnapshot.OriginalLoadReferences + Stamp = projectSnapshot.Stamp + } + +[] +type internal Extensions = + + [] + static member ToOptions(this: ProjectSnapshot) = this |> snapshotToOptions + + [] + static member ToOptions(this: FSharpProjectSnapshot) = + this.ProjectSnapshot |> snapshotToOptions + + [] + static member GetProjectIdentifier(this: FSharpProjectOptions) : ProjectIdentifier = + this.ProjectFileName, this.OtherOptions |> findOutputFileName |> Option.defaultValue "" diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index f6289a283ac..f59a1e9b6a5 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -485,10 +485,19 @@ type BoundModel private ( syntaxTreeOpt ) - /// Global service state -type FrameworkImportsCacheKey = FrameworkImportsCacheKey of resolvedpath: string list * assemblyName: string * targetFrameworkDirectories: string list * fsharpBinaries: string * langVersion: decimal +type FrameworkImportsCacheKey = + | FrameworkImportsCacheKey of resolvedpath: string list * assemblyName: string * targetFrameworkDirectories: string list * fsharpBinaries: string * langVersion: decimal + + interface ICacheKey with + member this.GetKey() = + this |> function FrameworkImportsCacheKey(assemblyName=a) -> a + member this.GetLabel() = + this |> function FrameworkImportsCacheKey(assemblyName=a) -> a + + member this.GetVersion() = this + /// Represents a cache of 'framework' references that can be shared between multiple incremental builds type FrameworkImportsCache(size) = @@ -593,6 +602,7 @@ module Utilities = /// Constructs the build data (IRawFSharpAssemblyData) representing the assembly when used /// as a cross-assembly reference. Note the assembly has not been generated on disk, so this is /// a virtualized view of the assembly contents as computed by background checking. +[] type RawFSharpAssemblyDataBackedByLanguageService (tcConfig, tcGlobals, generatedCcu: CcuThunk, outfile, topAttrs, assemblyName, ilAssemRef) = let exportRemapping = MakeExportRemapping generatedCcu generatedCcu.Contents diff --git a/src/Compiler/Service/IncrementalBuild.fsi b/src/Compiler/Service/IncrementalBuild.fsi index b4e60d403f0..0dedfb02948 100644 --- a/src/Compiler/Service/IncrementalBuild.fsi +++ b/src/Compiler/Service/IncrementalBuild.fsi @@ -22,6 +22,17 @@ open FSharp.Compiler.TcGlobals open FSharp.Compiler.Text open FSharp.Compiler.TypedTree open FSharp.Compiler.BuildGraph +open Internal.Utilities.Collections + +type internal FrameworkImportsCacheKey = + | FrameworkImportsCacheKey of + resolvedpath: string list * + assemblyName: string * + targetFrameworkDirectories: string list * + fsharpBinaries: string * + langVersion: decimal + + interface ICacheKey /// Lookup the global static cache for building the FrameworkTcImports type internal FrameworkImportsCache = @@ -132,6 +143,20 @@ type internal PartialCheckResults = member TimeStamp: DateTime +[] +type internal RawFSharpAssemblyDataBackedByLanguageService = + new: + tcConfig: TcConfig * + tcGlobals: TcGlobals * + generatedCcu: CcuThunk * + outfile: string * + topAttrs: TopAttribs * + assemblyName: string * + ilAssemRef: FSharp.Compiler.AbstractIL.IL.ILAssemblyRef -> + RawFSharpAssemblyDataBackedByLanguageService + + interface IRawFSharpAssemblyData + /// Manages an incremental build graph for the build of an F# project [] type internal IncrementalBuilder = diff --git a/src/Compiler/Service/ServiceAnalysis.fs b/src/Compiler/Service/ServiceAnalysis.fs index 89569a802c8..29df63b1194 100644 --- a/src/Compiler/Service/ServiceAnalysis.fs +++ b/src/Compiler/Service/ServiceAnalysis.fs @@ -8,7 +8,6 @@ open System.Runtime.CompilerServices open Internal.Utilities.Library open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols -open FSharp.Compiler.Syntax open FSharp.Compiler.Syntax.PrettyNaming open FSharp.Compiler.Text open FSharp.Compiler.Text.Range @@ -463,1210 +462,3 @@ module UnusedDeclarations = let unusedRanges = getUnusedDeclarationRanges allSymbolUsesInFile isScriptFile return unusedRanges } - -module UnnecessaryParentheses = - open System - - let (|Ident|) (ident: Ident) = ident.idText - - /// Represents a symbolic infix operator with the precedence of *, /, or %. - /// All instances of this type are considered equal. - [] - type MulDivMod = - | Mul - | Div - | Mod - - member _.CompareTo(_other: MulDivMod) = 0 - override this.Equals obj = this.CompareTo(unbox obj) = 0 - override _.GetHashCode() = 0 - - interface IComparable with - member this.CompareTo obj = this.CompareTo(unbox obj) - - /// Represents a symbolic infix operator with the precedence of + or -. - /// All instances of this type are considered equal. - [] - type AddSub = - | Add - | Sub - - member _.CompareTo(_other: AddSub) = 0 - override this.Equals obj = this.CompareTo(unbox obj) = 0 - override _.GetHashCode() = 0 - - interface IComparable with - member this.CompareTo obj = this.CompareTo(unbox obj) - - /// Holds a symbolic operator's original notation. - /// Equality is based on the contents of the string. - /// Comparison always returns 0. - [] - type OriginalNotation = - | OriginalNotation of string - - member _.CompareTo(_other: OriginalNotation) = 0 - - override this.Equals obj = - match this, obj with - | OriginalNotation this, (:? OriginalNotation as OriginalNotation other) -> String.Equals(this, other, StringComparison.Ordinal) - | _ -> false - - override this.GetHashCode() = - match this with - | OriginalNotation notation -> notation.GetHashCode() - - interface IComparable with - member this.CompareTo obj = this.CompareTo(unbox obj) - - /// Represents an expression's precedence. - /// Comparison is based only on the precedence case. - /// Equality considers the embedded original notation, if any. - /// - /// For example: - /// - /// compare (AddSub (Add, OriginalNotation "+")) (AddSub (Add, OriginalNotation "++")) = 0 - /// - /// but - /// - /// AddSub (Add, OriginalNotation "+") <> AddSub (Add, OriginalNotation "++") - type Precedence = - /// yield, yield!, return, return! - | Low - - /// <- - | Set - - /// := - | ColonEquals - - /// , - | Comma - - /// or, || - /// - /// Refers to the exact operators or and ||. - /// Instances with leading dots or question marks or trailing characters are parsed as Bar instead. - | BarBar of OriginalNotation - - /// &, && - /// - /// Refers to the exact operators & and &&. - /// Instances with leading dots or question marks or trailing characters are parsed as Amp instead. - | AmpAmp of OriginalNotation - - /// :>, :?> - | UpcastDowncast - - /// =…, |…, &…, $…, >…, <…, !=… - | Relational of OriginalNotation - - /// ^…, @… - | HatAt - - /// :: - | Cons - - /// :? - | TypeTest - - /// +…, -… - | AddSub of AddSub * OriginalNotation - - /// *…, /…, %… - | MulDivMod of MulDivMod * OriginalNotation - - /// **… - | Exp - - /// - x - | UnaryPrefix - - /// f x - | Apply - - /// -x, !… x, ~~… x - | High - - // x.y - | Dot - - /// Associativity/association. - type Assoc = - /// Non-associative or no association. - | Non - - /// Left-associative or left-hand association. - | Left - - /// Right-associative or right-hand association. - | Right - - module Assoc = - let ofPrecedence precedence = - match precedence with - | Low -> Non - | Set -> Non - | ColonEquals -> Right - | Comma -> Non - | BarBar _ -> Left - | AmpAmp _ -> Left - | UpcastDowncast -> Right - | Relational _ -> Left - | HatAt -> Right - | Cons -> Right - | TypeTest -> Non - | AddSub _ -> Left - | MulDivMod _ -> Left - | Exp -> Right - | UnaryPrefix -> Left - | Apply -> Left - | High -> Left - | Dot -> Left - - /// Matches if the two expressions or patterns refer to the same object. - [] - let inline (|Is|_|) (inner1: 'a) (inner2: 'a) = - if obj.ReferenceEquals(inner1, inner2) then - ValueSome Is - else - ValueNone - - module SynExpr = - open FSharp.Compiler.SyntaxTrivia - - /// See atomicExprAfterType in pars.fsy. - [] - let (|AtomicExprAfterType|_|) expr = - match expr with - | SynExpr.Paren _ - | SynExpr.Quote _ - | SynExpr.Const _ - | SynExpr.Tuple(isStruct = true) - | SynExpr.Record _ - | SynExpr.AnonRecd _ - | SynExpr.InterpolatedString _ - | SynExpr.Null _ - | SynExpr.ArrayOrList(isArray = true) - | SynExpr.ArrayOrListComputed(isArray = true) -> ValueSome AtomicExprAfterType - | _ -> ValueNone - - /// Matches if the given expression represents a high-precedence - /// function application, e.g., - /// - /// f x - /// - /// (+) x y - [] - let (|HighPrecedenceApp|_|) expr = - match expr with - | SynExpr.App(isInfix = false; funcExpr = SynExpr.Ident _) - | SynExpr.App(isInfix = false; funcExpr = SynExpr.LongIdent _) - | SynExpr.App(isInfix = false; funcExpr = SynExpr.App(isInfix = false)) -> ValueSome HighPrecedenceApp - | _ -> ValueNone - - module FuncExpr = - /// Matches when the given funcExpr is a direct application - /// of a symbolic operator, e.g., -, _not_ (~-). - [] - let (|SymbolicOperator|_|) funcExpr = - match funcExpr with - | SynExpr.LongIdent(longDotId = SynLongIdent(trivia = trivia)) -> - let rec tryPick = - function - | [] -> ValueNone - | Some(IdentTrivia.OriginalNotation op) :: _ -> ValueSome op - | _ :: rest -> tryPick rest - - tryPick trivia - | _ -> ValueNone - - /// Matches when the given expression is a prefix operator application, e.g., - /// - /// -x - /// - /// ~~~x - [] - let (|PrefixApp|_|) expr : Precedence voption = - match expr with - | SynExpr.App(isInfix = false; funcExpr = funcExpr & FuncExpr.SymbolicOperator op; argExpr = argExpr) -> - if funcExpr.Range.IsAdjacentTo argExpr.Range then - ValueSome High - else - assert (op.Length > 0) - - match op[0] with - | '!' - | '~' -> ValueSome High - | _ -> ValueSome UnaryPrefix - - | SynExpr.AddressOf(expr = expr; opRange = opRange) -> - if opRange.IsAdjacentTo expr.Range then - ValueSome High - else - ValueSome UnaryPrefix - - | _ -> ValueNone - - /// Tries to parse the given original notation as a symbolic infix operator. - [] - let (|SymbolPrec|_|) (originalNotation: string) = - // Trim any leading dots or question marks from the given symbolic operator. - // Leading dots or question marks have no effect on operator precedence or associativity - // with the exception of &, &&, and ||. - let ignoredLeadingChars = ".?".AsSpan() - let trimmed = originalNotation.AsSpan().TrimStart ignoredLeadingChars - assert (trimmed.Length > 0) - - match trimmed[0], originalNotation with - | _, ":=" -> ValueSome ColonEquals - | _, ("||" | "or") -> ValueSome(BarBar(OriginalNotation originalNotation)) - | _, ("&" | "&&") -> ValueSome(AmpAmp(OriginalNotation originalNotation)) - | '|', _ - | '&', _ - | '<', _ - | '>', _ - | '=', _ - | '$', _ -> ValueSome(Relational(OriginalNotation originalNotation)) - | '!', _ when trimmed.Length > 1 && trimmed[1] = '=' -> ValueSome(Relational(OriginalNotation originalNotation)) - | '^', _ - | '@', _ -> ValueSome HatAt - | _, "::" -> ValueSome Cons - | '+', _ -> ValueSome(AddSub(Add, OriginalNotation originalNotation)) - | '-', _ -> ValueSome(AddSub(Sub, OriginalNotation originalNotation)) - | '/', _ -> ValueSome(MulDivMod(Div, OriginalNotation originalNotation)) - | '%', _ -> ValueSome(MulDivMod(Mod, OriginalNotation originalNotation)) - | '*', _ when trimmed.Length > 1 && trimmed[1] = '*' -> ValueSome Exp - | '*', _ -> ValueSome(MulDivMod(Mul, OriginalNotation originalNotation)) - | _ -> ValueNone - - [] - let (|Contains|_|) (c: char) (s: string) = - if s.IndexOf c >= 0 then ValueSome Contains else ValueNone - - /// Any expressions in which the removal of parens would - /// lead to something like the following that would be - /// confused by the parser with a type parameter application: - /// - /// xz - /// - /// xz - [] - let rec (|ConfusableWithTypeApp|_|) synExpr = - match synExpr with - | SynExpr.Paren(expr = ConfusableWithTypeApp) - | SynExpr.App(funcExpr = ConfusableWithTypeApp) - | SynExpr.App(isInfix = true; funcExpr = FuncExpr.SymbolicOperator(Contains '>'); argExpr = ConfusableWithTypeApp) -> - ValueSome ConfusableWithTypeApp - | SynExpr.App(isInfix = true; funcExpr = funcExpr & FuncExpr.SymbolicOperator(Contains '<'); argExpr = argExpr) when - argExpr.Range.IsAdjacentTo funcExpr.Range - -> - ValueSome ConfusableWithTypeApp - | SynExpr.Tuple(exprs = exprs) -> - let rec anyButLast = - function - | _ :: [] - | [] -> ValueNone - | ConfusableWithTypeApp :: _ -> ValueSome ConfusableWithTypeApp - | _ :: tail -> anyButLast tail - - anyButLast exprs - | _ -> ValueNone - - /// Matches when the expression represents the infix application of a symbolic operator. - /// - /// (x λ y) ρ z - /// - /// x λ (y ρ z) - [] - let (|InfixApp|_|) synExpr : struct (Precedence * Assoc) voption = - match synExpr with - | SynExpr.App(funcExpr = SynExpr.App(isInfix = true; funcExpr = FuncExpr.SymbolicOperator(SymbolPrec prec))) -> - ValueSome(prec, Right) - | SynExpr.App(isInfix = true; funcExpr = FuncExpr.SymbolicOperator(SymbolPrec prec)) -> ValueSome(prec, Left) - | SynExpr.Upcast _ - | SynExpr.Downcast _ -> ValueSome(UpcastDowncast, Left) - | SynExpr.TypeTest _ -> ValueSome(TypeTest, Left) - | _ -> ValueNone - - /// Returns the given expression's precedence and the side of the inner expression, - /// if applicable. - [] - let (|OuterBinaryExpr|_|) inner outer : struct (Precedence * Assoc) voption = - match outer with - | SynExpr.YieldOrReturn _ - | SynExpr.YieldOrReturnFrom _ -> ValueSome(Low, Right) - | SynExpr.Tuple(exprs = SynExpr.Paren(expr = Is inner) :: _) -> ValueSome(Comma, Left) - | SynExpr.Tuple _ -> ValueSome(Comma, Right) - | InfixApp(Cons, side) -> ValueSome(Cons, side) - | SynExpr.Assert _ - | SynExpr.Lazy _ - | SynExpr.InferredUpcast _ - | SynExpr.InferredDowncast _ -> ValueSome(Apply, Non) - | PrefixApp prec -> ValueSome(prec, Non) - | InfixApp(prec, side) -> ValueSome(prec, side) - | SynExpr.App(argExpr = SynExpr.ComputationExpr _) -> ValueSome(UnaryPrefix, Left) - | SynExpr.App(funcExpr = SynExpr.Paren(expr = SynExpr.App _)) -> ValueSome(Apply, Left) - | SynExpr.App _ -> ValueSome(Apply, Non) - | SynExpr.DotSet(targetExpr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Dot, Left) - | SynExpr.DotSet(rhsExpr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Set, Right) - | SynExpr.DotIndexedSet(objectExpr = SynExpr.Paren(expr = Is inner)) - | SynExpr.DotNamedIndexedPropertySet(targetExpr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Dot, Left) - | SynExpr.DotIndexedSet(valueExpr = SynExpr.Paren(expr = Is inner)) - | SynExpr.DotNamedIndexedPropertySet(rhsExpr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Set, Right) - | SynExpr.LongIdentSet(expr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Set, Right) - | SynExpr.Set _ -> ValueSome(Set, Non) - | SynExpr.DotGet _ -> ValueSome(Dot, Left) - | SynExpr.DotIndexedGet(objectExpr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Dot, Left) - | _ -> ValueNone - - /// Matches a SynExpr.App nested in a sequence of dot-gets. - /// - /// x.M.N().O - [] - let (|NestedApp|_|) expr = - let rec loop = - function - | SynExpr.DotGet(expr = expr) - | SynExpr.DotIndexedGet(objectExpr = expr) -> loop expr - | SynExpr.App _ -> ValueSome NestedApp - | _ -> ValueNone - - loop expr - - /// Returns the given expression's precedence, if applicable. - [] - let (|InnerBinaryExpr|_|) expr : Precedence voption = - match expr with - | SynExpr.Tuple(isStruct = false) -> ValueSome Comma - | SynExpr.DotGet(expr = NestedApp) - | SynExpr.DotIndexedGet(objectExpr = NestedApp) -> ValueSome Apply - | SynExpr.DotGet _ - | SynExpr.DotIndexedGet _ -> ValueSome Dot - | PrefixApp prec -> ValueSome prec - | InfixApp(prec, _) -> ValueSome prec - | SynExpr.App _ - | SynExpr.Assert _ - | SynExpr.Lazy _ - | SynExpr.For _ - | SynExpr.ForEach _ - | SynExpr.While _ - | SynExpr.Do _ - | SynExpr.New _ - | SynExpr.InferredUpcast _ - | SynExpr.InferredDowncast _ -> ValueSome Apply - | SynExpr.DotIndexedSet _ - | SynExpr.DotNamedIndexedPropertySet _ - | SynExpr.DotSet _ -> ValueSome Set - | _ -> ValueNone - - module Dangling = - /// Returns the first matching nested right-hand target expression, if any. - let private dangling (target: SynExpr -> SynExpr option) = - let (|Target|_|) = target - let (|Last|) = List.last - - let rec loop expr = - match expr with - | Target expr -> ValueSome expr - | SynExpr.Tuple(isStruct = false; exprs = Last expr) - | SynExpr.App(argExpr = expr) - | SynExpr.IfThenElse(elseExpr = Some expr) - | SynExpr.IfThenElse(ifExpr = expr) - | SynExpr.Sequential(expr2 = expr) - | SynExpr.YieldOrReturn(expr = expr) - | SynExpr.YieldOrReturnFrom(expr = expr) - | SynExpr.Set(rhsExpr = expr) - | SynExpr.DotSet(rhsExpr = expr) - | SynExpr.DotNamedIndexedPropertySet(rhsExpr = expr) - | SynExpr.DotIndexedSet(valueExpr = expr) - | SynExpr.LongIdentSet(expr = expr) - | SynExpr.LetOrUse(body = expr) - | SynExpr.Lambda(body = expr) - | SynExpr.Match(clauses = Last(SynMatchClause(resultExpr = expr))) - | SynExpr.MatchLambda(matchClauses = Last(SynMatchClause(resultExpr = expr))) - | SynExpr.MatchBang(clauses = Last(SynMatchClause(resultExpr = expr))) - | SynExpr.TryWith(withCases = Last(SynMatchClause(resultExpr = expr))) - | SynExpr.TryFinally(finallyExpr = expr) -> loop expr - | _ -> ValueNone - - loop - - /// Matches a dangling if-then construct. - [] - let (|IfThen|_|) = - dangling (function - | SynExpr.IfThenElse _ as expr -> Some expr - | _ -> None) - - /// Matches a dangling sequential expression. - [] - let (|Sequential|_|) = - dangling (function - | SynExpr.Sequential _ as expr -> Some expr - | _ -> None) - - /// Matches a dangling try-with or try-finally construct. - [] - let (|Try|_|) = - dangling (function - | SynExpr.TryWith _ - | SynExpr.TryFinally _ as expr -> Some expr - | _ -> None) - - /// Matches a dangling match-like construct. - [] - let (|Match|_|) = - dangling (function - | SynExpr.Match _ - | SynExpr.MatchBang _ - | SynExpr.MatchLambda _ - | SynExpr.TryWith _ - | SynExpr.Lambda _ as expr -> Some expr - | _ -> None) - - /// Matches a nested dangling construct that could become problematic - /// if the surrounding parens were removed. - [] - let (|Problematic|_|) = - dangling (function - | SynExpr.Lambda _ - | SynExpr.MatchLambda _ - | SynExpr.Match _ - | SynExpr.MatchBang _ - | SynExpr.TryWith _ - | SynExpr.TryFinally _ - | SynExpr.IfThenElse _ - | SynExpr.Sequential _ - | SynExpr.LetOrUse _ - | SynExpr.Set _ - | SynExpr.LongIdentSet _ - | SynExpr.DotIndexedSet _ - | SynExpr.DotNamedIndexedPropertySet _ - | SynExpr.DotSet _ - | SynExpr.NamedIndexedPropertySet _ as expr -> Some expr - | _ -> None) - - /// If the given expression is a parenthesized expression and the parentheses - /// are unnecessary in the given context, returns the unnecessary parentheses' range. - let rec unnecessaryParentheses (getSourceLineStr: int -> string) expr path = - let unnecessaryParentheses = unnecessaryParentheses getSourceLineStr - - // Indicates whether the parentheses with the given range - // enclose an expression whose indentation would be invalid - // in context if it were not surrounded by parentheses. - let containsSensitiveIndentation outerOffsides (parenRange: range) = - let startLine = parenRange.StartLine - let endLine = parenRange.EndLine - - if startLine = endLine then - false - else - let rec loop offsides lineNo startCol = - if lineNo <= endLine then - let line = getSourceLineStr lineNo - - match offsides with - | ValueNone -> - let i = line.AsSpan(startCol).IndexOfAnyExcept(' ', ')') - - if i >= 0 then - let newOffsides = i + startCol - newOffsides <= outerOffsides || loop (ValueSome newOffsides) (lineNo + 1) 0 - else - loop offsides (lineNo + 1) 0 - - | ValueSome offsidesCol -> - let i = line.AsSpan(0, min offsidesCol line.Length).IndexOfAnyExcept(' ', ')') - - if i >= 0 && i < offsidesCol then - let slice = line.AsSpan(i, min (offsidesCol - i) (line.Length - i)) - let j = slice.IndexOfAnyExcept("*/%-+:^@><=!|0$.?".AsSpan()) - - let lo = i + (if j >= 0 && slice[j] = ' ' then j else 0) - lo < offsidesCol - 1 || lo <= outerOffsides || loop offsides (lineNo + 1) 0 - else - loop offsides (lineNo + 1) 0 - else - false - - loop ValueNone startLine (parenRange.StartColumn + 1) - - // Matches if the given expression starts with a symbol, e.g., <@ … @>, $"…", @"…", +1, -1… - let (|StartsWithSymbol|_|) = - let (|TextStartsWith|) (m: range) = - let line = getSourceLineStr m.StartLine - line[m.StartColumn] - - let (|StartsWith|) (s: string) = s[0] - - function - | SynExpr.Quote _ - | SynExpr.InterpolatedString _ - | SynExpr.Const(SynConst.String(synStringKind = SynStringKind.Verbatim), _) - | SynExpr.Const(SynConst.Byte _, TextStartsWith '+') - | SynExpr.Const(SynConst.UInt16 _, TextStartsWith '+') - | SynExpr.Const(SynConst.UInt32 _, TextStartsWith '+') - | SynExpr.Const(SynConst.UInt64 _, TextStartsWith '+') - | SynExpr.Const(SynConst.UIntPtr _, TextStartsWith '+') - | SynExpr.Const(SynConst.SByte _, TextStartsWith('-' | '+')) - | SynExpr.Const(SynConst.Int16 _, TextStartsWith('-' | '+')) - | SynExpr.Const(SynConst.Int32 _, TextStartsWith('-' | '+')) - | SynExpr.Const(SynConst.Int64 _, TextStartsWith('-' | '+')) - | SynExpr.Const(SynConst.IntPtr _, TextStartsWith('-' | '+')) - | SynExpr.Const(SynConst.Decimal _, TextStartsWith('-' | '+')) - | SynExpr.Const(SynConst.Double _, TextStartsWith('-' | '+')) - | SynExpr.Const(SynConst.Single _, TextStartsWith('-' | '+')) - | SynExpr.Const(SynConst.Measure(_, TextStartsWith('-' | '+'), _, _), _) - | SynExpr.Const(SynConst.UserNum(StartsWith('-' | '+'), _), _) -> Some StartsWithSymbol - | _ -> None - - // Matches if the given expression is a numeric literal - // that it is safe to "dot into," e.g., 1l, 0b1, 1e10, 1d, 1.0… - let (|DotSafeNumericLiteral|_|) = - /// 1l, 1d, 0b1, 0x1, 0o1, 1e10… - let (|TextContainsLetter|_|) (m: range) = - let line = getSourceLineStr m.StartLine - let span = line.AsSpan(m.StartColumn, m.EndColumn - m.StartColumn) - - if span.LastIndexOfAnyInRange('A', 'z') >= 0 then - Some TextContainsLetter - else - None - - // 1.0… - let (|TextEndsWithNumber|_|) (m: range) = - let line = getSourceLineStr m.StartLine - let span = line.AsSpan(m.StartColumn, m.EndColumn - m.StartColumn) - - if Char.IsDigit span[span.Length - 1] then - Some TextEndsWithNumber - else - None - - function - | SynExpr.Const(SynConst.Byte _, _) - | SynExpr.Const(SynConst.UInt16 _, _) - | SynExpr.Const(SynConst.UInt32 _, _) - | SynExpr.Const(SynConst.UInt64 _, _) - | SynExpr.Const(SynConst.UIntPtr _, _) - | SynExpr.Const(SynConst.SByte _, _) - | SynExpr.Const(SynConst.Int16 _, _) - | SynExpr.Const(SynConst.Int32 _, TextContainsLetter) - | SynExpr.Const(SynConst.Int64 _, _) - | SynExpr.Const(SynConst.IntPtr _, _) - | SynExpr.Const(SynConst.Decimal _, _) - | SynExpr.Const(SynConst.Double _, (TextEndsWithNumber | TextContainsLetter)) - | SynExpr.Const(SynConst.Single _, _) - | SynExpr.Const(SynConst.Measure _, _) - | SynExpr.Const(SynConst.UserNum _, _) -> Some DotSafeNumericLiteral - | _ -> None - - match expr, path with - // Check for nested matches, e.g., - // - // match … with … -> (…, match … with … -> … | … -> …) | … -> … - | SynExpr.Paren _, SyntaxNode.SynMatchClause _ :: path -> unnecessaryParentheses expr path - - // We always need parens for trait calls, e.g., - // - // let inline f x = (^a : (static member Parse : string -> ^a) x) - | SynExpr.Paren(expr = SynExpr.TraitCall _), _ -> ValueNone - - // Don't touch library-only stuff: - // - // (# "ldlen.multi 2 0" array : int #) - | SynExpr.Paren(expr = SynExpr.LibraryOnlyILAssembly _), _ - | SynExpr.Paren(expr = SynExpr.LibraryOnlyStaticOptimization _), _ - | SynExpr.Paren(expr = SynExpr.LibraryOnlyUnionCaseFieldGet _), _ - | SynExpr.Paren(expr = SynExpr.LibraryOnlyUnionCaseFieldSet _), _ -> ValueNone - - // Parens are required around the body expresion of a binding - // if the parenthesized expression would be invalid without its parentheses, e.g., - // - // let x = (x - // + y) - | SynExpr.Paren(rightParenRange = Some _; range = parenRange), - SyntaxNode.SynBinding(SynBinding(trivia = { LeadingKeyword = leadingKeyword })) :: _ when - containsSensitiveIndentation leadingKeyword.Range.StartColumn parenRange - -> - ValueNone - - // Parens are otherwise never required for binding bodies or for top-level expressions, e.g., - // - // let x = (…) - // _.member X = (…) - // (printfn "Hello, world.") - | SynExpr.Paren(rightParenRange = Some _; range = range), SyntaxNode.SynBinding _ :: _ - | SynExpr.Paren(rightParenRange = Some _; range = range), SyntaxNode.SynModule _ :: _ -> ValueSome range - - // Parens must be kept when there is a high-precedence function application - // before a prefix operator application before another expression that starts with a symbol, e.g., - // - // id -(-x) - // id -(-1y) - // id -($"") - // id -(@"") - // id -(<@ ValueNone @>) - // let (~+) _ = true in assert +($"{true}") - | SynExpr.Paren(expr = PrefixApp _ | StartsWithSymbol), - SyntaxNode.SynExpr(SynExpr.App _) :: SyntaxNode.SynExpr(HighPrecedenceApp | SynExpr.Assert _ | SynExpr.InferredUpcast _ | SynExpr.InferredDowncast _) :: _ -> - ValueNone - - // Parens are never required around suffixed or infixed numeric literals, e.g., - // - // (1l).ToString() - // (1uy).ToString() - // (0b1).ToString() - // (1e10).ToString() - // (1.0).ToString() - | SynExpr.Paren(expr = DotSafeNumericLiteral; rightParenRange = Some _; range = range), _ -> ValueSome range - - // Parens are required around bare decimal ints or doubles ending - // in dots when being dotted into, e.g., - // - // (1).ToString() - // (1.).ToString() - | SynExpr.Paren(expr = SynExpr.Const(constant = SynConst.Int32 _ | SynConst.Double _)), - SyntaxNode.SynExpr(SynExpr.DotGet _) :: _ -> ValueNone - - // Parens are required around join conditions: - // - // join … on (… = …) - | SynExpr.Paren(expr = SynExpr.App _), SyntaxNode.SynExpr(SynExpr.App _) :: SyntaxNode.SynExpr(SynExpr.JoinIn _) :: _ -> - ValueNone - - // Parens are not required around a few anointed expressions after inherit: - // - // inherit T(3) - // inherit T(null) - // inherit T("") - // … - | SynExpr.Paren(expr = AtomicExprAfterType; range = range), SyntaxNode.SynMemberDefn(SynMemberDefn.ImplicitInherit _) :: _ -> - ValueSome range - - // Parens are otherwise required in inherit T(x), etc. - | SynExpr.Paren _, SyntaxNode.SynMemberDefn(SynMemberDefn.ImplicitInherit _) :: _ -> ValueNone - - // We can't remove parens when they're required for fluent calls: - // - // x.M(y).N z - // x.M(y).[z] - // _.M(x) - // (f x)[z] - // (f(x))[z] - // x.M(y)[z] - | SynExpr.Paren _, - SyntaxNode.SynExpr(SynExpr.App _) :: SyntaxNode.SynExpr(SynExpr.DotGet _ | SynExpr.DotIndexedGet _ | SynExpr.DotLambda _) :: _ - | SynExpr.Paren(expr = SynExpr.App _), - SyntaxNode.SynExpr(SynExpr.App(argExpr = SynExpr.ArrayOrListComputed(isArray = false))) :: _ - | SynExpr.Paren _, - SyntaxNode.SynExpr(SynExpr.App _) :: SyntaxNode.SynExpr(SynExpr.App(argExpr = SynExpr.ArrayOrListComputed(isArray = false))) :: _ -> - ValueNone - - // Parens must stay around binary equals expressions in argument - // position lest they be interpreted as named argument assignments: - // - // o.M((x = y)) - // o.N((x = y), z) - | SynExpr.Paren(expr = SynExpr.Paren(expr = InfixApp(Relational(OriginalNotation "="), _))), - SyntaxNode.SynExpr(SynExpr.App(funcExpr = SynExpr.LongIdent _)) :: _ - | SynExpr.Paren(expr = InfixApp(Relational(OriginalNotation "="), _)), - SyntaxNode.SynExpr(SynExpr.Paren _) :: SyntaxNode.SynExpr(SynExpr.App(funcExpr = SynExpr.LongIdent _)) :: _ - | SynExpr.Paren(expr = InfixApp(Relational(OriginalNotation "="), _)), - SyntaxNode.SynExpr(SynExpr.Tuple(isStruct = false)) :: SyntaxNode.SynExpr(SynExpr.Paren _) :: SyntaxNode.SynExpr(SynExpr.App( - funcExpr = SynExpr.LongIdent _)) :: _ -> ValueNone - - // The :: operator is parsed differently from other symbolic infix operators, - // so we need to give it special treatment. - - // Outer right: - // - // (x) :: xs - // (x * y) :: zs - // … - | SynExpr.Paren(rightParenRange = Some _), - SyntaxNode.SynExpr(SynExpr.Tuple(isStruct = false; exprs = [ SynExpr.Paren _; _ ])) :: (SyntaxNode.SynExpr(SynExpr.App( - isInfix = true)) :: _ as path) -> unnecessaryParentheses expr path - - // Outer left: - // - // x :: (xs) - // x :: (ys @ zs) - // … - | SynExpr.Paren(rightParenRange = Some _) as argExpr, - SyntaxNode.SynExpr(SynExpr.Tuple(isStruct = false; exprs = [ _; SynExpr.Paren _ ])) :: SyntaxNode.SynExpr(SynExpr.App( - isInfix = true) as outer) :: path -> - unnecessaryParentheses - expr - (SyntaxNode.SynExpr(SynExpr.App(ExprAtomicFlag.NonAtomic, false, outer, argExpr, outer.Range)) - :: path) - - // Ordinary nested expressions. - | SynExpr.Paren(expr = inner; leftParenRange = leftParenRange; rightParenRange = Some _ as rightParenRange; range = range), - SyntaxNode.SynExpr outer :: outerPath when not (containsSensitiveIndentation outer.Range.StartColumn range) -> - let dangling expr = - match expr with - | Dangling.Problematic subExpr -> - let parenzedSubExpr = SynExpr.Paren(subExpr, leftParenRange, rightParenRange, range) - - match outer with - | SynExpr.Tuple(exprs = exprs) -> not (obj.ReferenceEquals(subExpr, List.last exprs)) - | InfixApp(_, Left) -> true - | _ -> unnecessaryParentheses parenzedSubExpr outerPath |> ValueOption.isNone - - | _ -> false - - let problematic (exprRange: range) (delimiterRange: range) = - exprRange.EndLine = delimiterRange.EndLine - && exprRange.EndColumn < delimiterRange.StartColumn - - let anyProblematic matchOrTryRange clauses = - let rec loop = - function - | [] -> false - | SynMatchClause(trivia = trivia) :: clauses -> - trivia.BarRange |> Option.exists (problematic matchOrTryRange) - || trivia.ArrowRange |> Option.exists (problematic matchOrTryRange) - || loop clauses - - loop clauses - - match outer, inner with - | ConfusableWithTypeApp, _ -> ValueNone - - | SynExpr.IfThenElse _, Dangling.Sequential _ -> ValueNone - - | SynExpr.IfThenElse(trivia = trivia), Dangling.IfThen ifThenElse when - problematic ifThenElse.Range trivia.ThenKeyword - || trivia.ElseKeyword |> Option.exists (problematic ifThenElse.Range) - -> - ValueNone - - | SynExpr.TryFinally(trivia = trivia), Dangling.Try tryExpr when problematic tryExpr.Range trivia.FinallyKeyword -> - ValueNone - - | SynExpr.Match(clauses = clauses; trivia = { WithKeyword = withKeyword }), Dangling.Match matchOrTry when - problematic matchOrTry.Range withKeyword - || anyProblematic matchOrTry.Range clauses - -> - ValueNone - - | SynExpr.MatchBang(clauses = clauses; trivia = { WithKeyword = withKeyword }), Dangling.Match matchOrTry when - problematic matchOrTry.Range withKeyword - || anyProblematic matchOrTry.Range clauses - -> - ValueNone - - | SynExpr.MatchLambda(matchClauses = clauses), Dangling.Match matchOrTry when anyProblematic matchOrTry.Range clauses -> - ValueNone - - | SynExpr.TryWith(withCases = clauses; trivia = trivia), Dangling.Match matchOrTry when - problematic matchOrTry.Range trivia.WithKeyword - || anyProblematic matchOrTry.Range clauses - -> - ValueNone - - | SynExpr.Sequential(expr1 = SynExpr.Paren(expr = Is inner); expr2 = expr2), Dangling.Problematic _ when - problematic inner.Range expr2.Range - -> - ValueNone - - | SynExpr.Record(copyInfo = Some(SynExpr.Paren(expr = Is inner), _)), Dangling.Problematic _ - | SynExpr.AnonRecd(copyInfo = Some(SynExpr.Paren(expr = Is inner), _)), Dangling.Problematic _ -> ValueNone - - | SynExpr.Record(recordFields = recordFields), Dangling.Problematic _ -> - let rec loop recordFields = - match recordFields with - | [] -> ValueSome range - | SynExprRecordField(expr = Some(SynExpr.Paren(expr = Is inner)); blockSeparator = Some _) :: SynExprRecordField( - fieldName = SynLongIdent(id = id :: _), _) :: _ -> - if problematic inner.Range id.idRange then - ValueNone - else - ValueSome range - | _ :: recordFields -> loop recordFields - - loop recordFields - - | SynExpr.AnonRecd(recordFields = recordFields), Dangling.Problematic _ -> - let rec loop recordFields = - match recordFields with - | [] -> ValueSome range - | (_, Some _blockSeparator, SynExpr.Paren(expr = Is inner)) :: (SynLongIdent(id = id :: _), _, _) :: _ -> - if problematic inner.Range id.idRange then - ValueNone - else - ValueSome range - | _ :: recordFields -> loop recordFields - - loop recordFields - - | SynExpr.Paren _, SynExpr.Typed _ - | SynExpr.Quote _, SynExpr.Typed _ - | SynExpr.AnonRecd _, SynExpr.Typed _ - | SynExpr.Record _, SynExpr.Typed _ - | SynExpr.While(doExpr = SynExpr.Paren(expr = Is inner)), SynExpr.Typed _ - | SynExpr.WhileBang(doExpr = SynExpr.Paren(expr = Is inner)), SynExpr.Typed _ - | SynExpr.For(doBody = Is inner), SynExpr.Typed _ - | SynExpr.ForEach(bodyExpr = Is inner), SynExpr.Typed _ - | SynExpr.Match _, SynExpr.Typed _ - | SynExpr.Do _, SynExpr.Typed _ - | SynExpr.LetOrUse(body = Is inner), SynExpr.Typed _ - | SynExpr.TryWith _, SynExpr.Typed _ - | SynExpr.TryFinally _, SynExpr.Typed _ -> ValueSome range - | _, SynExpr.Typed _ -> ValueNone - - | OuterBinaryExpr inner (outerPrecedence, side), InnerBinaryExpr innerPrecedence -> - let ambiguous = - match compare outerPrecedence innerPrecedence with - | 0 -> - match side, Assoc.ofPrecedence innerPrecedence with - | Non, _ - | _, Non - | Left, Right -> true - | Right, Right - | Left, Left -> false - | Right, Left -> - outerPrecedence <> innerPrecedence - || match outerPrecedence, innerPrecedence with - | _, MulDivMod(Div, _) - | _, MulDivMod(Mod, _) - | _, AddSub(Sub, _) -> true - | Relational _, Relational _ -> true - | _ -> false - - | c -> c > 0 - - if ambiguous || dangling inner then - ValueNone - else - ValueSome range - - | OuterBinaryExpr inner (_, Right), (SynExpr.Sequential _ | SynExpr.LetOrUse(trivia = { InKeyword = None })) -> ValueNone - | OuterBinaryExpr inner (_, Right), inner -> if dangling inner then ValueNone else ValueSome range - - // new T(expr) - | SynExpr.New _, AtomicExprAfterType -> ValueSome range - | SynExpr.New _, _ -> ValueNone - - // { inherit T(expr); … } - | SynExpr.Record(baseInfo = Some(_, SynExpr.Paren(expr = Is inner), _, _, _)), AtomicExprAfterType -> ValueSome range - | SynExpr.Record(baseInfo = Some(_, SynExpr.Paren(expr = Is inner), _, _, _)), _ -> ValueNone - - | _, SynExpr.Paren _ - | _, SynExpr.Quote _ - | _, SynExpr.Const _ - | _, SynExpr.Tuple(isStruct = true) - | _, SynExpr.AnonRecd _ - | _, SynExpr.ArrayOrList _ - | _, SynExpr.Record _ - | _, SynExpr.ObjExpr _ - | _, SynExpr.ArrayOrListComputed _ - | _, SynExpr.ComputationExpr _ - | _, SynExpr.TypeApp _ - | _, SynExpr.Ident _ - | _, SynExpr.LongIdent _ - | _, SynExpr.DotGet _ - | _, SynExpr.DotLambda _ - | _, SynExpr.DotIndexedGet _ - | _, SynExpr.Null _ - | _, SynExpr.InterpolatedString _ - - | SynExpr.Paren _, _ - | SynExpr.Quote _, _ - | SynExpr.Typed _, _ - | SynExpr.AnonRecd _, _ - | SynExpr.Record _, _ - | SynExpr.ObjExpr _, _ - | SynExpr.While _, _ - | SynExpr.WhileBang _, _ - | SynExpr.For _, _ - | SynExpr.ForEach _, _ - | SynExpr.Lambda _, _ - | SynExpr.MatchLambda _, _ - | SynExpr.Match _, _ - | SynExpr.MatchBang _, _ - | SynExpr.LetOrUse _, _ - | SynExpr.LetOrUseBang _, _ - | SynExpr.Sequential _, _ - | SynExpr.Do _, _ - | SynExpr.DoBang _, _ - | SynExpr.IfThenElse _, _ - | SynExpr.TryWith _, _ - | SynExpr.TryFinally _, _ - | SynExpr.ComputationExpr _, _ - | SynExpr.InterpolatedString _, _ -> ValueSome range - - | _ -> ValueNone - - | _ -> ValueNone - - module SynPat = - let (|Last|) = List.last - - /// Matches if any pattern in the given list is a SynPat.Typed. - [] - let (|AnyTyped|_|) pats = - if - pats - |> List.exists (function - | SynPat.Typed _ -> true - | _ -> false) - then - ValueSome AnyTyped - else - ValueNone - - /// Matches if any member in the given list is an inherit - /// or implementation of an interface with generic type args. - [] - let (|AnyGenericInheritOrInterfaceImpl|_|) members = - if - members - |> List.exists (function - | SynMemberDefn.ImplicitInherit(inheritType = SynType.App(typeArgs = _ :: _)) - | SynMemberDefn.ImplicitInherit(inheritType = SynType.LongIdentApp(typeArgs = _ :: _)) - | SynMemberDefn.Interface(interfaceType = SynType.App(typeArgs = _ :: _)) - | SynMemberDefn.Interface(interfaceType = SynType.LongIdentApp(typeArgs = _ :: _)) -> true - | _ -> false) - then - ValueSome AnyGenericInheritOrInterfaceImpl - else - ValueNone - - /// Matches the rightmost potentially dangling nested pattern. - let rec (|Rightmost|) pat = - match pat with - | SynPat.Or(rhsPat = Rightmost pat) - | SynPat.ListCons(rhsPat = Rightmost pat) - | SynPat.As(rhsPat = Rightmost pat) - | SynPat.Ands(pats = Last(Rightmost pat)) - | SynPat.Tuple(isStruct = false; elementPats = Last(Rightmost pat)) -> pat - | pat -> pat - - /// Matches if the given pattern is atomic. - [] - let (|Atomic|_|) pat = - match pat with - | SynPat.Named _ - | SynPat.Wild _ - | SynPat.Paren _ - | SynPat.Tuple(isStruct = true) - | SynPat.Record _ - | SynPat.ArrayOrList _ - | SynPat.Const _ - | SynPat.LongIdent(argPats = SynArgPats.Pats []) - | SynPat.Null _ - | SynPat.QuoteExpr _ -> ValueSome Atomic - | _ -> ValueNone - - /// If the given pattern is a parenthesized pattern and the parentheses - /// are unnecessary in the given context, returns the unnecessary parentheses' range. - let unnecessaryParentheses pat path = - match pat, path with - // Parens are needed in: - // - // let (Pattern …) = … - // let (x: …, y…) = … - // let (x: …), (y: …) = … - // let! (x: …) = … - // and! (x: …) = … - // use! (x: …) = … - // _.member M(x: …) = … - // match … with (x: …) -> … - // match … with (x, y: …) -> … - // function (x: …) -> … - // fun (x, y, …) -> … - // fun (x: …) -> … - // fun (Pattern …) -> … - | SynPat.Paren(SynPat.Typed _, _), SyntaxNode.SynPat(Rightmost rightmost) :: SyntaxNode.SynMatchClause _ :: _ when - obj.ReferenceEquals(pat, rightmost) - -> - ValueNone - | SynPat.Paren(Rightmost(SynPat.Typed _), _), SyntaxNode.SynMatchClause _ :: _ - | SynPat.Paren(SynPat.Typed _, _), SyntaxNode.SynExpr(SynExpr.LetOrUseBang _) :: _ - | SynPat.Paren(SynPat.Typed _, _), - SyntaxNode.SynPat(SynPat.Tuple(isStruct = false)) :: SyntaxNode.SynExpr(SynExpr.LetOrUseBang _) :: _ - | SynPat.Paren(SynPat.Tuple(isStruct = false; elementPats = AnyTyped), _), SyntaxNode.SynExpr(SynExpr.LetOrUseBang _) :: _ - | SynPat.Paren(SynPat.Typed _, _), SyntaxNode.SynPat(SynPat.Tuple(isStruct = false)) :: SyntaxNode.SynBinding _ :: _ - | SynPat.Paren(SynPat.Tuple(isStruct = false; elementPats = AnyTyped), _), SyntaxNode.SynBinding _ :: _ - | SynPat.Paren(SynPat.LongIdent(argPats = SynArgPats.Pats(_ :: _)), _), SyntaxNode.SynBinding _ :: _ - | SynPat.Paren(SynPat.LongIdent(argPats = SynArgPats.Pats(_ :: _)), _), SyntaxNode.SynExpr(SynExpr.Lambda _) :: _ - | SynPat.Paren(SynPat.Tuple(isStruct = false), _), SyntaxNode.SynExpr(SynExpr.Lambda(parsedData = Some _)) :: _ - | SynPat.Paren(SynPat.Typed _, _), SyntaxNode.SynExpr(SynExpr.Lambda(parsedData = Some _)) :: _ -> ValueNone - - // () is parsed as this. - | SynPat.Paren(SynPat.Const(SynConst.Unit, _), _), _ -> ValueNone - - // (()) is required when overriding a generic member - // where unit is the generic type argument: - // - // type C<'T> = abstract M : 'T -> unit - // let _ = { new C with override _.M (()) = () } - | SynPat.Paren(SynPat.Paren(SynPat.Const(SynConst.Unit, _), _), _), - SyntaxNode.SynPat(SynPat.LongIdent _) :: SyntaxNode.SynBinding _ :: SyntaxNode.SynExpr(SynExpr.ObjExpr( - objType = SynType.App(typeArgs = _ :: _) | SynType.LongIdentApp(typeArgs = _ :: _))) :: _ - | SynPat.Paren(SynPat.Paren(SynPat.Const(SynConst.Unit, _), _), _), - SyntaxNode.SynPat(SynPat.LongIdent _) :: SyntaxNode.SynBinding _ :: SyntaxNode.SynMemberDefn _ :: SyntaxNode.SynTypeDefn(SynTypeDefn( - typeRepr = SynTypeDefnRepr.ObjectModel(members = AnyGenericInheritOrInterfaceImpl))) :: _ -> ValueNone - - // Parens are required around the atomic argument of - // any additional `new` constructor that is not the last. - // - // type T … = - // new (x) = … - // new (x, y) = … - | SynPat.Paren(Atomic, range), - SyntaxNode.SynPat(SynPat.LongIdent(longDotId = SynLongIdent(id = [ Ident "new" ]))) :: SyntaxNode.SynBinding _ :: SyntaxNode.SynMemberDefn _ :: SyntaxNode.SynTypeDefn(SynTypeDefn( - typeRepr = SynTypeDefnRepr.ObjectModel(members = members))) :: _ -> - let lastNew = - (ValueNone, members) - ||> List.fold (fun lastNew ``member`` -> - match ``member`` with - | SynMemberDefn.Member( - memberDefn = SynBinding(headPat = SynPat.LongIdent(longDotId = SynLongIdent(id = [ Ident "new" ])))) -> - ValueSome ``member`` - | _ -> lastNew) - - match lastNew with - | ValueSome(SynMemberDefn.Member(memberDefn = SynBinding(headPat = SynPat.LongIdent(argPats = SynArgPats.Pats [ Is pat ])))) -> - ValueSome range - | _ -> ValueNone - - // Parens are otherwise never needed in these cases: - // - // let (x: …) = … - // for (…) in (…) do … - // let! (…) = … - // and! (…) = … - // use! (…) = … - // match … with (…) -> … - // function (…) -> … - // function (Pattern …) -> … - // fun (x) -> … - | SynPat.Paren(_, range), SyntaxNode.SynBinding _ :: _ - | SynPat.Paren(_, range), SyntaxNode.SynExpr(SynExpr.ForEach _) :: _ - | SynPat.Paren(_, range), SyntaxNode.SynExpr(SynExpr.LetOrUseBang _) :: _ - | SynPat.Paren(_, range), SyntaxNode.SynMatchClause _ :: _ - | SynPat.Paren(Atomic, range), SyntaxNode.SynExpr(SynExpr.Lambda(parsedData = Some _)) :: _ -> ValueSome range - - // Nested patterns. - | SynPat.Paren(inner, range), SyntaxNode.SynPat outer :: _ -> - match outer, inner with - // (x :: xs) :: ys - // (x, xs) :: ys - | SynPat.ListCons(lhsPat = SynPat.Paren(pat = Is inner)), SynPat.ListCons _ - | SynPat.ListCons(lhsPat = SynPat.Paren(pat = Is inner)), SynPat.Tuple(isStruct = false) -> ValueNone - - // A as (B | C) - // A as (B & C) - // x as (y, z) - // xs as (y :: zs) - | SynPat.As(rhsPat = SynPat.Paren(pat = Is inner)), - (SynPat.Or _ | SynPat.Ands _ | SynPat.Tuple(isStruct = false) | SynPat.ListCons _) -> ValueNone - - // (A | B) :: xs - // (A & B) :: xs - // (x as y) :: xs - | SynPat.ListCons _, SynPat.Or _ - | SynPat.ListCons _, SynPat.Ands _ - | SynPat.ListCons _, SynPat.As _ -> ValueNone - - // Pattern (x = (…)) - | SynPat.LongIdent(argPats = SynArgPats.NamePatPairs _), _ -> ValueSome range - - // Pattern (x : int) - // Pattern ([] x) - // Pattern (:? int) - // Pattern (A :: _) - // Pattern (A | B) - // Pattern (A & B) - // Pattern (A as B) - // Pattern (A, B) - // Pattern1 (Pattern2 (x = A)) - // Pattern1 (Pattern2 x y) - | SynPat.LongIdent _, SynPat.Typed _ - | SynPat.LongIdent _, SynPat.Attrib _ - | SynPat.LongIdent _, SynPat.IsInst _ - | SynPat.LongIdent _, SynPat.ListCons _ - | SynPat.LongIdent _, SynPat.Or _ - | SynPat.LongIdent _, SynPat.Ands _ - | SynPat.LongIdent _, SynPat.As _ - | SynPat.LongIdent _, SynPat.Tuple(isStruct = false) - | SynPat.LongIdent _, SynPat.LongIdent(argPats = SynArgPats.NamePatPairs _) - | SynPat.LongIdent _, SynPat.LongIdent(argPats = SynArgPats.Pats(_ :: _)) - - // A | (B as C) - // A & (B as C) - // A, (B as C) - | SynPat.Or _, SynPat.As _ - | SynPat.Ands _, SynPat.As _ - | SynPat.Tuple _, SynPat.As _ - - // x, (y, z) - // x & (y, z) - // (x, y) & z - | SynPat.Tuple _, SynPat.Tuple(isStruct = false) - | SynPat.Ands _, SynPat.Tuple(isStruct = false) - - // A, (B | C) - // A & (B | C) - | SynPat.Tuple _, SynPat.Or _ - | SynPat.Ands _, SynPat.Or _ -> ValueNone - - // (x : int) & y - // x & (y : int) & z - | SynPat.Ands(Last(SynPat.Paren(pat = Is inner)), _), SynPat.Typed _ -> ValueSome range - | SynPat.Ands _, SynPat.Typed _ -> ValueNone - - | _, SynPat.Const _ - | _, SynPat.Wild _ - | _, SynPat.Named _ - | _, SynPat.Typed _ - | _, SynPat.LongIdent(argPats = SynArgPats.Pats []) - | _, SynPat.Tuple(isStruct = true) - | _, SynPat.Paren _ - | _, SynPat.ArrayOrList _ - | _, SynPat.Record _ - | _, SynPat.Null _ - | _, SynPat.OptionalVal _ - | _, SynPat.IsInst _ - | _, SynPat.QuoteExpr _ - - | SynPat.Or _, _ - | SynPat.ListCons _, _ - | SynPat.Ands _, _ - | SynPat.As _, _ - | SynPat.LongIdent _, _ - | SynPat.Tuple _, _ - | SynPat.Paren _, _ - | SynPat.ArrayOrList _, _ - | SynPat.Record _, _ -> ValueSome range - - | _ -> ValueNone - - | _ -> ValueNone - - let getUnnecessaryParentheses (getSourceLineStr: int -> string) (parsedInput: ParsedInput) : Async = - async { - let ranges = HashSet Range.comparer - - let visitor = - { new SyntaxVisitorBase() with - member _.VisitExpr(path, _, defaultTraverse, expr) = - SynExpr.unnecessaryParentheses getSourceLineStr expr path - |> ValueOption.iter (ranges.Add >> ignore) - - defaultTraverse expr - - member _.VisitPat(path, defaultTraverse, pat) = - SynPat.unnecessaryParentheses pat path - |> ValueOption.iter (ranges.Add >> ignore) - - defaultTraverse pat - } - - SyntaxTraversal.traverseAll visitor parsedInput - return ranges - } diff --git a/src/Compiler/Service/ServiceAnalysis.fsi b/src/Compiler/Service/ServiceAnalysis.fsi index 836bfce0c56..672cf088759 100644 --- a/src/Compiler/Service/ServiceAnalysis.fsi +++ b/src/Compiler/Service/ServiceAnalysis.fsi @@ -3,7 +3,6 @@ namespace FSharp.Compiler.EditorServices open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.Syntax open FSharp.Compiler.Text module public UnusedOpens = @@ -32,14 +31,3 @@ module public UnusedDeclarations = /// Get all unused declarations in a file val getUnusedDeclarations: checkFileResults: FSharpCheckFileResults * isScriptFile: bool -> Async> - -module public UnnecessaryParentheses = - - /// Gets the ranges of all unnecessary pairs of parentheses in a file. - /// - /// Note that this may include pairs of nested ranges each of whose - /// lack of necessity depends on the other's presence, such - /// that it is valid to remove either set of parentheses but not both, e.g.: - /// - /// (x.M(y)).N → (x.M y).N ↮ x.M(y).N - val getUnnecessaryParentheses: getSourceLineStr: (int -> string) -> parsedInput: ParsedInput -> Async diff --git a/src/Compiler/Service/ServiceLexing.fs b/src/Compiler/Service/ServiceLexing.fs index e3f4dcc3c4e..66893ac950e 100644 --- a/src/Compiler/Service/ServiceLexing.fs +++ b/src/Compiler/Service/ServiceLexing.fs @@ -1042,8 +1042,8 @@ type FSharpLineTokenizer(lexbuf: UnicodeLexing.Lexbuf, maxLength: int option, fi 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) + | ValueNone -> () + | ValueSome 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) diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index 317f4bf84da..ec5b623d0b8 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -13,7 +13,6 @@ open FSharp.Compiler.Text open FSharp.Compiler.Text.Position open FSharp.Compiler.Text.Range -/// used to track route during traversal AST [] type SyntaxNode = | SynPat of SynPat @@ -31,6 +30,23 @@ type SyntaxNode = | SynTypeDefnSig of SynTypeDefnSig | SynMemberSig of SynMemberSig + member this.Range = + match this with + | SynPat pat -> pat.Range + | SynType ty -> ty.Range + | SynExpr expr -> expr.Range + | SynModule modul -> modul.Range + | SynModuleOrNamespace moduleOrNamespace -> moduleOrNamespace.Range + | SynTypeDefn tyDef -> tyDef.Range + | SynMemberDefn memberDef -> memberDef.Range + | SynMatchClause matchClause -> matchClause.Range + | SynBinding binding -> binding.RangeOfBindingWithRhs + | SynModuleOrNamespaceSig moduleOrNamespaceSig -> moduleOrNamespaceSig.Range + | SynModuleSigDecl moduleSigDecl -> moduleSigDecl.Range + | SynValSig(SynValSig.SynValSig(range = range)) -> range + | SynTypeDefnSig tyDefSig -> tyDefSig.Range + | SynMemberSig memberSig -> memberSig.Range + type SyntaxVisitorPath = SyntaxNode list [] @@ -163,10 +179,10 @@ type SyntaxVisitorBase<'T>() = None /// VisitSimplePats allows overriding behavior when visiting simple pats - abstract VisitSimplePats: path: SyntaxVisitorPath * synPats: SynSimplePat list -> 'T option + abstract VisitSimplePats: path: SyntaxVisitorPath * pat: SynPat -> 'T option - default _.VisitSimplePats(path, synPats) = - ignore (path, synPats) + default _.VisitSimplePats(path, pat) = + ignore (path, pat) None /// VisitPat allows overriding behavior when visiting patterns @@ -211,6 +227,15 @@ type SyntaxVisitorBase<'T>() = ignore path defaultTraverse valSig +[] +module private ParsedInputExtensions = + type ParsedInput with + + member parsedInput.Contents = + match parsedInput with + | ParsedInput.ImplFile file -> file.Contents |> List.map SyntaxNode.SynModuleOrNamespace + | ParsedInput.SigFile file -> file.Contents |> List.map SyntaxNode.SynModuleOrNamespaceSig + /// A range of utility functions to assist with traversing an AST module SyntaxTraversal = // treat ranges as though they are half-open: [,) @@ -304,7 +329,7 @@ module SyntaxTraversal = (pick: pos -> range -> obj -> (range * (unit -> 'T option)) list -> 'T option) (pos: pos) (visitor: SyntaxVisitorBase<'T>) - (parseTree: ParsedInput) + (ast: SyntaxNode list) : 'T option = let pick x = pick pos x @@ -749,15 +774,18 @@ module SyntaxTraversal = visitor.VisitPat(origPath, defaultTraverse, pat) - and traverseSynSimplePats origPath (pats: SynSimplePat list) = - match visitor.VisitSimplePats(origPath, pats) with + and traverseSynSimplePats origPath (pat: SynPat) = + match visitor.VisitSimplePats(origPath, pat) with | None -> - pats - |> List.tryPick (fun pat -> + let rec loop (pat: SynPat) = match pat with - | SynSimplePat.Attrib(attributes = attributes; range = m) -> - attributeApplicationDives origPath attributes |> pick m attributes - | _ -> None) + | SynPat.Paren(pat = pat) + | SynPat.Typed(pat = pat) -> loop pat + | SynPat.Tuple(elementPats = pats) -> List.tryPick loop pats + | SynPat.Attrib(_, attributes, m) -> attributeApplicationDives origPath attributes |> pick m attributes + | _ -> None + + loop pat | x -> x and traverseSynType origPath (StripParenTypes ty) = @@ -886,9 +914,8 @@ module SyntaxTraversal = traverseSynBinding path getBinding |> Option.orElseWith (fun () -> traverseSynBinding path setBinding) - | SynMemberDefn.ImplicitCtor(ctorArgs = simplePats) -> - match simplePats with - | SynSimplePats.SimplePats(pats = simplePats) -> traverseSynSimplePats path simplePats + | SynMemberDefn.ImplicitCtor(ctorArgs = pat) -> traverseSynSimplePats path pat + | SynMemberDefn.ImplicitInherit(synType, synExpr, _identOption, range) -> [ dive () synType.Range (fun () -> @@ -1060,40 +1087,377 @@ module SyntaxTraversal = attributeApplicationDives path attributes |> pick m.Range attributes | SynMemberSig.NestedType(nestedType = nestedType) -> traverseSynTypeDefnSig path nestedType - match parseTree with - | ParsedInput.ImplFile file -> - let l = file.Contents + let fileRange = + (range0, ast) ||> List.fold (fun acc node -> unionRanges acc node.Range) + + ast + |> List.map (fun node -> + match node with + | SyntaxNode.SynModuleOrNamespace moduleOrNamespace -> + dive moduleOrNamespace moduleOrNamespace.Range (traverseSynModuleOrNamespace []) + | SyntaxNode.SynModuleOrNamespaceSig moduleOrNamespaceSig -> + dive moduleOrNamespaceSig moduleOrNamespaceSig.Range (traverseSynModuleOrNamespaceSig []) + | SyntaxNode.SynPat pat -> dive pat pat.Range (traversePat []) + | SyntaxNode.SynType ty -> dive ty ty.Range (traverseSynType []) + | SyntaxNode.SynExpr expr -> dive expr expr.Range (traverseSynExpr []) + | SyntaxNode.SynModule modul -> dive modul modul.Range (traverseSynModuleDecl []) + | SyntaxNode.SynTypeDefn tyDef -> dive tyDef tyDef.Range (traverseSynTypeDefn []) + | SyntaxNode.SynMemberDefn memberDef -> dive memberDef memberDef.Range (traverseSynMemberDefn [] (fun _ -> None)) + | SyntaxNode.SynMatchClause matchClause -> dive matchClause matchClause.Range (traverseSynMatchClause []) + | SyntaxNode.SynBinding binding -> dive binding binding.RangeOfBindingWithRhs (traverseSynBinding []) + | SyntaxNode.SynModuleSigDecl moduleSigDecl -> dive moduleSigDecl moduleSigDecl.Range (traverseSynModuleSigDecl []) + | SyntaxNode.SynValSig(SynValSig.SynValSig(range = range) as valSig) -> dive valSig range (traverseSynValSig []) + | SyntaxNode.SynTypeDefnSig tyDefSig -> dive tyDefSig tyDefSig.Range (traverseSynTypeDefnSig []) + | SyntaxNode.SynMemberSig memberSig -> dive memberSig memberSig.Range (traverseSynMemberSig [])) + |> pick fileRange ast - let fileRange = -#if DEBUG - match l with - | [] -> range0 - | _ -> l |> List.map (fun x -> x.Range) |> List.reduce unionRanges -#else - range0 // only used for asserting, does not matter in non-debug -#endif - l - |> List.map (fun x -> dive x x.Range (traverseSynModuleOrNamespace [])) - |> pick fileRange l - | ParsedInput.SigFile sigFile -> - let l = sigFile.Contents + /// traverse an implementation file walking all the way down to SynExpr or TypeAbbrev at a particular location + /// + let Traverse (pos: pos, parseTree: ParsedInput, visitor: SyntaxVisitorBase<'T>) = + traverseUntil pick pos visitor parseTree.Contents - let fileRange = -#if DEBUG - match l with - | [] -> range0 - | _ -> l |> List.map (fun x -> x.Range) |> List.reduce unionRanges -#else - range0 // only used for asserting, does not matter in non-debug -#endif - l - |> List.map (fun x -> dive x x.Range (traverseSynModuleOrNamespaceSig [])) - |> pick fileRange l - - let traverseAll (visitor: SyntaxVisitorBase<'T>) (parseTree: ParsedInput) : unit = - let pick _ _ _ diveResults = - let rec loop = - function +[] +[] +module SyntaxNode = + let (|Attributes|) node = + let (|All|) = List.collect + let field (SynField(attributes = attributes)) = attributes + let unionCase (SynUnionCase(attributes = attributes)) = attributes + let enumCase (SynEnumCase(attributes = attributes)) = attributes + let typar (SynTyparDecl(attributes = attributes)) = attributes + + let (|SynComponentInfo|) componentInfo = + match componentInfo with + | SynComponentInfo(attributes = attributes; typeParams = Some(SynTyparDecls.PrefixList(decls = All typar attributes'))) + | SynComponentInfo(attributes = attributes; typeParams = Some(SynTyparDecls.PostfixList(decls = All typar attributes'))) + | SynComponentInfo( + attributes = attributes; typeParams = Some(SynTyparDecls.SinglePrefix(decl = SynTyparDecl(attributes = attributes')))) -> + attributes @ attributes' + | SynComponentInfo(attributes = attributes) -> attributes + + let (|SynBinding|) binding = + match binding with + | SynBinding(attributes = attributes; returnInfo = Some(SynBindingReturnInfo(attributes = attributes'))) -> + attributes @ attributes' + | SynBinding(attributes = attributes) -> attributes + + match node with + | SyntaxNode.SynModuleOrNamespace(SynModuleOrNamespace(attribs = attributes)) + | SyntaxNode.SynModuleOrNamespaceSig(SynModuleOrNamespaceSig(attribs = attributes)) + | SyntaxNode.SynModule(SynModuleDecl.Attributes(attributes = attributes)) + | SyntaxNode.SynTypeDefn(SynTypeDefn(typeInfo = SynComponentInfo attributes)) + | SyntaxNode.SynTypeDefn(SynTypeDefn( + typeRepr = SynTypeDefnRepr.Simple(SynTypeDefnSimpleRepr.Record(recordFields = All field attributes), _))) + | SyntaxNode.SynTypeDefn(SynTypeDefn( + typeRepr = SynTypeDefnRepr.Simple(SynTypeDefnSimpleRepr.Union(unionCases = All unionCase attributes), _))) + | SyntaxNode.SynTypeDefn(SynTypeDefn( + typeRepr = SynTypeDefnRepr.Simple(SynTypeDefnSimpleRepr.Enum(cases = All enumCase attributes), _))) + | SyntaxNode.SynMemberDefn(SynMemberDefn.AutoProperty(attributes = attributes)) + | SyntaxNode.SynMemberDefn(SynMemberDefn.AbstractSlot(slotSig = SynValSig(attributes = attributes))) + | SyntaxNode.SynMemberDefn(SynMemberDefn.ImplicitCtor(attributes = attributes)) + | SyntaxNode.SynBinding(SynBinding attributes) + | SyntaxNode.SynPat(SynPat.Attrib(attributes = attributes)) + | SyntaxNode.SynType(SynType.SignatureParameter(attributes = attributes)) + | SyntaxNode.SynValSig(SynValSig(attributes = attributes)) -> attributes + | _ -> [] + +[] +[] +module ParsedInput = + let fold folder state (parsedInput: ParsedInput) = + let mutable state = state + + let visitor = + { new SyntaxVisitorBase() with + member _.VisitExpr(path, _, defaultTraverse, expr) = + match path with + | SyntaxNode.SynMemberDefn _ as parent :: path -> state <- folder state path parent + | _ -> () + + state <- folder state path (SyntaxNode.SynExpr expr) + defaultTraverse expr + + member _.VisitPat(path, defaultTraverse, pat) = + state <- folder state path (SyntaxNode.SynPat pat) + defaultTraverse pat + + member _.VisitType(path, defaultTraverse, synType) = + match path with + | SyntaxNode.SynMemberDefn _ | SyntaxNode.SynMemberSig _ as parent :: path -> state <- folder state path parent + | _ -> () + + state <- folder state path (SyntaxNode.SynType synType) + defaultTraverse synType + + member _.VisitModuleDecl(path, defaultTraverse, synModuleDecl) = + state <- folder state path (SyntaxNode.SynModule synModuleDecl) + + match synModuleDecl with + | SynModuleDecl.Types(types, _) -> + let path = SyntaxNode.SynModule synModuleDecl :: path + + for ty in types do + state <- folder state path (SyntaxNode.SynTypeDefn ty) + + | _ -> () + + defaultTraverse synModuleDecl + + member _.VisitModuleOrNamespace(path, synModuleOrNamespace) = + state <- folder state path (SyntaxNode.SynModuleOrNamespace synModuleOrNamespace) + None + + member _.VisitMatchClause(path, defaultTraverse, matchClause) = + state <- folder state path (SyntaxNode.SynMatchClause matchClause) + defaultTraverse matchClause + + member _.VisitBinding(path, defaultTraverse, synBinding) = + match path with + | SyntaxNode.SynMemberDefn _ as parent :: path -> state <- folder state path parent + | _ -> () + + state <- folder state path (SyntaxNode.SynBinding synBinding) + defaultTraverse synBinding + + member _.VisitModuleOrNamespaceSig(path, synModuleOrNamespaceSig) = + state <- folder state path (SyntaxNode.SynModuleOrNamespaceSig synModuleOrNamespaceSig) + None + + member _.VisitModuleSigDecl(path, defaultTraverse, synModuleSigDecl) = + state <- folder state path (SyntaxNode.SynModuleSigDecl synModuleSigDecl) + + match synModuleSigDecl with + | SynModuleSigDecl.Types(types, _) -> + let path = SyntaxNode.SynModuleSigDecl synModuleSigDecl :: path + + for ty in types do + state <- folder state path (SyntaxNode.SynTypeDefnSig ty) + + | _ -> () + + defaultTraverse synModuleSigDecl + + member _.VisitValSig(path, defaultTraverse, valSig) = + match path with + | SyntaxNode.SynMemberSig _ as parent :: path -> state <- folder state path parent + | _ -> () + + state <- folder state path (SyntaxNode.SynValSig valSig) + defaultTraverse valSig + + member _.VisitSimplePats(path, _pat) = + match path with + | SyntaxNode.SynMemberDefn _ as node :: path -> state <- folder state path node + | _ -> () + + None + + member _.VisitInterfaceSynMemberDefnType(path, _synType) = + match path with + | SyntaxNode.SynMemberDefn _ as node :: path -> state <- folder state path node + | _ -> () + + None + } + + let pickAll _ _ _ diveResults = + let rec loop diveResults = + match diveResults with + | [] -> None + | (_, project) :: rest -> + ignore (project ()) + loop rest + + loop diveResults + + let ast = parsedInput.Contents + let m = (range0, ast) ||> List.fold (fun acc node -> unionRanges acc node.Range) + ignore (SyntaxTraversal.traverseUntil pickAll m.End visitor ast) + state + + let private foldWhileImpl pick pos folder state (ast: SyntaxNode list) = + let mutable state = state + + let visitor = + { new SyntaxVisitorBase() with + member _.VisitExpr(path, _, defaultTraverse, expr) = + match path with + | SyntaxNode.SynMemberDefn _ as parent :: path -> + match folder state path parent with + | Some state' -> + match folder state' path (SyntaxNode.SynExpr expr) with + | Some state' -> + state <- state' + defaultTraverse expr + | None -> Some() + | None -> Some() + | _ -> + match folder state path (SyntaxNode.SynExpr expr) with + | Some state' -> + state <- state' + defaultTraverse expr + | None -> Some() + + member _.VisitPat(path, defaultTraverse, pat) = + match folder state path (SyntaxNode.SynPat pat) with + | Some state' -> + state <- state' + defaultTraverse pat + | None -> Some() + + member _.VisitType(path, defaultTraverse, synType) = + match path with + | SyntaxNode.SynMemberDefn _ | SyntaxNode.SynMemberSig _ as parent :: path -> + match folder state path parent with + | Some state' -> + match folder state' path (SyntaxNode.SynType synType) with + | Some state' -> + state <- state' + defaultTraverse synType + | None -> Some() + | None -> Some() + | _ -> + match folder state path (SyntaxNode.SynType synType) with + | Some state' -> + state <- state' + defaultTraverse synType + | None -> Some() + + member _.VisitModuleDecl(path, defaultTraverse, synModuleDecl) = + match folder state path (SyntaxNode.SynModule synModuleDecl) with + | Some state' -> + state <- state' + + match synModuleDecl with + | SynModuleDecl.Types(types, _) -> + let path = SyntaxNode.SynModule synModuleDecl :: path + + let rec loop types = + match types with + | [] -> defaultTraverse synModuleDecl + | ty :: types -> + match folder state path (SyntaxNode.SynTypeDefn ty) with + | Some state' -> + state <- state' + loop types + | None -> Some() + + loop types + + | _ -> defaultTraverse synModuleDecl + + | None -> Some() + + member _.VisitModuleOrNamespace(path, synModuleOrNamespace) = + match folder state path (SyntaxNode.SynModuleOrNamespace synModuleOrNamespace) with + | Some state' -> + state <- state' + None + | None -> Some() + + member _.VisitMatchClause(path, defaultTraverse, matchClause) = + match folder state path (SyntaxNode.SynMatchClause matchClause) with + | Some state' -> + state <- state' + defaultTraverse matchClause + | None -> Some() + + member _.VisitBinding(path, defaultTraverse, synBinding) = + match path with + | SyntaxNode.SynMemberDefn _ as parent :: path -> + match folder state path parent with + | Some state' -> + match folder state' path (SyntaxNode.SynBinding synBinding) with + | Some state' -> + state <- state' + defaultTraverse synBinding + | None -> Some() + | None -> Some() + | _ -> + match folder state path (SyntaxNode.SynBinding synBinding) with + | Some state' -> + state <- state' + defaultTraverse synBinding + | None -> Some() + + member _.VisitModuleOrNamespaceSig(path, synModuleOrNamespaceSig) = + match folder state path (SyntaxNode.SynModuleOrNamespaceSig synModuleOrNamespaceSig) with + | Some state' -> + state <- state' + None + | None -> Some() + + member _.VisitModuleSigDecl(path, defaultTraverse, synModuleSigDecl) = + match folder state path (SyntaxNode.SynModuleSigDecl synModuleSigDecl) with + | Some state' -> + state <- state' + + match synModuleSigDecl with + | SynModuleSigDecl.Types(types, _) -> + let path = SyntaxNode.SynModuleSigDecl synModuleSigDecl :: path + + let rec loop types = + match types with + | [] -> defaultTraverse synModuleSigDecl + | ty :: types -> + match folder state path (SyntaxNode.SynTypeDefnSig ty) with + | Some state' -> + state <- state' + loop types + | None -> Some() + + loop types + + | _ -> defaultTraverse synModuleSigDecl + + | None -> Some() + + member _.VisitValSig(path, defaultTraverse, valSig) = + match path with + | SyntaxNode.SynMemberSig _ as parent :: path -> + match folder state path parent with + | Some state' -> + match folder state' path (SyntaxNode.SynValSig valSig) with + | Some state' -> + state <- state' + defaultTraverse valSig + | None -> Some() + | None -> Some() + | _ -> + match folder state path (SyntaxNode.SynValSig valSig) with + | Some state' -> + state <- state' + defaultTraverse valSig + | None -> Some() + + member _.VisitSimplePats(path, _pat) = + match path with + | SyntaxNode.SynMemberDefn _ as node :: path -> + match folder state path node with + | Some state' -> + state <- state' + None + | None -> Some() + | _ -> None + + member _.VisitInterfaceSynMemberDefnType(path, _synType) = + match path with + | SyntaxNode.SynMemberDefn _ as node :: path -> + match folder state path node with + | Some state' -> + state <- state' + None + | None -> Some() + | _ -> None + } + + ignore (SyntaxTraversal.traverseUntil pick pos visitor ast) + state + + let foldWhile folder state (parsedInput: ParsedInput) = + let pickAll _ _ _ diveResults = + let rec loop diveResults = + match diveResults with | [] -> None | (_, project) :: rest -> ignore (project ()) @@ -1101,9 +1465,105 @@ module SyntaxTraversal = loop diveResults - ignore<'T option> (traverseUntil pick parseTree.Range.End visitor parseTree) + let ast = parsedInput.Contents + let m = (range0, ast) ||> List.fold (fun acc node -> unionRanges acc node.Range) + foldWhileImpl pickAll m.End folder state ast + + let tryPick chooser position (parsedInput: ParsedInput) = + let visitor = + { new SyntaxVisitorBase<'T>() with + member _.VisitExpr(path, _, defaultTraverse, expr) = + (match path with + | SyntaxNode.SynMemberDefn _ as parent :: parentPath -> chooser parentPath parent + | _ -> None) + |> Option.orElseWith (fun () -> chooser path (SyntaxNode.SynExpr expr)) + |> Option.orElseWith (fun () -> defaultTraverse expr) + + member _.VisitPat(path, defaultTraverse, pat) = + chooser path (SyntaxNode.SynPat pat) + |> Option.orElseWith (fun () -> defaultTraverse pat) + + member _.VisitType(path, defaultTraverse, synType) = + (match path with + | SyntaxNode.SynMemberDefn _ | SyntaxNode.SynMemberSig _ as parent :: parentPath -> chooser parentPath parent + | _ -> None) + |> Option.orElseWith (fun () -> chooser path (SyntaxNode.SynType synType)) + |> Option.orElseWith (fun () -> defaultTraverse synType) + + member _.VisitModuleDecl(path, defaultTraverse, synModuleDecl) = + chooser path (SyntaxNode.SynModule synModuleDecl) + |> Option.orElseWith (fun () -> + match synModuleDecl with + | SynModuleDecl.Types(types, _) -> + let path = SyntaxNode.SynModule synModuleDecl :: path + types |> List.tryPick (SyntaxNode.SynTypeDefn >> chooser path) + | _ -> None) + |> Option.orElseWith (fun () -> defaultTraverse synModuleDecl) + + member _.VisitModuleOrNamespace(path, synModuleOrNamespace) = + chooser path (SyntaxNode.SynModuleOrNamespace synModuleOrNamespace) + + member _.VisitMatchClause(path, defaultTraverse, matchClause) = + chooser path (SyntaxNode.SynMatchClause matchClause) + |> Option.orElseWith (fun () -> defaultTraverse matchClause) + + member _.VisitBinding(path, defaultTraverse, synBinding) = + (match path with + | SyntaxNode.SynMemberDefn _ as parent :: parentPath -> chooser parentPath parent + | _ -> None) + |> Option.orElseWith (fun () -> chooser path (SyntaxNode.SynBinding synBinding)) + |> Option.orElseWith (fun () -> defaultTraverse synBinding) + + member _.VisitModuleOrNamespaceSig(path, synModuleOrNamespaceSig) = + chooser path (SyntaxNode.SynModuleOrNamespaceSig synModuleOrNamespaceSig) + + member _.VisitModuleSigDecl(path, defaultTraverse, synModuleSigDecl) = + chooser path (SyntaxNode.SynModuleSigDecl synModuleSigDecl) + |> Option.orElseWith (fun () -> + match synModuleSigDecl with + | SynModuleSigDecl.Types(types, _) -> + let path = SyntaxNode.SynModuleSigDecl synModuleSigDecl :: path + types |> List.tryPick (SyntaxNode.SynTypeDefnSig >> chooser path) + | _ -> None) + |> Option.orElseWith (fun () -> defaultTraverse synModuleSigDecl) + + member _.VisitValSig(path, defaultTraverse, valSig) = + (match path with + | SyntaxNode.SynMemberSig _ as parent :: parentPath -> chooser parentPath parent + | _ -> None) + |> Option.orElseWith (fun () -> chooser path (SyntaxNode.SynValSig valSig)) + |> Option.orElseWith (fun () -> defaultTraverse valSig) + + member _.VisitSimplePats(path, _pat) = + match path with + | SyntaxNode.SynMemberDefn _ as node :: path -> chooser path node + | _ -> None - /// traverse an implementation file walking all the way down to SynExpr or TypeAbbrev at a particular location - /// - let Traverse (pos: pos, parseTree, visitor: SyntaxVisitorBase<'T>) = - traverseUntil pick pos visitor parseTree + member _.VisitInterfaceSynMemberDefnType(path, _synType) = + match path with + | SyntaxNode.SynMemberDefn _ as node :: path -> chooser path node + | _ -> None + } + + SyntaxTraversal.traverseUntil SyntaxTraversal.pick position visitor parsedInput.Contents + + let tryPickLast chooser position (parsedInput: ParsedInput) = + (None, parsedInput.Contents) + ||> foldWhileImpl SyntaxTraversal.pick position (fun prev path node -> + match chooser path node with + | Some _ as next -> Some next + | None -> Some prev) + + let tryNode position (parsedInput: ParsedInput) = + let Matching = Some + + (None, parsedInput.Contents) + ||> foldWhileImpl SyntaxTraversal.pick position (fun _prev path node -> + if rangeContainsPos node.Range position then + Some(Matching(node, path)) + else + None) + + let exists predicate position parsedInput = + tryPick (fun path node -> if predicate path node then Some() else None) position parsedInput + |> Option.isSome diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fsi b/src/Compiler/Service/ServiceParseTreeWalk.fsi index ef118baf12e..86ca17380ee 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fsi +++ b/src/Compiler/Service/ServiceParseTreeWalk.fsi @@ -5,7 +5,7 @@ namespace FSharp.Compiler.Syntax open FSharp.Compiler.Syntax open FSharp.Compiler.Text -/// Used to track route during traversal of syntax using SyntaxTraversal.Traverse +/// Represents a major syntax node in the untyped abstract syntax tree. [] type SyntaxNode = | SynPat of SynPat @@ -23,6 +23,11 @@ type SyntaxNode = | SynTypeDefnSig of SynTypeDefnSig | SynMemberSig of SynMemberSig + /// The range of the syntax node, inclusive of its contents. + member Range: range + +/// Represents the set of ancestor nodes traversed before reaching +/// the current node in a traversal of the untyped abstract syntax tree. type SyntaxVisitorPath = SyntaxNode list [] @@ -152,8 +157,8 @@ type SyntaxVisitorBase<'T> = default VisitRecordField: path: SyntaxVisitorPath * copyOpt: SynExpr option * recordField: SynLongIdent option -> 'T option - abstract VisitSimplePats: path: SyntaxVisitorPath * synPats: SynSimplePat list -> 'T option - default VisitSimplePats: path: SyntaxVisitorPath * synPats: SynSimplePat list -> 'T option + abstract VisitSimplePats: path: SyntaxVisitorPath * pat: SynPat -> 'T option + default VisitSimplePats: path: SyntaxVisitorPath * pat: SynPat -> 'T option abstract VisitType: path: SyntaxVisitorPath * defaultTraverse: (SynType -> 'T option) * synType: SynType -> 'T option @@ -199,6 +204,157 @@ module public SyntaxTraversal = val internal pick: pos: pos -> outerRange: range -> debugObj: obj -> diveResults: (range * (unit -> 'a option)) list -> 'a option - val internal traverseAll: visitor: SyntaxVisitorBase<'T> -> parseTree: ParsedInput -> unit - val Traverse: pos: pos * parseTree: ParsedInput * visitor: SyntaxVisitorBase<'T> -> 'T option + +/// +/// Holds operations for working with s +/// in the untyped abstract syntax tree (AST). +/// +[] +[] +module SyntaxNode = + /// + /// Extracts the , if any, + /// from the given . + /// + val (|Attributes|): node: SyntaxNode -> SynAttributes + +/// +/// Holds operations for working with the +/// untyped abstract syntax tree (). +/// +[] +[] +module ParsedInput = + /// + /// Applies the given predicate to each node of the AST and its context (path) + /// down to a given position, returning true if a matching node is found, otherwise false. + /// Traversal is short-circuited if no matching node is found through the given position. + /// + /// The predicate to match each node against. + /// The position in the input file down to which to apply the function. + /// The AST to search. + /// True if a matching node is found, or false if no matching node is found. + /// + /// + /// let isInTypeDefn = + /// (pos, parsedInput) + /// ||> ParsedInput.exists (fun _path node -> + /// match node with + /// | SyntaxNode.SynTypeDefn _ -> true + /// | _ -> false) + /// + /// + val exists: + predicate: (SyntaxVisitorPath -> SyntaxNode -> bool) -> position: pos -> parsedInput: ParsedInput -> bool + + /// + /// Applies a function to each node of the AST and its context (path), + /// threading an accumulator through the computation. + /// + /// The function to use to update the state given each node and its context. + /// The initial state. + /// The AST to fold over. + /// The final state. + /// + /// + /// let unnecessaryParentheses = + /// (HashSet Range.comparer, parsedInput) ||> ParsedInput.fold (fun acc path node -> + /// match node with + /// | SyntaxNode.SynExpr (SynExpr.Paren (expr = inner; rightParenRange = Some _; range = range)) when + /// not (SynExpr.shouldBeParenthesizedInContext getLineString path inner) + /// -> + /// ignore (acc.Add range) + /// acc + /// + /// | SyntaxNode.SynPat (SynPat.Paren (inner, range)) when + /// not (SynPat.shouldBeParenthesizedInContext path inner) + /// -> + /// ignore (acc.Add range) + /// acc + /// + /// | _ -> acc) + /// + /// + val fold: + folder: ('State -> SyntaxVisitorPath -> SyntaxNode -> 'State) -> + state: 'State -> + parsedInput: ParsedInput -> + 'State + + /// + /// Applies a function to each node of the AST and its context (path) + /// until the folder returns None, threading an accumulator through the computation. + /// + /// The function to use to update the state given each node and its context, or to stop traversal by returning None. + /// The initial state. + /// The AST to fold over. + /// The final state. + val foldWhile: + folder: ('State -> SyntaxVisitorPath -> SyntaxNode -> 'State option) -> + state: 'State -> + parsedInput: ParsedInput -> + 'State + + /// + /// Dives to the deepest node that contains the given position, + /// returning the node and its path if found, or None if no + /// node contains the position. + /// + /// The position in the input file down to which to dive. + /// The AST to search. + /// The deepest node containing the given position, along with the path taken through the node's ancestors to find it. + val tryNode: position: pos -> parsedInput: ParsedInput -> (SyntaxNode * SyntaxVisitorPath) option + + /// + /// Applies the given function to each node of the AST and its context (path) + /// down to a given position, returning Some x for the first node + /// for which the function returns Some x for some value x, otherwise None. + /// Traversal is short-circuited if no matching node is found through the given position. + /// + /// The function to apply to each node and its context to derive an optional value. + /// The position in the input file down to which to apply the function. + /// The AST to search. + /// The first value for which the function returns Some, or None if no matching node is found. + /// + /// + /// let range = + /// (pos, parsedInput) ||> ParsedInput.tryPick (fun _path node -> + /// match node with + /// | SyntaxNode.SynExpr (SynExpr.InterpolatedString (range = range)) when + /// rangeContainsPos range pos + /// -> Some range + /// | _ -> None) + /// + /// + val tryPick: + chooser: (SyntaxVisitorPath -> SyntaxNode -> 'T option) -> + position: pos -> + parsedInput: ParsedInput -> + 'T option + + /// + /// Applies the given function to each node of the AST and its context (path) + /// down to a given position, returning Some x for the last (deepest) node + /// for which the function returns Some x for some value x, otherwise None. + /// Traversal is short-circuited if no matching node is found through the given position. + /// + /// The function to apply to each node and its context to derive an optional value. + /// The position in the input file down to which to apply the function. + /// The AST to search. + /// The last (deepest) value for which the function returns Some, or None if no matching node is found. + /// + /// + /// let range = + /// (pos, parsedInput) + /// ||> ParsedInput.tryPickLast (fun path node -> + /// match node, path with + /// | FuncIdent range -> Some range + /// | _ -> None) + /// + /// + val tryPickLast: + chooser: (SyntaxVisitorPath -> SyntaxNode -> 'T option) -> + position: pos -> + parsedInput: ParsedInput -> + 'T option diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index cfbe30deb8f..f1a8d3e9737 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -888,9 +888,8 @@ module ParsedInput = | None, Some binding -> walkBinding binding | Some getBinding, Some setBinding -> walkBinding getBinding |> Option.orElseWith (fun () -> walkBinding setBinding) - | SynMemberDefn.ImplicitCtor(attributes = Attributes attrs; ctorArgs = SynSimplePats.SimplePats(pats = simplePats)) -> - List.tryPick walkAttribute attrs - |> Option.orElseWith (fun () -> List.tryPick walkSimplePat simplePats) + | SynMemberDefn.ImplicitCtor(attributes = Attributes attrs; ctorArgs = pat) -> + List.tryPick walkAttribute attrs |> Option.orElseWith (fun _ -> walkPat pat) | SynMemberDefn.ImplicitInherit(t, e, _, _) -> walkType t |> Option.orElseWith (fun () -> walkExpr e) @@ -1577,25 +1576,36 @@ module ParsedInput = | [] when range.StartLine = pos.Line -> Some CompletionContext.Invalid | _ -> None - member _.VisitSimplePats(_, pats) = + member _.VisitSimplePats(_, pat) = // Lambdas and their patterns are processed above in VisitExpr, // so VisitSimplePats is only called for primary constructors - pats - |> List.tryPick (fun pat -> - match pat with - // type C (x| ) - | SynSimplePat.Id(range = range) when rangeContainsPos range pos -> Some CompletionContext.Invalid - | SynSimplePat.Typed(pat = SynSimplePat.Id(range = idRange); targetType = synType) -> - // type C (x|: int) -> - if rangeContainsPos idRange pos then - Some CompletionContext.Invalid - // type C (x: int|) -> - elif rangeContainsPos synType.Range pos then - Some CompletionContext.Type - else - None - | _ -> None) + let rec loop (pat: SynPat) = + if not (rangeContainsPos pat.Range pos) then + None + else + + match pat with + // type C (x{caret} ) + | SynPat.Named _ + | SynPat.Const(SynConst.Unit, _) -> Some CompletionContext.Invalid + + | SynPat.Attrib(pat, _, _) + | SynPat.Paren(pat, _) -> loop pat + + | SynPat.Tuple(_, pats, _, _) -> List.tryPick loop pats + + | SynPat.Typed(pat, synType, _) -> + // type C (x: int{caret}) -> + if rangeContainsPos synType.Range pos then + Some CompletionContext.Type + else + // type C (x{caret}: int) -> + loop pat + + | _ -> None + + loop pat member _.VisitPat(_, defaultTraverse, pat) = TryGetCompletionContextInPattern false pat None pos @@ -2085,9 +2095,9 @@ module ParsedInput = | SynMemberDefn.GetSetMember(getBinding, setBinding, _, _) -> Option.iter walkBinding getBinding Option.iter walkBinding setBinding - | SynMemberDefn.ImplicitCtor(attributes = Attributes attrs; ctorArgs = SynSimplePats.SimplePats(pats = simplePats)) -> + | SynMemberDefn.ImplicitCtor(attributes = Attributes attrs; ctorArgs = pat) -> List.iter walkAttribute attrs - List.iter walkSimplePat simplePats + walkPat pat | SynMemberDefn.ImplicitInherit(t, e, _, _) -> walkType t walkExpr e diff --git a/src/Compiler/Service/SynExpr.fs b/src/Compiler/Service/SynExpr.fs new file mode 100644 index 00000000000..41dbd4d4d18 --- /dev/null +++ b/src/Compiler/Service/SynExpr.fs @@ -0,0 +1,939 @@ +namespace FSharp.Compiler.Syntax + +open System +open FSharp.Compiler.SyntaxTrivia +open FSharp.Compiler.Text + +[] +[] +module SynExpr = + let (|Last|) = List.last + + /// Matches if the two values refer to the same object. + [] + let inline (|Is|_|) (inner1: 'a) (inner2: 'a) = + if obj.ReferenceEquals(inner1, inner2) then + ValueSome Is + else + ValueNone + + /// Represents a symbolic infix operator with the precedence of *, /, or %. + /// All instances of this type are considered equal. + [] + type MulDivMod = + | Mul + | Div + | Mod + + member _.CompareTo(_other: MulDivMod) = 0 + override this.Equals obj = this.CompareTo(unbox obj) = 0 + override _.GetHashCode() = 0 + + interface IComparable with + member this.CompareTo obj = this.CompareTo(unbox obj) + + /// Represents a symbolic infix operator with the precedence of + or -. + /// All instances of this type are considered equal. + [] + type AddSub = + | Add + | Sub + + member _.CompareTo(_other: AddSub) = 0 + override this.Equals obj = this.CompareTo(unbox obj) = 0 + override _.GetHashCode() = 0 + + interface IComparable with + member this.CompareTo obj = this.CompareTo(unbox obj) + + /// Holds a symbolic operator's original notation. + /// Equality is based on the contents of the string. + /// Comparison always returns 0. + [] + type OriginalNotation = + | OriginalNotation of string + + member _.CompareTo(_other: OriginalNotation) = 0 + + override this.Equals obj = + match this, obj with + | OriginalNotation this, (:? OriginalNotation as OriginalNotation other) -> String.Equals(this, other, StringComparison.Ordinal) + | _ -> false + + override this.GetHashCode() = + match this with + | OriginalNotation notation -> notation.GetHashCode() + + interface IComparable with + member this.CompareTo obj = this.CompareTo(unbox obj) + + /// Represents an expression's precedence. + /// Comparison is based only on the precedence case. + /// Equality considers the embedded original notation, if any. + /// + /// For example: + /// + /// compare (AddSub (Add, OriginalNotation "+")) (AddSub (Add, OriginalNotation "++")) = 0 + /// + /// but + /// + /// AddSub (Add, OriginalNotation "+") <> AddSub (Add, OriginalNotation "++") + type Precedence = + /// yield, yield!, return, return! + | Low + + /// <- + | Set + + /// := + | ColonEquals + + /// , + | Comma + + /// or, || + /// + /// Refers to the exact operators or and ||. + /// Instances with leading dots or question marks or trailing characters are parsed as Bar instead. + | BarBar of OriginalNotation + + /// &, && + /// + /// Refers to the exact operators & and &&. + /// Instances with leading dots or question marks or trailing characters are parsed as Amp instead. + | AmpAmp of OriginalNotation + + /// :>, :?> + | UpcastDowncast + + /// =…, |…, &…, $…, >…, <…, !=… + | Relational of OriginalNotation + + /// ^…, @… + | HatAt + + /// :: + | Cons + + /// :? + | TypeTest + + /// +…, -… + | AddSub of AddSub * OriginalNotation + + /// *…, /…, %… + | MulDivMod of MulDivMod * OriginalNotation + + /// **… + | Exp + + /// - x + | UnaryPrefix + + /// f x + | Apply + + /// -x, !… x, ~~… x + | High + + // x.y + | Dot + + /// Associativity/association. + type Assoc = + /// Non-associative or no association. + | Non + + /// Left-associative or left-hand association. + | Left + + /// Right-associative or right-hand association. + | Right + + module Assoc = + let ofPrecedence precedence = + match precedence with + | Low -> Non + | Set -> Non + | ColonEquals -> Right + | Comma -> Non + | BarBar _ -> Left + | AmpAmp _ -> Left + | UpcastDowncast -> Right + | Relational _ -> Left + | HatAt -> Right + | Cons -> Right + | TypeTest -> Non + | AddSub _ -> Left + | MulDivMod _ -> Left + | Exp -> Right + | UnaryPrefix -> Left + | Apply -> Left + | High -> Left + | Dot -> Left + + /// See atomicExprAfterType in pars.fsy. + [] + let (|AtomicExprAfterType|_|) expr = + match expr with + | SynExpr.Paren _ + | SynExpr.Quote _ + | SynExpr.Const _ + | SynExpr.Tuple(isStruct = true) + | SynExpr.Record _ + | SynExpr.AnonRecd _ + | SynExpr.InterpolatedString _ + | SynExpr.Null _ + | SynExpr.ArrayOrList(isArray = true) + | SynExpr.ArrayOrListComputed(isArray = true) -> ValueSome AtomicExprAfterType + | _ -> ValueNone + + /// Matches if the given expression represents a high-precedence + /// function application, e.g., + /// + /// f x + /// + /// (+) x y + [] + let (|HighPrecedenceApp|_|) expr = + match expr with + | SynExpr.App(isInfix = false; funcExpr = SynExpr.Ident _) + | SynExpr.App(isInfix = false; funcExpr = SynExpr.LongIdent _) + | SynExpr.App(isInfix = false; funcExpr = SynExpr.App(isInfix = false)) -> ValueSome HighPrecedenceApp + | _ -> ValueNone + + module FuncExpr = + /// Matches when the given funcExpr is a direct application + /// of a symbolic operator, e.g., -, _not_ (~-). + [] + let (|SymbolicOperator|_|) funcExpr = + match funcExpr with + | SynExpr.LongIdent(longDotId = SynLongIdent(trivia = trivia)) -> + let rec tryPick = + function + | [] -> ValueNone + | Some(IdentTrivia.OriginalNotation op) :: _ -> ValueSome op + | _ :: rest -> tryPick rest + + tryPick trivia + | _ -> ValueNone + + /// Matches when the given expression is a prefix operator application, e.g., + /// + /// -x + /// + /// ~~~x + [] + let (|PrefixApp|_|) expr : Precedence voption = + match expr with + | SynExpr.App(isInfix = false; funcExpr = funcExpr & FuncExpr.SymbolicOperator op; argExpr = argExpr) -> + if funcExpr.Range.IsAdjacentTo argExpr.Range then + ValueSome High + else + assert (op.Length > 0) + + match op[0] with + | '!' + | '~' -> ValueSome High + | _ -> ValueSome UnaryPrefix + + | SynExpr.AddressOf(expr = expr; opRange = opRange) -> + if opRange.IsAdjacentTo expr.Range then + ValueSome High + else + ValueSome UnaryPrefix + + | _ -> ValueNone + + /// Tries to parse the given original notation as a symbolic infix operator. + [] + let (|SymbolPrec|_|) (originalNotation: string) = + // Trim any leading dots or question marks from the given symbolic operator. + // Leading dots or question marks have no effect on operator precedence or associativity + // with the exception of &, &&, and ||. + let ignoredLeadingChars = ".?".AsSpan() + let trimmed = originalNotation.AsSpan().TrimStart ignoredLeadingChars + assert (trimmed.Length > 0) + + match trimmed[0], originalNotation with + | _, ":=" -> ValueSome ColonEquals + | _, ("||" | "or") -> ValueSome(BarBar(OriginalNotation originalNotation)) + | _, ("&" | "&&") -> ValueSome(AmpAmp(OriginalNotation originalNotation)) + | '|', _ + | '&', _ + | '<', _ + | '>', _ + | '=', _ + | '$', _ -> ValueSome(Relational(OriginalNotation originalNotation)) + | '!', _ when trimmed.Length > 1 && trimmed[1] = '=' -> ValueSome(Relational(OriginalNotation originalNotation)) + | '^', _ + | '@', _ -> ValueSome HatAt + | _, "::" -> ValueSome Cons + | '+', _ -> ValueSome(AddSub(Add, OriginalNotation originalNotation)) + | '-', _ -> ValueSome(AddSub(Sub, OriginalNotation originalNotation)) + | '/', _ -> ValueSome(MulDivMod(Div, OriginalNotation originalNotation)) + | '%', _ -> ValueSome(MulDivMod(Mod, OriginalNotation originalNotation)) + | '*', _ when trimmed.Length > 1 && trimmed[1] = '*' -> ValueSome Exp + | '*', _ -> ValueSome(MulDivMod(Mul, OriginalNotation originalNotation)) + | _ -> ValueNone + + [] + let (|Contains|_|) (c: char) (s: string) = + if s.IndexOf c >= 0 then ValueSome Contains else ValueNone + + /// Any expressions in which the removal of parens would + /// lead to something like the following that would be + /// confused by the parser with a type parameter application: + /// + /// xz + /// + /// xz + [] + let rec (|ConfusableWithTypeApp|_|) synExpr = + match synExpr with + | SynExpr.Paren(expr = ConfusableWithTypeApp) + | SynExpr.App(funcExpr = ConfusableWithTypeApp) + | SynExpr.App(isInfix = true; funcExpr = FuncExpr.SymbolicOperator(Contains '>'); argExpr = ConfusableWithTypeApp) -> + ValueSome ConfusableWithTypeApp + | SynExpr.App(isInfix = true; funcExpr = funcExpr & FuncExpr.SymbolicOperator(Contains '<'); argExpr = argExpr) when + argExpr.Range.IsAdjacentTo funcExpr.Range + -> + ValueSome ConfusableWithTypeApp + | SynExpr.Tuple(exprs = exprs) -> + let rec anyButLast = + function + | _ :: [] + | [] -> ValueNone + | ConfusableWithTypeApp :: _ -> ValueSome ConfusableWithTypeApp + | _ :: tail -> anyButLast tail + + anyButLast exprs + | _ -> ValueNone + + /// Matches when the expression represents the infix application of a symbolic operator. + /// + /// (x λ y) ρ z + /// + /// x λ (y ρ z) + [] + let (|InfixApp|_|) synExpr : struct (Precedence * Assoc) voption = + match synExpr with + | SynExpr.App(funcExpr = SynExpr.App(isInfix = true; funcExpr = FuncExpr.SymbolicOperator(SymbolPrec prec))) -> + ValueSome(prec, Right) + | SynExpr.App(isInfix = true; funcExpr = FuncExpr.SymbolicOperator(SymbolPrec prec)) -> ValueSome(prec, Left) + | SynExpr.Upcast _ + | SynExpr.Downcast _ -> ValueSome(UpcastDowncast, Left) + | SynExpr.TypeTest _ -> ValueSome(TypeTest, Left) + | _ -> ValueNone + + /// Returns the given expression's precedence and the side of the inner expression, + /// if applicable. + [] + let (|OuterBinaryExpr|_|) inner outer : struct (Precedence * Assoc) voption = + match outer with + | SynExpr.YieldOrReturn _ + | SynExpr.YieldOrReturnFrom _ -> ValueSome(Low, Right) + | SynExpr.Tuple(exprs = SynExpr.Paren(expr = Is inner) :: _) -> ValueSome(Comma, Left) + | SynExpr.Tuple _ -> ValueSome(Comma, Right) + | InfixApp(Cons, side) -> ValueSome(Cons, side) + | SynExpr.Assert _ + | SynExpr.Lazy _ + | SynExpr.InferredUpcast _ + | SynExpr.InferredDowncast _ -> ValueSome(Apply, Non) + | PrefixApp prec -> ValueSome(prec, Non) + | InfixApp(prec, side) -> ValueSome(prec, side) + | SynExpr.App(argExpr = SynExpr.ComputationExpr _) -> ValueSome(UnaryPrefix, Left) + | SynExpr.App(funcExpr = SynExpr.Paren(expr = SynExpr.App _)) -> ValueSome(Apply, Left) + | SynExpr.App _ -> ValueSome(Apply, Non) + | SynExpr.DotSet(targetExpr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Dot, Left) + | SynExpr.DotSet(rhsExpr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Set, Right) + | SynExpr.DotIndexedSet(objectExpr = SynExpr.Paren(expr = Is inner)) + | SynExpr.DotNamedIndexedPropertySet(targetExpr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Dot, Left) + | SynExpr.DotIndexedSet(valueExpr = SynExpr.Paren(expr = Is inner)) + | SynExpr.DotNamedIndexedPropertySet(rhsExpr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Set, Right) + | SynExpr.LongIdentSet(expr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Set, Right) + | SynExpr.Set _ -> ValueSome(Set, Non) + | SynExpr.DotGet _ -> ValueSome(Dot, Left) + | SynExpr.DotIndexedGet(objectExpr = SynExpr.Paren(expr = Is inner)) -> ValueSome(Dot, Left) + | _ -> ValueNone + + /// Matches a SynExpr.App nested in a sequence of dot-gets. + /// + /// x.M.N().O + [] + let (|NestedApp|_|) expr = + let rec loop = + function + | SynExpr.DotGet(expr = expr) + | SynExpr.DotIndexedGet(objectExpr = expr) -> loop expr + | SynExpr.App _ -> ValueSome NestedApp + | _ -> ValueNone + + loop expr + + /// Returns the given expression's precedence, if applicable. + [] + let (|InnerBinaryExpr|_|) expr : Precedence voption = + match expr with + | SynExpr.Tuple(isStruct = false) -> ValueSome Comma + | SynExpr.DotGet(expr = NestedApp) + | SynExpr.DotIndexedGet(objectExpr = NestedApp) -> ValueSome Apply + | SynExpr.DotGet _ + | SynExpr.DotIndexedGet _ -> ValueSome Dot + | PrefixApp prec -> ValueSome prec + | InfixApp(prec, _) -> ValueSome prec + | SynExpr.App _ + | SynExpr.Assert _ + | SynExpr.Lazy _ + | SynExpr.For _ + | SynExpr.ForEach _ + | SynExpr.While _ + | SynExpr.Do _ + | SynExpr.New _ + | SynExpr.InferredUpcast _ + | SynExpr.InferredDowncast _ -> ValueSome Apply + | SynExpr.DotIndexedSet _ + | SynExpr.DotNamedIndexedPropertySet _ + | SynExpr.DotSet _ -> ValueSome Set + | _ -> ValueNone + + module Dangling = + /// Returns the first matching nested right-hand target expression, if any. + let private dangling (target: SynExpr -> SynExpr option) = + let (|Target|_|) = target + + let rec loop expr = + match expr with + | Target expr -> ValueSome expr + | SynExpr.Tuple(isStruct = false; exprs = Last expr) + | SynExpr.App(argExpr = expr) + | SynExpr.IfThenElse(elseExpr = Some expr) + | SynExpr.IfThenElse(ifExpr = expr) + | SynExpr.Sequential(expr2 = expr) + | SynExpr.YieldOrReturn(expr = expr) + | SynExpr.YieldOrReturnFrom(expr = expr) + | SynExpr.Set(rhsExpr = expr) + | SynExpr.DotSet(rhsExpr = expr) + | SynExpr.DotNamedIndexedPropertySet(rhsExpr = expr) + | SynExpr.DotIndexedSet(valueExpr = expr) + | SynExpr.LongIdentSet(expr = expr) + | SynExpr.LetOrUse(body = expr) + | SynExpr.Lambda(body = expr) + | SynExpr.Match(clauses = Last(SynMatchClause(resultExpr = expr))) + | SynExpr.MatchLambda(matchClauses = Last(SynMatchClause(resultExpr = expr))) + | SynExpr.MatchBang(clauses = Last(SynMatchClause(resultExpr = expr))) + | SynExpr.TryWith(withCases = Last(SynMatchClause(resultExpr = expr))) + | SynExpr.TryFinally(finallyExpr = expr) + | SynExpr.Do(expr = expr) + | SynExpr.DoBang(expr = expr) -> loop expr + | _ -> ValueNone + + loop + + /// Matches a dangling if-then construct. + [] + let (|IfThen|_|) = + dangling (function + | SynExpr.IfThenElse _ as expr -> Some expr + | _ -> None) + + /// Matches a dangling sequential expression. + [] + let (|Sequential|_|) = + dangling (function + | SynExpr.Sequential _ as expr -> Some expr + | _ -> None) + + /// Matches a dangling try-with or try-finally construct. + [] + let (|Try|_|) = + dangling (function + | SynExpr.TryWith _ + | SynExpr.TryFinally _ as expr -> Some expr + | _ -> None) + + /// Matches a dangling match-like construct. + [] + let (|Match|_|) = + dangling (function + | SynExpr.Match _ + | SynExpr.MatchBang _ + | SynExpr.MatchLambda _ + | SynExpr.TryWith _ + | SynExpr.Lambda _ as expr -> Some expr + | _ -> None) + + /// Matches a nested dangling construct that could become problematic + /// if the surrounding parens were removed. + [] + let (|Problematic|_|) = + dangling (function + | SynExpr.Lambda _ + | SynExpr.MatchLambda _ + | SynExpr.Match _ + | SynExpr.MatchBang _ + | SynExpr.TryWith _ + | SynExpr.TryFinally _ + | SynExpr.IfThenElse _ + | SynExpr.Sequential _ + | SynExpr.LetOrUse _ + | SynExpr.Set _ + | SynExpr.LongIdentSet _ + | SynExpr.DotIndexedSet _ + | SynExpr.DotNamedIndexedPropertySet _ + | SynExpr.DotSet _ + | SynExpr.NamedIndexedPropertySet _ as expr -> Some expr + | _ -> None) + + /// Indicates whether the expression with the given range + /// includes indentation that would be invalid + /// in context if it were not wrapped in parentheses. + let containsSensitiveIndentation (getSourceLineStr: int -> string) outerOffsidesColumn (range: range) = + let startLine = range.StartLine + let endLine = range.EndLine + + if startLine = endLine then + range.StartColumn <= outerOffsidesColumn + else + let rec loop offsides lineNo startCol = + if lineNo <= endLine then + let line = getSourceLineStr lineNo + + match offsides with + | ValueNone -> + let i = line.AsSpan(startCol).IndexOfAnyExcept(' ', ')') + + if i >= 0 then + let newOffsides = i + startCol + + newOffsides <= outerOffsidesColumn + || loop (ValueSome newOffsides) (lineNo + 1) 0 + else + loop offsides (lineNo + 1) 0 + + | ValueSome offsidesCol -> + let i = line.AsSpan(0, min offsidesCol line.Length).IndexOfAnyExcept(' ', ')') + + if i >= 0 && i < offsidesCol then + let slice = line.AsSpan(i, min (offsidesCol - i) (line.Length - i)) + let j = slice.IndexOfAnyExcept("*/%-+:^@><=!|0$.?".AsSpan()) + + let lo = i + (if j >= 0 && slice[j] = ' ' then j else 0) + + lo < offsidesCol - 1 + || lo <= outerOffsidesColumn + || loop offsides (lineNo + 1) 0 + else + loop offsides (lineNo + 1) 0 + else + false + + loop ValueNone startLine range.StartColumn + + let rec shouldBeParenthesizedInContext (getSourceLineStr: int -> string) path expr : bool = + let shouldBeParenthesizedInContext = shouldBeParenthesizedInContext getSourceLineStr + let containsSensitiveIndentation = containsSensitiveIndentation getSourceLineStr + + // Matches if the given expression starts with a symbol, e.g., <@ … @>, $"…", @"…", +1, -1… + let (|StartsWithSymbol|_|) = + let (|TextStartsWith|) (m: range) = + let line = getSourceLineStr m.StartLine + line[m.StartColumn] + + let (|StartsWith|) (s: string) = s[0] + + function + | SynExpr.Quote _ + | SynExpr.InterpolatedString _ + | SynExpr.Const(SynConst.String(synStringKind = SynStringKind.Verbatim), _) + | SynExpr.Const(SynConst.Byte _, TextStartsWith '+') + | SynExpr.Const(SynConst.UInt16 _, TextStartsWith '+') + | SynExpr.Const(SynConst.UInt32 _, TextStartsWith '+') + | SynExpr.Const(SynConst.UInt64 _, TextStartsWith '+') + | SynExpr.Const(SynConst.UIntPtr _, TextStartsWith '+') + | SynExpr.Const(SynConst.SByte _, TextStartsWith('-' | '+')) + | SynExpr.Const(SynConst.Int16 _, TextStartsWith('-' | '+')) + | SynExpr.Const(SynConst.Int32 _, TextStartsWith('-' | '+')) + | SynExpr.Const(SynConst.Int64 _, TextStartsWith('-' | '+')) + | SynExpr.Const(SynConst.IntPtr _, TextStartsWith('-' | '+')) + | SynExpr.Const(SynConst.Decimal _, TextStartsWith('-' | '+')) + | SynExpr.Const(SynConst.Double _, TextStartsWith('-' | '+')) + | SynExpr.Const(SynConst.Single _, TextStartsWith('-' | '+')) + | SynExpr.Const(SynConst.Measure(_, TextStartsWith('-' | '+'), _, _), _) + | SynExpr.Const(SynConst.UserNum(StartsWith('-' | '+'), _), _) -> Some StartsWithSymbol + | _ -> None + + // Matches if the given expression is a numeric literal + // that it is safe to "dot into," e.g., 1l, 0b1, 1e10, 1d, 1.0… + let (|DotSafeNumericLiteral|_|) = + /// 1l, 1d, 0b1, 0x1, 0o1, 1e10… + let (|TextContainsLetter|_|) (m: range) = + let line = getSourceLineStr m.StartLine + let span = line.AsSpan(m.StartColumn, m.EndColumn - m.StartColumn) + + if span.LastIndexOfAnyInRange('A', 'z') >= 0 then + Some TextContainsLetter + else + None + + // 1.0… + let (|TextEndsWithNumber|_|) (m: range) = + let line = getSourceLineStr m.StartLine + let span = line.AsSpan(m.StartColumn, m.EndColumn - m.StartColumn) + + if Char.IsDigit span[span.Length - 1] then + Some TextEndsWithNumber + else + None + + function + | SynExpr.Const(SynConst.Byte _, _) + | SynExpr.Const(SynConst.UInt16 _, _) + | SynExpr.Const(SynConst.UInt32 _, _) + | SynExpr.Const(SynConst.UInt64 _, _) + | SynExpr.Const(SynConst.UIntPtr _, _) + | SynExpr.Const(SynConst.SByte _, _) + | SynExpr.Const(SynConst.Int16 _, _) + | SynExpr.Const(SynConst.Int32 _, TextContainsLetter) + | SynExpr.Const(SynConst.Int64 _, _) + | SynExpr.Const(SynConst.IntPtr _, _) + | SynExpr.Const(SynConst.Decimal _, _) + | SynExpr.Const(SynConst.Double _, (TextEndsWithNumber | TextContainsLetter)) + | SynExpr.Const(SynConst.Single _, _) + | SynExpr.Const(SynConst.Measure _, _) + | SynExpr.Const(SynConst.UserNum _, _) -> Some DotSafeNumericLiteral + | _ -> None + + match expr, path with + // Parens must stay around binary equals expressions in argument + // position lest they be interpreted as named argument assignments: + // + // o.M((x = y)) + // o.N((x = y), z) + | SynExpr.Paren(expr = InfixApp(Relational(OriginalNotation "="), _)), + SyntaxNode.SynExpr(SynExpr.App(funcExpr = SynExpr.LongIdent _)) :: _ + | InfixApp(Relational(OriginalNotation "="), _), + SyntaxNode.SynExpr(SynExpr.Paren _) :: SyntaxNode.SynExpr(SynExpr.App(funcExpr = SynExpr.LongIdent _)) :: _ + | InfixApp(Relational(OriginalNotation "="), _), + SyntaxNode.SynExpr(SynExpr.Tuple(isStruct = false)) :: SyntaxNode.SynExpr(SynExpr.Paren _) :: SyntaxNode.SynExpr(SynExpr.App( + funcExpr = SynExpr.LongIdent _)) :: _ -> true + + // Already parenthesized. + | _, SyntaxNode.SynExpr(SynExpr.Paren _) :: _ -> false + + // Parens must stay around indentation that would otherwise be invalid: + // + // let _ = (x + // +y) + | _, SyntaxNode.SynBinding(SynBinding(trivia = trivia)) :: _ when + containsSensitiveIndentation trivia.LeadingKeyword.Range.StartColumn expr.Range + -> + true + + // Parens must stay around indentation that would otherwise be invalid: + // + // return ( + // x + // ) + | _, SyntaxNode.SynExpr outer :: _ when containsSensitiveIndentation outer.Range.StartColumn expr.Range -> true + + // Check for nested matches, e.g., + // + // match … with … -> (…, match … with … -> … | … -> …) | … -> … + | _, SyntaxNode.SynMatchClause _ :: path -> shouldBeParenthesizedInContext path expr + + // We always need parens for trait calls, e.g., + // + // let inline f x = (^a : (static member Parse : string -> ^a) x) + | SynExpr.TraitCall _, _ -> true + + // Don't touch library-only stuff: + // + // (# "ldlen.multi 2 0" array : int #) + | SynExpr.LibraryOnlyILAssembly _, _ + | SynExpr.LibraryOnlyStaticOptimization _, _ + | SynExpr.LibraryOnlyUnionCaseFieldGet _, _ + | SynExpr.LibraryOnlyUnionCaseFieldSet _, _ -> true + + // Parens are otherwise never required for binding bodies or for top-level expressions, e.g., + // + // let x = (…) + // _.member X = (…) + // (printfn "Hello, world.") + | _, SyntaxNode.SynBinding _ :: _ + | _, SyntaxNode.SynModule _ :: _ -> false + + // Parens must be kept when there is a high-precedence function application + // before a prefix operator application before another expression that starts with a symbol, e.g., + // + // id -(-x) + // id -(-1y) + // id -($"") + // id -(@"") + // id -(<@ ValueNone @>) + // let (~+) _ = true in assert +($"{true}") + | (PrefixApp _ | StartsWithSymbol), + SyntaxNode.SynExpr(SynExpr.App _) :: SyntaxNode.SynExpr(HighPrecedenceApp | SynExpr.Assert _ | SynExpr.InferredUpcast _ | SynExpr.InferredDowncast _) :: _ -> + true + + // Parens are never required around suffixed or infixed numeric literals, e.g., + // + // (1l).ToString() + // (1uy).ToString() + // (0b1).ToString() + // (1e10).ToString() + // (1.0).ToString() + | DotSafeNumericLiteral, _ -> false + + // Parens are required around bare decimal ints or doubles ending + // in dots when being dotted into, e.g., + // + // (1).ToString() + // (1.).ToString() + | SynExpr.Const(constant = SynConst.Int32 _ | SynConst.Double _), SyntaxNode.SynExpr(SynExpr.DotGet _) :: _ -> true + + // Parens are required around join conditions: + // + // join … on (… = …) + | SynExpr.App _, SyntaxNode.SynExpr(SynExpr.App _) :: SyntaxNode.SynExpr(SynExpr.JoinIn _) :: _ -> true + + // Parens are not required around a few anointed expressions after inherit: + // + // inherit T(3) + // inherit T(null) + // inherit T("") + // … + | AtomicExprAfterType, SyntaxNode.SynMemberDefn(SynMemberDefn.ImplicitInherit _) :: _ -> false + + // Parens are otherwise required in inherit T(x), etc. + | _, SyntaxNode.SynMemberDefn(SynMemberDefn.ImplicitInherit _) :: _ -> true + + // We can't remove parens when they're required for fluent calls: + // + // x.M(y).N z + // x.M(y).[z] + // _.M(x) + // (f x)[z] + // (f(x))[z] + // x.M(y)[z] + | _, SyntaxNode.SynExpr(SynExpr.App _) :: SyntaxNode.SynExpr(SynExpr.DotGet _ | SynExpr.DotIndexedGet _ | SynExpr.DotLambda _) :: _ + | SynExpr.App _, SyntaxNode.SynExpr(SynExpr.App(argExpr = SynExpr.ArrayOrListComputed(isArray = false))) :: _ + | _, + SyntaxNode.SynExpr(SynExpr.App _) :: SyntaxNode.SynExpr(SynExpr.App(argExpr = SynExpr.ArrayOrListComputed(isArray = false))) :: _ -> + true + + // The :: operator is parsed differently from other symbolic infix operators, + // so we need to give it special treatment. + + // Outer right: + // + // (x) :: xs + // (x * y) :: zs + // … + | _, + SyntaxNode.SynExpr(SynExpr.Tuple(isStruct = false; exprs = [ SynExpr.Paren _; _ ])) :: (SyntaxNode.SynExpr(SynExpr.App( + isInfix = true)) :: _ as path) -> shouldBeParenthesizedInContext path expr + + // Outer left: + // + // x :: (xs) + // x :: (ys @ zs) + // … + | argExpr, + SyntaxNode.SynExpr(SynExpr.Tuple(isStruct = false; exprs = [ _; SynExpr.Paren _ ])) :: SyntaxNode.SynExpr(SynExpr.App( + isInfix = true) as outer) :: path -> + shouldBeParenthesizedInContext + (SyntaxNode.SynExpr(SynExpr.App(ExprAtomicFlag.NonAtomic, false, outer, argExpr, outer.Range)) + :: path) + expr + + // Ordinary nested expressions. + | inner, SyntaxNode.SynExpr outer :: outerPath -> + let dangling expr = + match expr with + | Dangling.Problematic subExpr -> + match outer with + | SynExpr.Tuple(exprs = exprs) -> not (obj.ReferenceEquals(subExpr, List.last exprs)) + | InfixApp(_, Left) -> true + | _ -> shouldBeParenthesizedInContext outerPath subExpr + + | _ -> false + + let problematic (exprRange: range) (delimiterRange: range) = + exprRange.EndLine = delimiterRange.EndLine + && exprRange.EndColumn < delimiterRange.StartColumn + + let anyProblematic matchOrTryRange clauses = + let rec loop = + function + | [] -> false + | SynMatchClause(trivia = trivia) :: clauses -> + trivia.BarRange |> Option.exists (problematic matchOrTryRange) + || trivia.ArrowRange |> Option.exists (problematic matchOrTryRange) + || loop clauses + + loop clauses + + match outer, inner with + | ConfusableWithTypeApp, _ -> true + + | SynExpr.IfThenElse _, Dangling.Sequential _ -> true + + | SynExpr.IfThenElse(trivia = trivia), Dangling.IfThen ifThenElse when + problematic ifThenElse.Range trivia.ThenKeyword + || trivia.ElseKeyword |> Option.exists (problematic ifThenElse.Range) + -> + true + + | SynExpr.TryFinally(trivia = trivia), Dangling.Try tryExpr when problematic tryExpr.Range trivia.FinallyKeyword -> true + + | SynExpr.Match(clauses = clauses; trivia = { WithKeyword = withKeyword }), Dangling.Match matchOrTry when + problematic matchOrTry.Range withKeyword + || anyProblematic matchOrTry.Range clauses + -> + true + + | SynExpr.MatchBang(clauses = clauses; trivia = { WithKeyword = withKeyword }), Dangling.Match matchOrTry when + problematic matchOrTry.Range withKeyword + || anyProblematic matchOrTry.Range clauses + -> + true + + | SynExpr.MatchLambda(matchClauses = clauses), Dangling.Match matchOrTry when anyProblematic matchOrTry.Range clauses -> true + + | SynExpr.TryWith(withCases = clauses; trivia = trivia), Dangling.Match matchOrTry when + problematic matchOrTry.Range trivia.WithKeyword + || anyProblematic matchOrTry.Range clauses + -> + true + + | SynExpr.Sequential(expr1 = SynExpr.Paren(expr = Is inner); expr2 = expr2), Dangling.Problematic _ when + problematic inner.Range expr2.Range + -> + true + + | SynExpr.InterpolatedString(contents = contents), (SynExpr.Tuple(isStruct = false) | Dangling.Problematic _) -> + contents + |> List.exists (function + | SynInterpolatedStringPart.FillExpr(qualifiers = Some _) -> true + | _ -> false) + + | SynExpr.Record(copyInfo = Some(SynExpr.Paren(expr = Is inner), _)), Dangling.Problematic _ + | SynExpr.AnonRecd(copyInfo = Some(SynExpr.Paren(expr = Is inner), _)), Dangling.Problematic _ -> true + + | SynExpr.Record(recordFields = recordFields), Dangling.Problematic _ -> + let rec loop recordFields = + match recordFields with + | [] -> false + | SynExprRecordField(expr = Some(SynExpr.Paren(expr = Is inner)); blockSeparator = Some _) :: SynExprRecordField( + fieldName = SynLongIdent(id = id :: _), _) :: _ -> problematic inner.Range id.idRange + | _ :: recordFields -> loop recordFields + + loop recordFields + + | SynExpr.AnonRecd(recordFields = recordFields), Dangling.Problematic _ -> + let rec loop recordFields = + match recordFields with + | [] -> false + | (_, Some _blockSeparator, SynExpr.Paren(expr = Is inner)) :: (SynLongIdent(id = id :: _), _, _) :: _ -> + problematic inner.Range id.idRange + | _ :: recordFields -> loop recordFields + + loop recordFields + + | SynExpr.Paren _, SynExpr.Typed _ + | SynExpr.Quote _, SynExpr.Typed _ + | SynExpr.AnonRecd _, SynExpr.Typed _ + | SynExpr.Record _, SynExpr.Typed _ + | SynExpr.While(doExpr = SynExpr.Paren(expr = Is inner)), SynExpr.Typed _ + | SynExpr.WhileBang(doExpr = SynExpr.Paren(expr = Is inner)), SynExpr.Typed _ + | SynExpr.For(doBody = Is inner), SynExpr.Typed _ + | SynExpr.ForEach(bodyExpr = Is inner), SynExpr.Typed _ + | SynExpr.Match _, SynExpr.Typed _ + | SynExpr.Do _, SynExpr.Typed _ + | SynExpr.LetOrUse(body = Is inner), SynExpr.Typed _ + | SynExpr.TryWith _, SynExpr.Typed _ + | SynExpr.TryFinally _, SynExpr.Typed _ -> false + | _, SynExpr.Typed _ -> true + + | OuterBinaryExpr inner (outerPrecedence, side), InnerBinaryExpr innerPrecedence -> + let ambiguous = + match compare outerPrecedence innerPrecedence with + | 0 -> + match side, Assoc.ofPrecedence innerPrecedence with + | Non, _ + | _, Non + | Left, Right -> true + | Right, Right + | Left, Left -> false + | Right, Left -> + outerPrecedence <> innerPrecedence + || match outerPrecedence, innerPrecedence with + | _, MulDivMod(Div, _) + | _, MulDivMod(Mod, _) + | _, AddSub(Sub, _) -> true + | Relational _, Relational _ -> true + | _ -> false + + | c -> c > 0 + + ambiguous || dangling inner + + | OuterBinaryExpr inner (_, Right), (SynExpr.Sequential _ | SynExpr.LetOrUse(trivia = { InKeyword = None })) -> true + | OuterBinaryExpr inner (_, Right), inner -> dangling inner + + // new T(expr) + | SynExpr.New _, AtomicExprAfterType -> false + | SynExpr.New _, _ -> true + + // { inherit T(expr); … } + | SynExpr.Record(baseInfo = Some(_, SynExpr.Paren(expr = Is inner), _, _, _)), AtomicExprAfterType -> false + | SynExpr.Record(baseInfo = Some(_, SynExpr.Paren(expr = Is inner), _, _, _)), _ -> true + + | _, SynExpr.Paren _ + | _, SynExpr.Quote _ + | _, SynExpr.Const _ + | _, SynExpr.Tuple(isStruct = true) + | _, SynExpr.AnonRecd _ + | _, SynExpr.ArrayOrList _ + | _, SynExpr.Record _ + | _, SynExpr.ObjExpr _ + | _, SynExpr.ArrayOrListComputed _ + | _, SynExpr.ComputationExpr _ + | _, SynExpr.TypeApp _ + | _, SynExpr.Ident _ + | _, SynExpr.LongIdent _ + | _, SynExpr.DotGet _ + | _, SynExpr.DotLambda _ + | _, SynExpr.DotIndexedGet _ + | _, SynExpr.Null _ + | _, SynExpr.InterpolatedString _ + + | SynExpr.Paren _, _ + | SynExpr.Quote _, _ + | SynExpr.Typed _, _ + | SynExpr.AnonRecd _, _ + | SynExpr.Record _, _ + | SynExpr.ObjExpr _, _ + | SynExpr.While _, _ + | SynExpr.WhileBang _, _ + | SynExpr.For _, _ + | SynExpr.ForEach _, _ + | SynExpr.Lambda _, _ + | SynExpr.MatchLambda _, _ + | SynExpr.Match _, _ + | SynExpr.MatchBang _, _ + | SynExpr.LetOrUse _, _ + | SynExpr.LetOrUseBang _, _ + | SynExpr.Sequential _, _ + | SynExpr.Do _, _ + | SynExpr.DoBang _, _ + | SynExpr.IfThenElse _, _ + | SynExpr.TryWith _, _ + | SynExpr.TryFinally _, _ + | SynExpr.ComputationExpr _, _ + | SynExpr.InterpolatedString _, _ -> false + + | _ -> true + + | _ -> true diff --git a/src/Compiler/Service/SynExpr.fsi b/src/Compiler/Service/SynExpr.fsi new file mode 100644 index 00000000000..9e74ce6c1e9 --- /dev/null +++ b/src/Compiler/Service/SynExpr.fsi @@ -0,0 +1,15 @@ +namespace FSharp.Compiler.Syntax + +[] +[] +module public SynExpr = + + /// + /// Returns true if the given expression should be parenthesized in the given context, otherwise false. + /// + /// A function for getting the text of a given source line. + /// The expression's ancestor nodes. + /// The expression to check. + /// True if the given expression should be parenthesized in the given context, otherwise false. + val shouldBeParenthesizedInContext: + getSourceLineStr: (int -> string) -> path: SyntaxVisitorPath -> expr: SynExpr -> bool diff --git a/src/Compiler/Service/SynPat.fs b/src/Compiler/Service/SynPat.fs new file mode 100644 index 00000000000..53212cf17dc --- /dev/null +++ b/src/Compiler/Service/SynPat.fs @@ -0,0 +1,253 @@ +namespace FSharp.Compiler.Syntax + +[] +[] +module SynPat = + let (|Last|) = List.last + + /// Matches if the two values refer to the same object. + [] + let inline (|Is|_|) (inner1: 'a) (inner2: 'a) = + if obj.ReferenceEquals(inner1, inner2) then + ValueSome Is + else + ValueNone + + let (|Ident|) (ident: Ident) = ident.idText + + /// Matches if any pattern in the given list is a SynPat.Typed. + [] + let (|AnyTyped|_|) pats = + if + pats + |> List.exists (function + | SynPat.Typed _ -> true + | _ -> false) + then + ValueSome AnyTyped + else + ValueNone + + /// Matches if any member in the given list is an inherit + /// or implementation of an interface with generic type args. + [] + let (|AnyGenericInheritOrInterfaceImpl|_|) members = + if + members + |> List.exists (function + | SynMemberDefn.ImplicitInherit(inheritType = SynType.App(typeArgs = _ :: _)) + | SynMemberDefn.ImplicitInherit(inheritType = SynType.LongIdentApp(typeArgs = _ :: _)) + | SynMemberDefn.Interface(interfaceType = SynType.App(typeArgs = _ :: _)) + | SynMemberDefn.Interface(interfaceType = SynType.LongIdentApp(typeArgs = _ :: _)) -> true + | _ -> false) + then + ValueSome AnyGenericInheritOrInterfaceImpl + else + ValueNone + + /// Matches the rightmost potentially dangling nested pattern. + let rec (|Rightmost|) pat = + match pat with + | SynPat.Or(rhsPat = Rightmost pat) + | SynPat.ListCons(rhsPat = Rightmost pat) + | SynPat.As(rhsPat = Rightmost pat) + | SynPat.Ands(pats = Last(Rightmost pat)) + | SynPat.Tuple(isStruct = false; elementPats = Last(Rightmost pat)) -> pat + | pat -> pat + + /// Matches if the given pattern is atomic. + [] + let (|Atomic|_|) pat = + match pat with + | SynPat.Named _ + | SynPat.Wild _ + | SynPat.Paren _ + | SynPat.Tuple(isStruct = true) + | SynPat.Record _ + | SynPat.ArrayOrList _ + | SynPat.Const _ + | SynPat.LongIdent(argPats = SynArgPats.Pats []) + | SynPat.Null _ + | SynPat.QuoteExpr _ -> ValueSome Atomic + | _ -> ValueNone + + let shouldBeParenthesizedInContext path pat : bool = + match pat, path with + // Parens are needed in: + // + // let (Pattern …) = … + // let (x: …, y…) = … + // let (x: …), (y: …) = … + // let! (x: …) = … + // and! (x: …) = … + // use! (x: …) = … + // _.member M(x: …) = … + // match … with (x: …) -> … + // match … with (x, y: …) -> … + // function (x: …) -> … + // fun (x, y, …) -> … + // fun (x: …) -> … + // fun (Pattern …) -> … + | SynPat.Typed _, SyntaxNode.SynPat(Rightmost(SynPat.Paren(Is pat, _))) :: SyntaxNode.SynMatchClause _ :: _ + | Rightmost(SynPat.Typed _), SyntaxNode.SynMatchClause _ :: _ + | SynPat.Typed _, SyntaxNode.SynExpr(SynExpr.LetOrUseBang _) :: _ + | SynPat.Typed _, SyntaxNode.SynPat(SynPat.Tuple(isStruct = false)) :: SyntaxNode.SynExpr(SynExpr.LetOrUseBang _) :: _ + | SynPat.Tuple(isStruct = false; elementPats = AnyTyped), SyntaxNode.SynExpr(SynExpr.LetOrUseBang _) :: _ + | SynPat.Typed _, SyntaxNode.SynPat(SynPat.Tuple(isStruct = false)) :: SyntaxNode.SynBinding _ :: _ + | SynPat.Tuple(isStruct = false; elementPats = AnyTyped), SyntaxNode.SynBinding _ :: _ + | SynPat.LongIdent(argPats = SynArgPats.Pats(_ :: _)), SyntaxNode.SynBinding _ :: _ + | SynPat.LongIdent(argPats = SynArgPats.Pats(_ :: _)), SyntaxNode.SynExpr(SynExpr.Lambda _) :: _ + | SynPat.Tuple(isStruct = false), SyntaxNode.SynExpr(SynExpr.Lambda(parsedData = Some _)) :: _ + | SynPat.Typed _, SyntaxNode.SynExpr(SynExpr.Lambda(parsedData = Some _)) :: _ -> true + + // () is parsed as this. + | SynPat.Const(SynConst.Unit, _), _ -> true + + // (()) is required when overriding a generic member + // where unit is the generic type argument: + // + // type C<'T> = abstract M : 'T -> unit + // let _ = { new C with override _.M (()) = () } + | SynPat.Paren(SynPat.Const(SynConst.Unit, _), _), + SyntaxNode.SynPat(SynPat.LongIdent _) :: SyntaxNode.SynBinding _ :: SyntaxNode.SynExpr(SynExpr.ObjExpr( + objType = SynType.App(typeArgs = _ :: _) | SynType.LongIdentApp(typeArgs = _ :: _))) :: _ + | SynPat.Paren(SynPat.Const(SynConst.Unit, _), _), + SyntaxNode.SynPat(SynPat.LongIdent _) :: SyntaxNode.SynBinding _ :: SyntaxNode.SynMemberDefn _ :: SyntaxNode.SynTypeDefn(SynTypeDefn( + typeRepr = SynTypeDefnRepr.ObjectModel(members = AnyGenericInheritOrInterfaceImpl))) :: _ -> true + + // Parens are required around the atomic argument of + // any additional `new` constructor that is not the last. + // + // type T … = + // new (x) = … + // new (x, y) = … + | Atomic, + SyntaxNode.SynPat(SynPat.LongIdent(longDotId = SynLongIdent(id = [ Ident "new" ]))) :: SyntaxNode.SynBinding _ :: SyntaxNode.SynMemberDefn _ :: SyntaxNode.SynTypeDefn(SynTypeDefn( + typeRepr = SynTypeDefnRepr.ObjectModel(members = members))) :: _ -> + let lastNew = + (ValueNone, members) + ||> List.fold (fun lastNew ``member`` -> + match ``member`` with + | SynMemberDefn.Member( + memberDefn = SynBinding(headPat = SynPat.LongIdent(longDotId = SynLongIdent(id = [ Ident "new" ])))) -> + ValueSome ``member`` + | _ -> lastNew) + + match lastNew with + | ValueSome(SynMemberDefn.Member( + memberDefn = SynBinding(headPat = SynPat.LongIdent(argPats = SynArgPats.Pats [ SynPat.Paren(Is pat, _) ])))) -> false + | _ -> true + + // Parens are otherwise never needed in these cases: + // + // let (x: …) = … + // for (…) in (…) do … + // let! (…) = … + // and! (…) = … + // use! (…) = … + // match … with (…) -> … + // function (…) -> … + // function (Pattern …) -> … + // fun (x) -> … + | _, SyntaxNode.SynBinding _ :: _ + | _, SyntaxNode.SynExpr(SynExpr.ForEach _) :: _ + | _, SyntaxNode.SynExpr(SynExpr.LetOrUseBang _) :: _ + | _, SyntaxNode.SynMatchClause _ :: _ + | Atomic, SyntaxNode.SynExpr(SynExpr.Lambda(parsedData = Some _)) :: _ -> false + + // Nested patterns. + | inner, SyntaxNode.SynPat outer :: _ -> + match outer, inner with + // (x :: xs) :: ys + // (x, xs) :: ys + | SynPat.ListCons(lhsPat = SynPat.Paren(pat = Is inner)), SynPat.ListCons _ + | SynPat.ListCons(lhsPat = SynPat.Paren(pat = Is inner)), SynPat.Tuple(isStruct = false) -> true + + // A as (B | C) + // A as (B & C) + // x as (y, z) + // xs as (y :: zs) + | SynPat.As(rhsPat = SynPat.Paren(pat = Is inner)), + (SynPat.Or _ | SynPat.Ands _ | SynPat.Tuple(isStruct = false) | SynPat.ListCons _) -> true + + // (A | B) :: xs + // (A & B) :: xs + // (x as y) :: xs + | SynPat.ListCons _, SynPat.Or _ + | SynPat.ListCons _, SynPat.Ands _ + | SynPat.ListCons _, SynPat.As _ -> true + + // Pattern (x = (…)) + | SynPat.LongIdent(argPats = SynArgPats.NamePatPairs _), _ -> false + + // Pattern (x : int) + // Pattern ([] x) + // Pattern (:? int) + // Pattern (A :: _) + // Pattern (A | B) + // Pattern (A & B) + // Pattern (A as B) + // Pattern (A, B) + // Pattern1 (Pattern2 (x = A)) + // Pattern1 (Pattern2 x y) + | SynPat.LongIdent _, SynPat.Typed _ + | SynPat.LongIdent _, SynPat.Attrib _ + | SynPat.LongIdent _, SynPat.IsInst _ + | SynPat.LongIdent _, SynPat.ListCons _ + | SynPat.LongIdent _, SynPat.Or _ + | SynPat.LongIdent _, SynPat.Ands _ + | SynPat.LongIdent _, SynPat.As _ + | SynPat.LongIdent _, SynPat.Tuple(isStruct = false) + | SynPat.LongIdent _, SynPat.LongIdent(argPats = SynArgPats.NamePatPairs _) + | SynPat.LongIdent _, SynPat.LongIdent(argPats = SynArgPats.Pats(_ :: _)) + + // A | (B as C) + // A & (B as C) + // A, (B as C) + | SynPat.Or _, SynPat.As _ + | SynPat.Ands _, SynPat.As _ + | SynPat.Tuple _, SynPat.As _ + + // x, (y, z) + // x & (y, z) + // (x, y) & z + | SynPat.Tuple _, SynPat.Tuple(isStruct = false) + | SynPat.Ands _, SynPat.Tuple(isStruct = false) + + // A, (B | C) + // A & (B | C) + | SynPat.Tuple _, SynPat.Or _ + | SynPat.Ands _, SynPat.Or _ -> true + + // (x : int) & y + // x & (y : int) & z + | SynPat.Ands(Last(SynPat.Paren(pat = Is inner)), _), SynPat.Typed _ -> false + | SynPat.Ands _, SynPat.Typed _ -> true + + | _, SynPat.Const _ + | _, SynPat.Wild _ + | _, SynPat.Named _ + | _, SynPat.Typed _ + | _, SynPat.LongIdent(argPats = SynArgPats.Pats []) + | _, SynPat.Tuple(isStruct = true) + | _, SynPat.Paren _ + | _, SynPat.ArrayOrList _ + | _, SynPat.Record _ + | _, SynPat.Null _ + | _, SynPat.OptionalVal _ + | _, SynPat.IsInst _ + | _, SynPat.QuoteExpr _ + + | SynPat.Or _, _ + | SynPat.ListCons _, _ + | SynPat.Ands _, _ + | SynPat.As _, _ + | SynPat.LongIdent _, _ + | SynPat.Tuple _, _ + | SynPat.Paren _, _ + | SynPat.ArrayOrList _, _ + | SynPat.Record _, _ -> false + + | _ -> true + + | _ -> true diff --git a/src/Compiler/Service/SynPat.fsi b/src/Compiler/Service/SynPat.fsi new file mode 100644 index 00000000000..77bcd9c600c --- /dev/null +++ b/src/Compiler/Service/SynPat.fsi @@ -0,0 +1,13 @@ +namespace FSharp.Compiler.Syntax + +[] +[] +module public SynPat = + + /// + /// Returns true if the given pattern should be parenthesized in the given context, otherwise false. + /// + /// The pattern's ancestor nodes. + /// The pattern to check. + /// True if the given pattern should be parenthesized in the given context, otherwise false. + val shouldBeParenthesizedInContext: path: SyntaxVisitorPath -> pat: SynPat -> bool diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs new file mode 100644 index 00000000000..9f3881ecfd2 --- /dev/null +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -0,0 +1,2106 @@ +namespace FSharp.Compiler.CodeAnalysis.TransparentCompiler + +open System +open System.Collections.Generic +open System.Runtime.CompilerServices +open System.Diagnostics +open System.IO + +open Internal.Utilities.Collections +open Internal.Utilities.Library + +open FSharp.Compiler +open FSharp.Compiler.AbstractIL.IL +open FSharp.Compiler.AbstractIL.ILBinaryReader +open FSharp.Compiler.BuildGraph +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CompilerConfig +open FSharp.Compiler.CompilerImports +open FSharp.Compiler.CompilerOptions +open FSharp.Compiler.CheckBasics +open FSharp.Compiler.DependencyManager +open FSharp.Compiler.Diagnostics +open FSharp.Compiler.DiagnosticsLogger +open FSharp.Compiler.IO +open FSharp.Compiler.ScriptClosure +open FSharp.Compiler.Symbols +open FSharp.Compiler.TcGlobals +open FSharp.Compiler.Text +open FSharp.Compiler.Text.Range +open FSharp.Compiler.Xml +open System.Threading.Tasks +open FSharp.Compiler.ParseAndCheckInputs +open FSharp.Compiler.GraphChecking +open FSharp.Compiler.Syntax +open FSharp.Compiler.CompilerDiagnostics +open FSharp.Compiler.NameResolution +open Internal.Utilities.Library.Extras +open FSharp.Compiler.TypedTree +open FSharp.Compiler.CheckDeclarations +open FSharp.Compiler.EditorServices +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CreateILModule +open FSharp.Compiler.TypedTreeOps +open System.Threading +open Internal.Utilities.Hashing + +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot + +/// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. +[] +type internal TcInfo = + { + tcState: TcState + tcEnvAtEndOfFile: TcEnv + + /// Disambiguation table for module names + moduleNamesDict: ModuleNamesDict + + topAttribs: TopAttribs option + + latestCcuSigForFile: ModuleOrNamespaceType option + + /// Accumulated diagnostics, last file first + tcDiagnosticsRev: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] list + + tcDependencyFiles: string list + + sigNameOpt: (string * QualifiedNameOfFile) option + + graphNode: NodeToTypeCheck option + + stateContainsNodes: Set + + sink: TcResultsSinkImpl list + } + + member x.TcDiagnostics = Array.concat (List.rev x.tcDiagnosticsRev) + +[] +type internal TcIntermediate = + { + finisher: Finisher + //tcEnvAtEndOfFile: TcEnv + + /// Disambiguation table for module names + moduleNamesDict: ModuleNamesDict + + /// Accumulated diagnostics, last file first + tcDiagnosticsRev: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] list + + tcDependencyFiles: string list + + sink: TcResultsSinkImpl + } + +/// Things we need to start parsing and checking files for a given project snapshot +type internal BootstrapInfo = + { + // Each instance gets an Id on creation, unfortunately partial type check results using different instances are not compatible + // So if this needs to be recreated for whatever reason then we need to re type check all files + Id: int + + AssemblyName: string + OutFile: string + TcConfig: TcConfig + TcImports: TcImports + TcGlobals: TcGlobals + InitialTcInfo: TcInfo + + // TODO: Figure out how these work and if they need to be added to the snapshot... + LoadedSources: (range * FSharpFileSnapshot) list + + // TODO: Might be a bit more complicated if we want to support adding files to the project via OtherOptions + // ExtraSourceFilesAfter: FSharpFileSnapshot list + + LoadClosure: LoadClosure option + LastFileName: string + } + +type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string + +[] +type internal DependencyGraphType = + /// A dependency graph for a single file - it will be missing files which this file does not depend on + | File + /// A dependency graph for a project - it will contain all files in the project + | Project + +[] +type internal Extensions = + [] + static member Key<'T when 'T :> IFileSnapshot>(fileSnapshots: 'T list, ?extraKeyFlag) = + + { new ICacheKey<_, _> with + member _.GetLabel() = + let lastFile = + fileSnapshots + |> List.tryLast + |> Option.map (fun f -> f.FileName |> shortPath) + |> Option.defaultValue "[no file]" + + $"%d{fileSnapshots.Length} files ending with {lastFile}" + + member _.GetKey() = + Md5Hasher.empty + |> Md5Hasher.addStrings (fileSnapshots |> Seq.map (fun f -> f.FileName)) + |> pair extraKeyFlag + + member _.GetVersion() = + Md5Hasher.empty + |> Md5Hasher.addBytes' (fileSnapshots |> Seq.map (fun f -> f.Version)) + |> Md5Hasher.toString + } + +[] +module private TypeCheckingGraphProcessing = + open FSharp.Compiler.GraphChecking.GraphProcessing + + // TODO Do we need to suppress some error logging if we + // TODO apply the same partial results multiple times? + // TODO Maybe we can enable logging only for the final fold + /// + /// Combine type-checking results of dependencies needed to type-check a 'higher' node in the graph + /// + /// Initial state + /// Direct dependencies of a node + /// Transitive dependencies of a node + /// A way to fold a single result into existing state + let private combineResults + (emptyState: TcInfo) + (deps: ProcessedNode> array) + (transitiveDeps: ProcessedNode> array) + (folder: TcInfo -> Finisher -> TcInfo) + : TcInfo = + match deps with + | [||] -> emptyState + | _ -> + // Instead of starting with empty state, + // reuse state produced by the dependency with the biggest number of transitive dependencies. + // This is to reduce the number of folds required to achieve the final state. + let biggestDependency = + let sizeMetric (node: ProcessedNode<_, _>) = node.Info.TransitiveDeps.Length + deps |> Array.maxBy sizeMetric + + let firstState = biggestDependency.Result |> fst + + // Find items not already included in the state. + let itemsPresent = + set + [| + yield! biggestDependency.Info.TransitiveDeps + yield biggestDependency.Info.Item + |] + + let resultsToAdd = + transitiveDeps + |> Array.filter (fun dep -> itemsPresent.Contains dep.Info.Item = false) + |> Array.distinctBy (fun dep -> dep.Info.Item) + |> Array.sortWith (fun a b -> + // We preserve the order in which items are folded to the state. + match a.Info.Item, b.Info.Item with + | NodeToTypeCheck.PhysicalFile aIdx, NodeToTypeCheck.PhysicalFile bIdx + | NodeToTypeCheck.ArtificialImplFile aIdx, NodeToTypeCheck.ArtificialImplFile bIdx -> aIdx.CompareTo bIdx + | NodeToTypeCheck.PhysicalFile _, NodeToTypeCheck.ArtificialImplFile _ -> -1 + | NodeToTypeCheck.ArtificialImplFile _, NodeToTypeCheck.PhysicalFile _ -> 1) + |> Array.map (fun dep -> dep.Result |> snd) + + // Fold results not already included and produce the final state + let state = Array.fold folder firstState resultsToAdd + state + + /// + /// Process a graph of items. + /// A version of 'GraphProcessing.processGraph' with a signature specific to type-checking. + /// + let processTypeCheckingGraph + (graph: Graph) + (work: NodeToTypeCheck -> TcInfo -> Async>) + (emptyState: TcInfo) + : Async<(int * PartialResult) list * TcInfo> = + async { + + let workWrapper + (getProcessedNode: + NodeToTypeCheck -> ProcessedNode>) + (node: NodeInfo) + : Async> = + async { + let folder (state: TcInfo) (Finisher(finisher = finisher)) : TcInfo = finisher state |> snd + let deps = node.Deps |> Array.except [| node.Item |] |> Array.map getProcessedNode + + let transitiveDeps = + node.TransitiveDeps + |> Array.except [| node.Item |] + |> Array.map getProcessedNode + + let inputState = combineResults emptyState deps transitiveDeps folder + + let! singleRes = work node.Item inputState + let state = folder inputState singleRes + return state, singleRes + } + + let! results = processGraphAsync graph workWrapper + + let finalFileResults, state = + (([], emptyState), + results + |> Array.choose (fun (item, res) -> + match item with + | NodeToTypeCheck.ArtificialImplFile _ -> None + | NodeToTypeCheck.PhysicalFile file -> Some(file, res))) + ||> Array.fold (fun (fileResults, state) (item, (_, Finisher(finisher = finisher))) -> + let fileResult, state = finisher state + (item, fileResult) :: fileResults, state) + + return finalFileResults, state + } + +type internal CompilerCaches(sizeFactor: int) = + + let sf = sizeFactor + + member _.SizeFactor = sf + + member val ParseFile = AsyncMemoize(keepStrongly = 50 * sf, keepWeakly = 20 * sf, name = "ParseFile") + + member val ParseAndCheckFileInProject = AsyncMemoize(sf, 2 * sf, name = "ParseAndCheckFileInProject") + + member val ParseAndCheckAllFilesInProject = AsyncMemoizeDisabled(sf, 2 * sf, name = "ParseAndCheckFullProject") + + member val ParseAndCheckProject = AsyncMemoize(sf, 2 * sf, name = "ParseAndCheckProject") + + member val FrameworkImports = AsyncMemoize(sf, 2 * sf, name = "FrameworkImports") + + member val BootstrapInfoStatic = AsyncMemoize(sf, 2 * sf, name = "BootstrapInfoStatic") + + member val BootstrapInfo = AsyncMemoize(sf, 2 * sf, name = "BootstrapInfo") + + member val TcLastFile = AsyncMemoizeDisabled(sf, 2 * sf, name = "TcLastFile") + + member val TcIntermediate = AsyncMemoize(20 * sf, 20 * sf, name = "TcIntermediate") + + member val DependencyGraph = AsyncMemoize(sf, 2 * sf, name = "DependencyGraph") + + member val ProjectExtras = AsyncMemoizeDisabled(sf, 2 * sf, name = "ProjectExtras") + + member val AssemblyData = AsyncMemoize(sf, 2 * sf, name = "AssemblyData") + + member val SemanticClassification = AsyncMemoize(sf, 2 * sf, name = "SemanticClassification") + + member val ItemKeyStore = AsyncMemoize(sf, 2 * sf, name = "ItemKeyStore") + + member this.Clear(projects: Set) = + let shouldClear project = projects |> Set.contains project + + this.ParseFile.Clear(fst >> shouldClear) + this.ParseAndCheckFileInProject.Clear(snd >> shouldClear) + this.ParseAndCheckProject.Clear(shouldClear) + this.BootstrapInfoStatic.Clear(shouldClear) + this.BootstrapInfo.Clear(shouldClear) + this.TcIntermediate.Clear(snd >> shouldClear) + this.AssemblyData.Clear(shouldClear) + this.SemanticClassification.Clear(snd >> shouldClear) + this.ItemKeyStore.Clear(snd >> shouldClear) + +type internal TransparentCompiler + ( + legacyReferenceResolver, + projectCacheSize, + keepAssemblyContents, + keepAllBackgroundResolutions, + tryGetMetadataSnapshot, + suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + parallelReferenceResolution, + captureIdentifiersWhenParsing, + getSource: (string -> Async) option, + useChangeNotifications, + useSyntaxTreeCache + ) as self = + + // Is having just one of these ok? + let lexResourceManager = Lexhelp.LexResourceManager() + + // Mutable so we can easily clear them by creating a new instance + let mutable caches = CompilerCaches(100) + + // TODO: do we need this? + //let maxTypeCheckingParallelism = max 1 (Environment.ProcessorCount / 2) + //let maxParallelismSemaphore = new SemaphoreSlim(maxTypeCheckingParallelism) + + // We currently share one global dependency provider for all scripts for the FSharpChecker. + // For projects, one is used per project. + // + // Sharing one for all scripts is necessary for good performance from GetProjectOptionsFromScript, + // which requires a dependency provider to process through the project options prior to working out + // if the cached incremental builder can be used for the project. + let dependencyProviderForScripts = new DependencyProvider() + + // Legacy events, they're used in tests... eventually they should go away + let beforeFileChecked = Event() + let fileParsed = Event() + let fileChecked = Event() + let projectChecked = Event() + + // use this to process not-yet-implemented tasks + let backgroundCompiler = + BackgroundCompiler( + legacyReferenceResolver, + projectCacheSize, + keepAssemblyContents, + keepAllBackgroundResolutions, + tryGetMetadataSnapshot, + suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + parallelReferenceResolution, + captureIdentifiersWhenParsing, + getSource, + useChangeNotifications, + useSyntaxTreeCache + ) + :> IBackgroundCompiler + + let ComputeFrameworkImports (tcConfig: TcConfig) frameworkDLLs nonFrameworkResolutions = + let frameworkDLLsKey = + frameworkDLLs + |> List.map (fun ar -> ar.resolvedPath) // The cache key. Just the minimal data. + |> List.sort // Sort to promote cache hits. + + // The data elements in this key are very important. There should be nothing else in the TcConfig that logically affects + // the import of a set of framework DLLs into F# CCUs. That is, the F# CCUs that result from a set of DLLs (including + // FSharp.Core.dll and mscorlib.dll) must be logically invariant of all the other compiler configuration parameters. + let key = + FrameworkImportsCacheKey( + frameworkDLLsKey, + tcConfig.primaryAssembly.Name, + tcConfig.GetTargetFrameworkDirectories(), + tcConfig.fsharpBinariesDir, + tcConfig.langVersion.SpecifiedVersion + ) + + caches.FrameworkImports.Get( + key, + node { + use _ = Activity.start "ComputeFrameworkImports" [] + let tcConfigP = TcConfigProvider.Constant tcConfig + + return! TcImports.BuildFrameworkTcImports(tcConfigP, frameworkDLLs, nonFrameworkResolutions) + } + ) + + // Link all the assemblies together and produce the input typecheck accumulator + let CombineImportedAssembliesTask + ( + assemblyName, + tcConfig: TcConfig, + tcConfigP, + tcGlobals, + frameworkTcImports, + nonFrameworkResolutions, + unresolvedReferences, + dependencyProvider, + loadClosureOpt: LoadClosure option, + basicDependencies, + importsInvalidatedByTypeProvider: Event + ) = + + node { + let diagnosticsLogger = + CompilationDiagnosticLogger("CombineImportedAssembliesTask", tcConfig.diagnosticsOptions) + + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) + + let! tcImports = + node { + try + let! tcImports = + TcImports.BuildNonFrameworkTcImports( + tcConfigP, + frameworkTcImports, + nonFrameworkResolutions, + unresolvedReferences, + dependencyProvider + ) +#if !NO_TYPEPROVIDERS + // TODO: review and handle the event + tcImports.GetCcusExcludingBase() + |> Seq.iter (fun ccu -> + // When a CCU reports an invalidation, merge them together and just report a + // general "imports invalidated". This triggers a rebuild. + // + // We are explicit about what the handler closure captures to help reason about the + // lifetime of captured objects, especially in case the type provider instance gets leaked + // or keeps itself alive mistakenly, e.g. via some global state in the type provider instance. + // + // The handler only captures + // 1. a weak reference to the importsInvalidated event. + // + // The IncrementalBuilder holds the strong reference the importsInvalidated event. + // + // In the invalidation handler we use a weak reference to allow the IncrementalBuilder to + // be collected if, for some reason, a TP instance is not disposed or not GC'd. + let capturedImportsInvalidated = WeakReference<_>(importsInvalidatedByTypeProvider) + + ccu.Deref.InvalidateEvent.Add(fun _ -> + match capturedImportsInvalidated.TryGetTarget() with + | true, tg -> tg.Trigger() + | _ -> ())) +#endif +#if NO_TYPEPROVIDERS + ignore importsInvalidatedByTypeProvider +#endif + return tcImports + with + | :? OperationCanceledException -> + // if it's been canceled then it shouldn't be needed anymore + return frameworkTcImports + | exn -> + Debug.Assert(false, sprintf "Could not BuildAllReferencedDllTcImports %A" exn) + diagnosticsLogger.Warning exn + return frameworkTcImports + } + + let tcInitial, openDecls0 = + GetInitialTcEnv(assemblyName, rangeStartup, tcConfig, tcImports, tcGlobals) + + let tcState = + GetInitialTcState(rangeStartup, assemblyName, tcConfig, tcGlobals, tcImports, tcInitial, openDecls0) + + let loadClosureErrors = + [ + match loadClosureOpt with + | None -> () + | Some loadClosure -> + for inp in loadClosure.Inputs do + yield! inp.MetaCommandDiagnostics + ] + + let initialErrors = + Array.append (Array.ofList loadClosureErrors) (diagnosticsLogger.GetDiagnostics()) + + let tcInfo = + { + tcState = tcState + tcEnvAtEndOfFile = tcInitial + topAttribs = None + latestCcuSigForFile = None + tcDiagnosticsRev = [ initialErrors ] + moduleNamesDict = Map.empty + tcDependencyFiles = basicDependencies + sigNameOpt = None + graphNode = None + stateContainsNodes = Set.empty + sink = [] + } + + return tcImports, tcInfo + } + + let getProjectReferences (project: ProjectSnapshotBase<_>) userOpName = + [ + for r in project.ReferencedProjects do + + match r with + | FSharpReferencedProjectSnapshot.FSharpReference(nm, projectSnapshot) -> + // Don't use cross-project references for FSharp.Core, since various bits of code + // require a concrete FSharp.Core to exist on-disk. The only solutions that have + // these cross-project references to FSharp.Core are VisualFSharp.sln and FSharp.sln. The ramification + // of this is that you need to build FSharp.Core to get intellisense in those projects. + + if + (try + Path.GetFileNameWithoutExtension(nm) + with _ -> + "") + <> GetFSharpCoreLibraryName() + then + { new IProjectReference with + member x.EvaluateRawContents() = + node { + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) + + return! + self.GetAssemblyData( + projectSnapshot.ProjectSnapshot, + nm, + userOpName + ".CheckReferencedProject(" + nm + ")" + ) + } + + member x.TryGetLogicalTimeStamp(cache) = + // TODO: + None + + member x.FileName = nm + } + | FSharpReferencedProjectSnapshot.PEReference(getStamp, delayedReader) -> + { new IProjectReference with + member x.EvaluateRawContents() = + node { + let! ilReaderOpt = delayedReader.TryGetILModuleReader() |> NodeCode.FromCancellable + + match ilReaderOpt with + | Some ilReader -> + let ilModuleDef, ilAsmRefs = ilReader.ILModuleDef, ilReader.ILAssemblyRefs + let data = RawFSharpAssemblyData(ilModuleDef, ilAsmRefs) :> IRawFSharpAssemblyData + return ProjectAssemblyDataResult.Available data + | _ -> + // Note 'false' - if a PEReference doesn't find an ILModuleReader then we don't + // continue to try to use an on-disk DLL + return ProjectAssemblyDataResult.Unavailable false + } + + member x.TryGetLogicalTimeStamp _ = getStamp () |> Some + member x.FileName = delayedReader.OutputFile + } + + | FSharpReferencedProjectSnapshot.ILModuleReference(nm, getStamp, getReader) -> + { new IProjectReference with + member x.EvaluateRawContents() = + cancellable { + let ilReader = getReader () + let ilModuleDef, ilAsmRefs = ilReader.ILModuleDef, ilReader.ILAssemblyRefs + let data = RawFSharpAssemblyData(ilModuleDef, ilAsmRefs) :> IRawFSharpAssemblyData + return ProjectAssemblyDataResult.Available data + } + |> NodeCode.FromCancellable + + member x.TryGetLogicalTimeStamp _ = getStamp () |> Some + member x.FileName = nm + } + ] + + let ComputeTcConfigBuilder (projectSnapshot: ProjectSnapshotBase<_>) = + + let useSimpleResolutionSwitch = "--simpleresolution" + let commandLineArgs = projectSnapshot.CommandLineOptions + let defaultFSharpBinariesDir = FSharpCheckerResultsSettings.defaultFSharpBinariesDir + let useScriptResolutionRules = projectSnapshot.UseScriptResolutionRules + + let projectReferences = + getProjectReferences projectSnapshot "ComputeTcConfigBuilder" + + // TODO: script support + let loadClosureOpt: LoadClosure option = None + + let getSwitchValue (switchString: string) = + match commandLineArgs |> List.tryFindIndex (fun s -> s.StartsWithOrdinal switchString) with + | Some idx -> Some(commandLineArgs[idx].Substring(switchString.Length)) + | _ -> None + + let sdkDirOverride = + match loadClosureOpt with + | None -> None + | Some loadClosure -> loadClosure.SdkDirOverride + + // see also fsc.fs: runFromCommandLineToImportingAssemblies(), as there are many similarities to where the PS creates a tcConfigB + let tcConfigB = + TcConfigBuilder.CreateNew( + legacyReferenceResolver, + defaultFSharpBinariesDir, + implicitIncludeDir = projectSnapshot.ProjectDirectory, + reduceMemoryUsage = ReduceMemoryFlag.Yes, + isInteractive = useScriptResolutionRules, + isInvalidationSupported = true, + defaultCopyFSharpCore = CopyFSharpCoreFlag.No, + tryGetMetadataSnapshot = tryGetMetadataSnapshot, + sdkDirOverride = sdkDirOverride, + rangeForErrors = range0 + ) + + tcConfigB.primaryAssembly <- + match loadClosureOpt with + | None -> PrimaryAssembly.Mscorlib + | Some loadClosure -> + if loadClosure.UseDesktopFramework then + PrimaryAssembly.Mscorlib + else + PrimaryAssembly.System_Runtime + + tcConfigB.resolutionEnvironment <- (LegacyResolutionEnvironment.EditingOrCompilation true) + + tcConfigB.conditionalDefines <- + let define = + if useScriptResolutionRules then + "INTERACTIVE" + else + "COMPILED" + + define :: tcConfigB.conditionalDefines + + tcConfigB.projectReferences <- projectReferences + + tcConfigB.useSimpleResolution <- (getSwitchValue useSimpleResolutionSwitch) |> Option.isSome + + // Apply command-line arguments and collect more source files if they are in the arguments + let sourceFilesNew = + ApplyCommandLineArgs(tcConfigB, projectSnapshot.SourceFileNames, commandLineArgs) + + // Never open PDB files for the language service, even if --standalone is specified + tcConfigB.openDebugInformationForLaterStaticLinking <- false + + tcConfigB.xmlDocInfoLoader <- + { new IXmlDocumentationInfoLoader with + /// Try to load xml documentation associated with an assembly by the same file path with the extension ".xml". + member _.TryLoad(assemblyFileName) = + let xmlFileName = Path.ChangeExtension(assemblyFileName, ".xml") + + // REVIEW: File IO - Will eventually need to change this to use a file system interface of some sort. + XmlDocumentationInfo.TryCreateFromFile(xmlFileName) + } + |> Some + + tcConfigB.parallelReferenceResolution <- parallelReferenceResolution + tcConfigB.captureIdentifiersWhenParsing <- captureIdentifiersWhenParsing + + tcConfigB, sourceFilesNew, loadClosureOpt + + let mutable BootstrapInfoIdCounter = 0 + + /// Bootstrap info that does not depend source files + let ComputeBootstrapInfoStatic (projectSnapshot: ProjectCore, tcConfig: TcConfig, assemblyName: string, loadClosureOpt) = + + caches.BootstrapInfoStatic.Get( + projectSnapshot.CacheKeyWith("BootstrapInfoStatic", assemblyName), + node { + use _ = + Activity.start + "ComputeBootstrapInfoStatic" + [| + Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName + "references", projectSnapshot.ReferencedProjects.Length.ToString() + |] + + // Resolve assemblies and create the framework TcImports. This caches a level of "system" references. No type providers are + // included in these references. + + let frameworkDLLs, nonFrameworkResolutions, unresolvedReferences = + TcAssemblyResolutions.SplitNonFoundationalResolutions(tcConfig) + + // Prepare the frameworkTcImportsCache + let! tcGlobals, frameworkTcImports = ComputeFrameworkImports tcConfig frameworkDLLs nonFrameworkResolutions + + // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. + // This is ok because not much can actually go wrong here. + let diagnosticsLogger = + CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) + + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) + + let tcConfigP = TcConfigProvider.Constant tcConfig + + let importsInvalidatedByTypeProvider = Event() + + let basicDependencies = + [ + for UnresolvedAssemblyReference(referenceText, _) in unresolvedReferences do + // Exclude things that are definitely not a file name + if not (FileSystem.IsInvalidPathShim referenceText) then + let file = + if FileSystem.IsPathRootedShim referenceText then + referenceText + else + Path.Combine(projectSnapshot.ProjectDirectory, referenceText) + + yield file + + for r in nonFrameworkResolutions do + yield r.resolvedPath + ] + + // For scripts, the dependency provider is already available. + // For projects create a fresh one for the project. + let dependencyProvider = + if projectSnapshot.UseScriptResolutionRules then + dependencyProviderForScripts + else + new DependencyProvider() + + let! tcImports, initialTcInfo = + CombineImportedAssembliesTask( + assemblyName, + tcConfig, + tcConfigP, + tcGlobals, + frameworkTcImports, + nonFrameworkResolutions, + unresolvedReferences, + dependencyProvider, + loadClosureOpt, + basicDependencies, + importsInvalidatedByTypeProvider + ) + + let bootstrapId = Interlocked.Increment &BootstrapInfoIdCounter + + return bootstrapId, tcImports, tcGlobals, initialTcInfo, importsInvalidatedByTypeProvider + } + ) + + let computeBootstrapInfoInner (projectSnapshot: ProjectSnapshot) = + node { + + let tcConfigB, sourceFiles, loadClosureOpt = ComputeTcConfigBuilder projectSnapshot + + // If this is a builder for a script, re-apply the settings inferred from the + // script and its load closure to the configuration. + // + // NOTE: it would probably be cleaner and more accurate to re-run the load closure at this point. + let setupConfigFromLoadClosure () = + match loadClosureOpt with + | Some loadClosure -> + let dllReferences = + [ + for reference in tcConfigB.referencedDLLs do + // If there's (one or more) resolutions of closure references then yield them all + match + loadClosure.References + |> List.tryFind (fun (resolved, _) -> resolved = reference.Text) + with + | Some(resolved, closureReferences) -> + for closureReference in closureReferences do + yield AssemblyReference(closureReference.originalReference.Range, resolved, None) + | None -> yield reference + ] + + tcConfigB.referencedDLLs <- [] + + tcConfigB.primaryAssembly <- + (if loadClosure.UseDesktopFramework then + PrimaryAssembly.Mscorlib + else + PrimaryAssembly.System_Runtime) + // Add one by one to remove duplicates + dllReferences + |> List.iter (fun dllReference -> tcConfigB.AddReferencedAssemblyByPath(dllReference.Range, dllReference.Text)) + + tcConfigB.knownUnresolvedReferences <- loadClosure.UnresolvedReferences + | None -> () + + setupConfigFromLoadClosure () + + let tcConfig = TcConfig.Create(tcConfigB, validate = true) + let outFile, _, assemblyName = tcConfigB.DecideNames sourceFiles + + let! bootstrapId, tcImports, tcGlobals, initialTcInfo, _importsInvalidatedByTypeProvider = + ComputeBootstrapInfoStatic(projectSnapshot.ProjectCore, tcConfig, assemblyName, loadClosureOpt) + + // Check for the existence of loaded sources and prepend them to the sources list if present. + let loadedSources = + tcConfig.GetAvailableLoadedSources() + |> List.map (fun (m, fileName) -> m, FSharpFileSnapshot.CreateFromFileSystem(fileName)) + + return + match sourceFiles with + | [] -> None + | _ -> + Some + { + Id = bootstrapId + AssemblyName = assemblyName + OutFile = outFile + TcConfig = tcConfig + TcImports = tcImports + TcGlobals = tcGlobals + InitialTcInfo = initialTcInfo + LoadedSources = loadedSources + LoadClosure = loadClosureOpt + LastFileName = sourceFiles |> List.tryLast |> Option.defaultValue "" + //ImportsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider + } + } + + let ComputeBootstrapInfo (projectSnapshot: ProjectSnapshot) = + + caches.BootstrapInfo.Get( + projectSnapshot.NoFileVersionsKey, + node { + use _ = + Activity.start "ComputeBootstrapInfo" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] + + // Trap and report diagnostics from creation. + let delayedLogger = CapturingDiagnosticsLogger("IncrementalBuilderCreation") + use _ = new CompilationGlobalsScope(delayedLogger, BuildPhase.Parameter) + + let! bootstrapInfoOpt = + node { + try + return! computeBootstrapInfoInner projectSnapshot + with exn -> + errorRecoveryNoRange exn + return None + } + + let diagnostics = + match bootstrapInfoOpt with + | Some bootstrapInfo -> + let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions + + let diagnosticsLogger = + CompilationDiagnosticLogger("IncrementalBuilderCreation", diagnosticsOptions) + + delayedLogger.CommitDelayedDiagnostics diagnosticsLogger + diagnosticsLogger.GetDiagnostics() + | _ -> Array.ofList delayedLogger.Diagnostics + |> Array.map (fun (diagnostic, severity) -> + let flatErrors = + bootstrapInfoOpt + |> Option.map (fun bootstrapInfo -> bootstrapInfo.TcConfig.flatErrors) + |> Option.defaultValue false // TODO: do we need to figure this out? + + FSharpDiagnostic.CreateFromException(diagnostic, severity, range.Zero, suggestNamesForErrors, flatErrors, None)) + + return bootstrapInfoOpt, diagnostics + } + ) + + // TODO: Not sure if we should cache this. For VS probably not. Maybe it can be configurable by FCS user. + let LoadSource (file: FSharpFileSnapshot) isExe isLastCompiland = + node { + let! source = file.GetSource() |> NodeCode.AwaitTask + + return + FSharpFileSnapshotWithSource( + FileName = file.FileName, + Source = source, + SourceHash = source.GetChecksum(), + IsLastCompiland = isLastCompiland, + IsExe = isExe + ) + } + + let LoadSources (bootstrapInfo: BootstrapInfo) (projectSnapshot: ProjectSnapshot) = + node { + let isExe = bootstrapInfo.TcConfig.target.IsExe + + let! sources = + projectSnapshot.SourceFiles + |> Seq.map (fun f -> LoadSource f isExe (f.FileName = bootstrapInfo.LastFileName)) + |> NodeCode.Parallel + + return ProjectSnapshotWithSources(projectSnapshot.ProjectCore, sources |> Array.toList) + + } + + let ComputeParseFile (projectSnapshot: ProjectSnapshotBase<_>) (tcConfig: TcConfig) (file: FSharpFileSnapshotWithSource) = + + let key = + { new ICacheKey<_, _> with + member _.GetLabel() = file.FileName |> shortPath + + member _.GetKey() = + projectSnapshot.ProjectCore.Identifier, file.FileName + + member _.GetVersion() = + projectSnapshot.ParsingVersion, + file.StringVersion, + // TODO: is there a situation where this is not enough and we need to have them separate? + file.IsLastCompiland && file.IsExe + } + + caches.ParseFile.Get( + key, + node { + use _ = + Activity.start + "ComputeParseFile" + [| + Activity.Tags.fileName, file.FileName |> shortPath + Activity.Tags.version, file.StringVersion + |] + + let diagnosticsLogger = + CompilationDiagnosticLogger("Parse", tcConfig.diagnosticsOptions) + // Return the disposable object that cleans up + use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parse) + + let flags = file.IsLastCompiland, file.IsExe + let fileName = file.FileName + let sourceText = file.Source + + let input = + ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) + + // TODO: Hashing of syntax tree + let inputHash = file.Version + + fileParsed.Trigger(fileName, Unchecked.defaultof<_>) + + return FSharpParsedFile(fileName, inputHash, sourceText, input, diagnosticsLogger.GetDiagnostics()) + } + ) + + // In case we don't want to use any parallel processing + let mkLinearGraph count : Graph = + seq { + 0, [||] + + yield! + [ 0 .. count - 1 ] + |> Seq.rev + |> Seq.pairwise + |> Seq.map (fun (a, b) -> a, [| b |]) + } + |> Graph.make + + let computeDependencyGraph (tcConfig: TcConfig) parsedInputs (processGraph: Graph -> Graph) = + node { + let sourceFiles: FileInProject array = + parsedInputs + |> Seq.toArray + |> Array.mapi (fun idx (input: ParsedInput) -> + { + Idx = idx + FileName = input.FileName + ParsedInput = input + }) + + use _ = + Activity.start "ComputeDependencyGraph" [| Activity.Tags.fileName, (sourceFiles |> Array.last).FileName |] + + let filePairs = FilePairMap(sourceFiles) + + // TODO: we will probably want to cache and re-use larger graphs if available + + let graph = + if tcConfig.compilingFSharpCore then + mkLinearGraph sourceFiles.Length + else + DependencyResolution.mkGraph filePairs sourceFiles |> fst |> processGraph + + let nodeGraph = TransformDependencyGraph(graph, filePairs) + + let fileNames = + parsedInputs + |> Seq.mapi (fun idx input -> idx, Path.GetFileName input.FileName) + |> Map.ofSeq + + let debugGraph = + nodeGraph + |> Graph.map (function + | NodeToTypeCheck.PhysicalFile i -> i, $"[{i}] {fileNames[i]}" + | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF [{i}] : {fileNames[i]}") + |> Graph.serialiseToMermaid + + //Trace.TraceInformation("\n" + debugGraph) + + if Activity.Current <> null then + Activity.Current.AddTag("graph", debugGraph) |> ignore + + return nodeGraph, graph + } + + let removeImplFilesThatHaveSignatures (projectSnapshot: ProjectSnapshot) (graph: Graph) = + + let removeIndexes = + projectSnapshot.SourceFileNames + |> Seq.mapi pair + |> Seq.groupBy ( + snd + >> (fun fileName -> + if fileName.EndsWith(".fsi") then + fileName.Substring(0, fileName.Length - 1) + else + fileName) + ) + |> Seq.map (snd >> (Seq.toList)) + |> Seq.choose (function + | [ idx1, _; idx2, _ ] -> max idx1 idx2 |> Some + | _ -> None) + |> Set + + graph + |> Seq.filter (fun x -> not (removeIndexes.Contains x.Key)) + |> Seq.map (fun x -> x.Key, x.Value |> Array.filter (fun node -> not (removeIndexes.Contains node))) + |> Graph.make + + let removeImplFilesThatHaveSignaturesExceptLastOne (projectSnapshot: ProjectSnapshotBase<_>) (graph: Graph) = + + let removeIndexes = + projectSnapshot.SourceFileNames + |> Seq.mapi pair + |> Seq.groupBy ( + snd + >> (fun fileName -> + if fileName.EndsWith(".fsi") then + fileName.Substring(0, fileName.Length - 1) + else + fileName) + ) + |> Seq.map (snd >> (Seq.toList)) + |> Seq.choose (function + | [ idx1, _; idx2, _ ] -> max idx1 idx2 |> Some + | _ -> None) + |> Set + // Don't remove the last file + |> Set.remove (projectSnapshot.SourceFiles.Length - 1) + + graph + |> Seq.filter (fun x -> not (removeIndexes.Contains x.Key)) + |> Seq.map (fun x -> x.Key, x.Value |> Array.filter (fun node -> not (removeIndexes.Contains node))) + |> Graph.make + + let ComputeDependencyGraphForFile (tcConfig: TcConfig) (priorSnapshot: ProjectSnapshotBase) = + let key = priorSnapshot.SourceFiles.Key(DependencyGraphType.File) + //let lastFileIndex = (parsedInputs |> Array.length) - 1 + //caches.DependencyGraph.Get(key, computeDependencyGraph parsedInputs (Graph.subGraphFor lastFileIndex)) + caches.DependencyGraph.Get( + key, + computeDependencyGraph + tcConfig + (priorSnapshot.SourceFiles |> Seq.map (fun f -> f.ParsedInput)) + (removeImplFilesThatHaveSignaturesExceptLastOne priorSnapshot) + ) + + let ComputeDependencyGraphForProject (tcConfig: TcConfig) (projectSnapshot: ProjectSnapshotBase) = + + let key = projectSnapshot.SourceFiles.Key(DependencyGraphType.Project) + //caches.DependencyGraph.Get(key, computeDependencyGraph parsedInputs (removeImplFilesThatHaveSignatures projectSnapshot)) + caches.DependencyGraph.Get( + key, + computeDependencyGraph tcConfig (projectSnapshot.SourceFiles |> Seq.map (fun f -> f.ParsedInput)) id + ) + + let ComputeTcIntermediate + (projectSnapshot: ProjectSnapshotBase) + (dependencyGraph: Graph) + (index: FileIndex) + (nodeToCheck: NodeToTypeCheck) + bootstrapInfo + (prevTcInfo: TcInfo) + = + + ignore dependencyGraph + + let key = projectSnapshot.FileKey(index).WithExtraVersion(bootstrapInfo.Id) + + let _label, _k, _version = key.GetLabel(), key.GetKey(), key.GetVersion() + + caches.TcIntermediate.Get( + key, + node { + + let file = projectSnapshot.SourceFiles[index] + + let input = file.ParsedInput + let fileName = file.FileName + + use _ = + Activity.start + "ComputeTcIntermediate" + [| + Activity.Tags.fileName, fileName |> Path.GetFileName + "key", key.GetLabel() + "version", "-" // key.GetVersion() + |] + + beforeFileChecked.Trigger(fileName, Unchecked.defaultof<_>) + + let tcConfig = bootstrapInfo.TcConfig + let tcGlobals = bootstrapInfo.TcGlobals + let tcImports = bootstrapInfo.TcImports + + let mainInputFileName = file.FileName + let sourceText = file.SourceText + let parsedMainInput = file.ParsedInput + + // Initialize the error handler + let errHandler = + ParseAndCheckFile.DiagnosticsHandler( + true, + mainInputFileName, + tcConfig.diagnosticsOptions, + sourceText, + suggestNamesForErrors, + tcConfig.flatErrors + ) + + use _ = + new CompilationGlobalsScope(errHandler.DiagnosticsLogger, BuildPhase.TypeCheck) + + // Apply nowarns to tcConfig (may generate errors, so ensure diagnosticsLogger is installed) + let tcConfig = + ApplyNoWarnsToTcConfig(tcConfig, parsedMainInput, Path.GetDirectoryName mainInputFileName) + + // update the error handler with the modified tcConfig + errHandler.DiagnosticOptions <- tcConfig.diagnosticsOptions + + let diagnosticsLogger = errHandler.DiagnosticsLogger + + //let capturingDiagnosticsLogger = CapturingDiagnosticsLogger("TypeCheck") + + //let diagnosticsLogger = + // GetDiagnosticsLoggerFilteringByScopedPragmas( + // false, + // input.ScopedPragmas, + // tcConfig.diagnosticsOptions, + // capturingDiagnosticsLogger + // ) + + //use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.TypeCheck) + + //beforeFileChecked.Trigger fileName + + ApplyMetaCommandsFromInputToTcConfig(tcConfig, input, Path.GetDirectoryName fileName, tcImports.DependencyProvider) + |> ignore + + let sink = TcResultsSinkImpl(tcGlobals, file.SourceText) + + let hadParseErrors = not (Array.isEmpty file.ParseErrors) + + let input, moduleNamesDict = + DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input + + //let! ct = NodeCode.CancellationToken + + try + //do! maxParallelismSemaphore.WaitAsync(ct) |> NodeCode.AwaitTask + + let! finisher = + CheckOneInputWithCallback + nodeToCheck + ((fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), + tcConfig, + tcImports, + tcGlobals, + None, + TcResultsSink.WithSink sink, + prevTcInfo.tcState, + input, + true) + |> Cancellable.toAsync + |> NodeCode.AwaitAsync + + //fileChecked.Trigger fileName + + fileChecked.Trigger(fileName, Unchecked.defaultof<_>) + + return + { + finisher = finisher + moduleNamesDict = moduleNamesDict + tcDiagnosticsRev = [ errHandler.CollectedPhasedDiagnostics ] + tcDependencyFiles = [ fileName ] + sink = sink + } + finally + () + //maxParallelismSemaphore.Release() |> ignore + } + ) + + let processGraphNode projectSnapshot bootstrapInfo dependencyFiles collectSinks (fileNode: NodeToTypeCheck) tcInfo = + // TODO: should this be node? + async { + match fileNode with + | NodeToTypeCheck.PhysicalFile index -> + + let! tcIntermediate = + ComputeTcIntermediate projectSnapshot dependencyFiles index fileNode bootstrapInfo tcInfo + |> Async.AwaitNodeCode + + let (Finisher(node = node; finisher = finisher)) = tcIntermediate.finisher + + return + Finisher( + node, + (fun tcInfo -> + + if tcInfo.stateContainsNodes |> Set.contains fileNode then + failwith $"Oops!" + + //if + // tcInfo.stateContainsNodes + // Signature files don't have to be right above the impl file... if we need this check then + // we need to do it differently + // |> Set.contains (NodeToTypeCheck.ArtificialImplFile(index - 1)) + //then + // failwith $"Oops???" + + let partialResult, tcState = finisher tcInfo.tcState + + let tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile = partialResult + + let tcEnvAtEndOfFile = + if keepAllBackgroundResolutions then + tcEnv + else + tcState.TcEnvFromImpls + + partialResult, + { tcInfo with + tcState = tcState + tcEnvAtEndOfFile = tcEnvAtEndOfFile + moduleNamesDict = tcIntermediate.moduleNamesDict + topAttribs = Some topAttribs + tcDiagnosticsRev = tcIntermediate.tcDiagnosticsRev @ tcInfo.tcDiagnosticsRev + tcDependencyFiles = tcIntermediate.tcDependencyFiles @ tcInfo.tcDependencyFiles + latestCcuSigForFile = Some ccuSigForFile + graphNode = Some node + stateContainsNodes = tcInfo.stateContainsNodes |> Set.add node + sink = + if collectSinks then + tcIntermediate.sink :: tcInfo.sink + else + [ tcIntermediate.sink ] + }) + ) + + | NodeToTypeCheck.ArtificialImplFile index -> + return + Finisher( + fileNode, + (fun tcInfo -> + + if tcInfo.stateContainsNodes |> Set.contains fileNode then + failwith $"Oops!" + + if + tcInfo.stateContainsNodes + |> Set.contains (NodeToTypeCheck.PhysicalFile(index + 1)) + then + failwith $"Oops!!!" + + let parsedInput = projectSnapshot.SourceFiles[index].ParsedInput + let prefixPathOpt = None + // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. + let partialResult, tcState = + AddSignatureResultToTcImplEnv + (bootstrapInfo.TcImports, + bootstrapInfo.TcGlobals, + prefixPathOpt, + TcResultsSink.NoSink, + tcInfo.tcState, + parsedInput) + tcInfo.tcState + + let tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile = partialResult + + let tcEnvAtEndOfFile = + if keepAllBackgroundResolutions then + tcEnv + else + tcState.TcEnvFromImpls + + partialResult, + { tcInfo with + tcState = tcState + tcEnvAtEndOfFile = tcEnvAtEndOfFile + topAttribs = Some topAttribs + latestCcuSigForFile = Some ccuSigForFile + graphNode = Some fileNode + stateContainsNodes = tcInfo.stateContainsNodes |> Set.add fileNode + }) + ) + + } + + let parseSourceFiles (projectSnapshot: ProjectSnapshotWithSources) tcConfig = + node { + let! parsedInputs = + projectSnapshot.SourceFiles + |> Seq.map (ComputeParseFile projectSnapshot tcConfig) + |> NodeCode.Parallel + + return ProjectSnapshotBase<_>(projectSnapshot.ProjectCore, parsedInputs |> Array.toList) + } + + // Type check file and all its dependencies + let ComputeTcLastFile (bootstrapInfo: BootstrapInfo) (projectSnapshot: ProjectSnapshotWithSources) = + let fileName = projectSnapshot.SourceFiles |> List.last |> (fun f -> f.FileName) + + caches.TcLastFile.Get( + projectSnapshot.FileKey fileName, + node { + let file = projectSnapshot.SourceFiles |> List.last + + use _ = + Activity.start "ComputeTcLastFile" [| Activity.Tags.fileName, file.FileName |> Path.GetFileName |] + + let! projectSnapshot = parseSourceFiles projectSnapshot bootstrapInfo.TcConfig + + let! graph, dependencyFiles = ComputeDependencyGraphForFile bootstrapInfo.TcConfig projectSnapshot + + let! results, tcInfo = + processTypeCheckingGraph + graph + (processGraphNode projectSnapshot bootstrapInfo dependencyFiles false) + bootstrapInfo.InitialTcInfo + |> NodeCode.AwaitAsync + + let lastResult = results |> List.head |> snd + + return lastResult, tcInfo + } + ) + + let getParseResult (projectSnapshot: ProjectSnapshot) creationDiags file (tcConfig: TcConfig) = + node { + let! parsedFile = ComputeParseFile projectSnapshot tcConfig file + + let parseDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + tcConfig.diagnosticsOptions, + false, + file.FileName, + parsedFile.ParseErrors, + suggestNamesForErrors, + tcConfig.flatErrors, + None + ) + + let diagnostics = [| yield! creationDiags; yield! parseDiagnostics |] + + return + FSharpParseFileResults( + diagnostics = diagnostics, + input = parsedFile.ParsedInput, + parseHadErrors = (parseDiagnostics.Length > 0), + // TODO: check if we really need this in parse results + dependencyFiles = [||] + ) + } + + let emptyParseResult fileName diagnostics = + let parseTree = EmptyParsedInput(fileName, (false, false)) + FSharpParseFileResults(diagnostics, parseTree, true, [||]) + + let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: ProjectSnapshot) = + caches.ParseAndCheckFileInProject.Get( + projectSnapshot.FileKey fileName, + node { + + use _ = + Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + + match! ComputeBootstrapInfo projectSnapshot with + | None, creationDiags -> return emptyParseResult fileName creationDiags, FSharpCheckFileAnswer.Aborted + + | Some bootstrapInfo, creationDiags -> + + let priorSnapshot = projectSnapshot.UpTo fileName + let! snapshotWithSources = LoadSources bootstrapInfo priorSnapshot + let file = snapshotWithSources.SourceFiles |> List.last + + let! parseResults = getParseResult projectSnapshot Seq.empty file bootstrapInfo.TcConfig + + let! result, tcInfo = ComputeTcLastFile bootstrapInfo snapshotWithSources + + let (tcEnv, _topAttribs, checkedImplFileOpt, ccuSigForFile) = result + + let tcState = tcInfo.tcState + + let sink = tcInfo.sink.Head // TODO: don't use head + + let tcResolutions = sink.GetResolutions() + let tcSymbolUses = sink.GetSymbolUses() + let tcOpenDeclarations = sink.GetOpenDeclarations() + + let tcDependencyFiles = [] // TODO add as a set to TcIntermediate + + // TODO: Apparently creating diagnostics can produce further diagnostics. So let's capture those too. Hopefully there is a more elegant solution... + // Probably diagnostics need to be evaluated during typecheck anyway for proper formatting, which might take care of this too. + let extraLogger = CapturingDiagnosticsLogger("DiagnosticsWhileCreatingDiagnostics") + use _ = new CompilationGlobalsScope(extraLogger, BuildPhase.TypeCheck) + + // Apply nowarns to tcConfig (may generate errors, so ensure diagnosticsLogger is installed) + let tcConfig = + ApplyNoWarnsToTcConfig(bootstrapInfo.TcConfig, parseResults.ParseTree, Path.GetDirectoryName fileName) + + let diagnosticsOptions = tcConfig.diagnosticsOptions + + let symbolEnv = + SymbolEnv(bootstrapInfo.TcGlobals, tcState.Ccu, Some tcState.CcuSig, bootstrapInfo.TcImports) + + let tcDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + diagnosticsOptions, + false, + fileName, + tcInfo.TcDiagnostics, + suggestNamesForErrors, + bootstrapInfo.TcConfig.flatErrors, + Some symbolEnv + ) + + let extraDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + diagnosticsOptions, + false, + fileName, + extraLogger.Diagnostics, + suggestNamesForErrors, + bootstrapInfo.TcConfig.flatErrors, + Some symbolEnv + ) + + let tcDiagnostics = [| yield! extraDiagnostics; yield! tcDiagnostics |] + + let loadClosure = None // TODO: script support + + let typedResults = + FSharpCheckFileResults.Make( + fileName, + projectSnapshot.ProjectFileName, + bootstrapInfo.TcConfig, + bootstrapInfo.TcGlobals, + projectSnapshot.IsIncompleteTypeCheckEnvironment, + None, + projectSnapshot.ToOptions(), + Array.ofList tcDependencyFiles, + creationDiags, + parseResults.Diagnostics, + tcDiagnostics, + keepAssemblyContents, + ccuSigForFile, + tcState.Ccu, + bootstrapInfo.TcImports, + tcEnv.AccessRights, + tcResolutions, + tcSymbolUses, + tcEnv.NameEnv, + loadClosure, + checkedImplFileOpt, + tcOpenDeclarations + ) + + return (parseResults, FSharpCheckFileAnswer.Succeeded typedResults) + } + ) + + let ComputeParseAndCheckAllFilesInProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: ProjectSnapshotWithSources) = + caches.ParseAndCheckAllFilesInProject.Get( + projectSnapshot.FullKey, + node { + use _ = + Activity.start + "ComputeParseAndCheckAllFilesInProject" + [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] + + let! projectSnapshot = parseSourceFiles projectSnapshot bootstrapInfo.TcConfig + + let! graph, dependencyFiles = ComputeDependencyGraphForProject bootstrapInfo.TcConfig projectSnapshot + + return! + processTypeCheckingGraph + graph + (processGraphNode projectSnapshot bootstrapInfo dependencyFiles true) + bootstrapInfo.InitialTcInfo + |> NodeCode.AwaitAsync + } + ) + + let ComputeProjectExtras (bootstrapInfo: BootstrapInfo) (projectSnapshot: ProjectSnapshotWithSources) = + caches.ProjectExtras.Get( + projectSnapshot.SignatureKey, + node { + + let! results, finalInfo = ComputeParseAndCheckAllFilesInProject bootstrapInfo projectSnapshot + + let assemblyName = bootstrapInfo.AssemblyName + let tcConfig = bootstrapInfo.TcConfig + let tcGlobals = bootstrapInfo.TcGlobals + + let results = results |> Seq.sortBy fst |> Seq.map snd |> Seq.toList + + // Finish the checking + let (_tcEnvAtEndOfLastFile, topAttrs, checkedImplFiles, _), tcState = + CheckMultipleInputsFinish(results, finalInfo.tcState) + + let tcState, _, ccuContents = CheckClosedInputSetFinish([], tcState) + + let generatedCcu = tcState.Ccu.CloneWithFinalizedContents(ccuContents) + + // Compute the identity of the generated assembly based on attributes, options etc. + // Some of this is duplicated from fsc.fs + let ilAssemRef = + let publicKey = + try + let signingInfo = ValidateKeySigningAttributes(tcConfig, tcGlobals, topAttrs) + + match GetStrongNameSigner signingInfo with + | None -> None + | Some s -> Some(PublicKey.KeyAsToken(s.PublicKey)) + with exn -> + errorRecoveryNoRange exn + None + + let locale = + TryFindFSharpStringAttribute + tcGlobals + (tcGlobals.FindSysAttrib "System.Reflection.AssemblyCultureAttribute") + topAttrs.assemblyAttrs + + let assemVerFromAttrib = + TryFindFSharpStringAttribute + tcGlobals + (tcGlobals.FindSysAttrib "System.Reflection.AssemblyVersionAttribute") + topAttrs.assemblyAttrs + |> Option.bind (fun v -> + try + Some(parseILVersion v) + with _ -> + None) + + let ver = + match assemVerFromAttrib with + | None -> tcConfig.version.GetVersionInfo(tcConfig.implicitIncludeDir) + | Some v -> v + + ILAssemblyRef.Create(assemblyName, None, publicKey, false, Some ver, locale) + + let assemblyDataResult = + try + // Assemblies containing type provider components can not successfully be used via cross-assembly references. + // We return 'None' for the assembly portion of the cross-assembly reference + let hasTypeProviderAssemblyAttrib = + topAttrs.assemblyAttrs + |> List.exists (fun (Attrib(tcref, _, _, _, _, _, _)) -> + let nm = tcref.CompiledRepresentationForNamedType.BasicQualifiedName + + nm = typeof.FullName) + + if tcState.CreatesGeneratedProvidedTypes || hasTypeProviderAssemblyAttrib then + ProjectAssemblyDataResult.Unavailable true + else + ProjectAssemblyDataResult.Available( + RawFSharpAssemblyDataBackedByLanguageService( + bootstrapInfo.TcConfig, + bootstrapInfo.TcGlobals, + generatedCcu, + bootstrapInfo.OutFile, + topAttrs, + bootstrapInfo.AssemblyName, + ilAssemRef + ) + :> IRawFSharpAssemblyData + ) + with exn -> + errorRecoveryNoRange exn + ProjectAssemblyDataResult.Unavailable true + + return finalInfo, ilAssemRef, assemblyDataResult, checkedImplFiles + } + ) + + let ComputeAssemblyData (projectSnapshot: ProjectSnapshot) fileName = + caches.AssemblyData.Get( + projectSnapshot.SignatureKey, + node { + + try + + let availableOnDiskModifiedTime = + if FileSystem.FileExistsShim fileName then + Some <| FileSystem.GetLastWriteTimeShim fileName + else + None + + // TODO: This kinda works, but the problem is that in order to switch a project to "in-memory" mode + // - some file needs to be edited (this tirggers a re-check, but LastModifiedTimeOnDisk won't change) + // - saved (this will not trigger anything) + // - and then another change has to be made (to any file buffer) - so that recheck is triggered and we get here again + // Until that sequence happens the project will be used from disk (if available). + // To get around it we probably need to detect changes made in the editor and record a timestamp for them. + let shouldUseOnDisk = + availableOnDiskModifiedTime + |> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) + + let name = projectSnapshot.ProjectFileName |> Path.GetFileNameWithoutExtension + + if shouldUseOnDisk then + Trace.TraceInformation($"Using assembly on disk: {name}") + return ProjectAssemblyDataResult.Unavailable true + else + match! ComputeBootstrapInfo projectSnapshot with + | None, _ -> + Trace.TraceInformation($"Using assembly on disk (unintentionally): {name}") + return ProjectAssemblyDataResult.Unavailable true + | Some bootstrapInfo, _creationDiags -> + + let! snapshotWithSources = LoadSources bootstrapInfo projectSnapshot + + let! _, _, assemblyDataResult, _ = ComputeProjectExtras bootstrapInfo snapshotWithSources + Trace.TraceInformation($"Using in-memory project reference: {name}") + + return assemblyDataResult + with + | TaskCancelled ex -> return raise ex + | ex -> + errorR (exn ($"Error while computing assembly data for project {projectSnapshot.Label}: {ex}")) + return ProjectAssemblyDataResult.Unavailable true + } + ) + + let ComputeParseAndCheckProject (projectSnapshot: ProjectSnapshot) = + caches.ParseAndCheckProject.Get( + projectSnapshot.FullKey, + node { + + match! ComputeBootstrapInfo projectSnapshot with + | None, creationDiags -> + return FSharpCheckProjectResults(projectSnapshot.ProjectFileName, None, keepAssemblyContents, creationDiags, None) + | Some bootstrapInfo, creationDiags -> + + let! snapshotWithSources = LoadSources bootstrapInfo projectSnapshot + + let! tcInfo, ilAssemRef, assemblyDataResult, checkedImplFiles = ComputeProjectExtras bootstrapInfo snapshotWithSources + + let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions + let fileName = DummyFileNameForRangesWithoutASpecificLocation + + let topAttribs = tcInfo.topAttribs + let tcState = tcInfo.tcState + let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile + let tcDiagnostics = tcInfo.TcDiagnostics + let tcDependencyFiles = tcInfo.tcDependencyFiles + + let symbolEnv = + SymbolEnv(bootstrapInfo.TcGlobals, tcInfo.tcState.Ccu, Some tcInfo.tcState.CcuSig, bootstrapInfo.TcImports) + |> Some + + let tcDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + diagnosticsOptions, + true, + fileName, + tcDiagnostics, + suggestNamesForErrors, + bootstrapInfo.TcConfig.flatErrors, + symbolEnv + ) + + let diagnostics = [| yield! creationDiags; yield! tcDiagnostics |] + + let getAssemblyData () = + match assemblyDataResult with + | ProjectAssemblyDataResult.Available data -> Some data + | _ -> None + + let symbolUses = + tcInfo.sink |> Seq.rev |> Seq.map (fun sink -> sink.GetSymbolUses()) + + let details = + (bootstrapInfo.TcGlobals, + bootstrapInfo.TcImports, + tcState.Ccu, + tcState.CcuSig, + Choice2Of2(async.Return symbolUses), + topAttribs, + getAssemblyData, + ilAssemRef, + tcEnvAtEnd.AccessRights, + Some checkedImplFiles, + Array.ofList tcDependencyFiles, + projectSnapshot.ToOptions()) + + let results = + FSharpCheckProjectResults( + projectSnapshot.ProjectFileName, + Some bootstrapInfo.TcConfig, + keepAssemblyContents, + diagnostics, + Some details + ) + + return results + } + ) + + let tryGetSink (fileName: string) (projectSnapshot: ProjectSnapshot) = + node { + match! ComputeBootstrapInfo projectSnapshot with + | None, _ -> return None + | Some bootstrapInfo, _creationDiags -> + + let! snapshotWithSources = projectSnapshot.UpTo fileName |> LoadSources bootstrapInfo + + let! _, tcInfo = ComputeTcLastFile bootstrapInfo snapshotWithSources + + return tcInfo.sink |> List.tryHead |> Option.map (fun sink -> sink, bootstrapInfo) + } + + let ComputeSemanticClassification (fileName: string, projectSnapshot: ProjectSnapshot) = + caches.SemanticClassification.Get( + projectSnapshot.FileKey fileName, + node { + use _ = + Activity.start "ComputeSemanticClassification" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + + let! sinkOpt = tryGetSink fileName projectSnapshot + + return + sinkOpt + |> Option.bind (fun (sink, bootstrapInfo) -> + let sResolutions = sink.GetResolutions() + + let semanticClassification = + sResolutions.GetSemanticClassification( + bootstrapInfo.TcGlobals, + bootstrapInfo.TcImports.GetImportMap(), + sink.GetFormatSpecifierLocations(), + None + ) + + let sckBuilder = SemanticClassificationKeyStoreBuilder() + sckBuilder.WriteAll semanticClassification + + sckBuilder.TryBuildAndReset()) + |> Option.map (fun sck -> sck.GetView()) + } + ) + + let ComputeItemKeyStore (fileName: string, projectSnapshot: ProjectSnapshot) = + caches.ItemKeyStore.Get( + projectSnapshot.FileKey fileName, + node { + use _ = + Activity.start "ComputeItemKeyStore" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + + let! sinkOpt = tryGetSink fileName projectSnapshot + + return + sinkOpt + |> Option.bind (fun (sink, { TcGlobals = g }) -> + let sResolutions = sink.GetResolutions() + + let builder = ItemKeyStoreBuilder(g) + + let preventDuplicates = + HashSet( + { new IEqualityComparer with + member _.Equals((s1, e1): struct (pos * pos), (s2, e2): struct (pos * pos)) = + Position.posEq s1 s2 && Position.posEq e1 e2 + + member _.GetHashCode o = o.GetHashCode() + } + ) + + sResolutions.CapturedNameResolutions + |> Seq.iter (fun cnr -> + let r = cnr.Range + + if preventDuplicates.Add struct (r.Start, r.End) then + builder.Write(cnr.Range, cnr.Item)) + + builder.TryBuildAndReset()) + } + ) + + member _.ParseFile(fileName, projectSnapshot: ProjectSnapshot, _userOpName) = + node { + //use _ = + // Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + + // TODO: might need to deal with exceptions here: + let tcConfigB, sourceFileNames, _ = ComputeTcConfigBuilder projectSnapshot + + let tcConfig = TcConfig.Create(tcConfigB, validate = true) + + let _index, fileSnapshot = + projectSnapshot.SourceFiles + |> Seq.mapi pair + |> Seq.tryFind (fun (_, f) -> f.FileName = fileName) + |> Option.defaultWith (fun () -> failwith $"File not found: {fileName}") + + let isExe = tcConfig.target.IsExe + let isLastCompiland = fileName = (sourceFileNames |> List.last) + + let! file = LoadSource fileSnapshot isExe isLastCompiland + let! parseResult = getParseResult projectSnapshot Seq.empty file tcConfig + return parseResult + } + + member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: ProjectSnapshot, userOpName: string) = + ignore userOpName + ComputeParseAndCheckFileInProject fileName projectSnapshot + + member _.FindReferencesInFile(fileName: string, projectSnapshot: ProjectSnapshot, symbol: FSharpSymbol, userOpName: string) = + ignore userOpName + + node { + match! ComputeItemKeyStore(fileName, projectSnapshot) with + | None -> return Seq.empty + | Some itemKeyStore -> return itemKeyStore.FindAll symbol.Item + } + + member _.GetAssemblyData(projectSnapshot: ProjectSnapshot, fileName, _userOpName) = + ComputeAssemblyData projectSnapshot fileName + + member _.Caches = caches + + member _.SetCacheSizeFactor(sizeFactor: int) = + if sizeFactor <> caches.SizeFactor then + caches <- CompilerCaches(sizeFactor) + + interface IBackgroundCompiler with + + member this.CheckFileInProject + ( + parseResults: FSharpParseFileResults, + fileName: string, + fileVersion: int, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + node { + let! snapshot = + FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) + |> NodeCode.AwaitAsync + + ignore parseResults + + let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot.ProjectSnapshot, userOpName) + + return result + } + + member this.CheckFileInProjectAllowingStaleCachedResults + ( + parseResults: FSharpParseFileResults, + fileName: string, + fileVersion: int, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + node { + let! snapshot = + FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) + |> NodeCode.AwaitAsync + + ignore parseResults + + let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot.ProjectSnapshot, userOpName) + + return Some result + } + + member this.ClearCache(projects: FSharpProjectIdentifier seq, userOpName: string) : unit = + use _ = + Activity.start "TransparentCompiler.ClearCache" [| Activity.Tags.userOpName, userOpName |] + + this.Caches.Clear( + projects + |> Seq.map (function + | FSharpProjectIdentifier(x, y) -> (x, y)) + |> Set + ) + + member this.ClearCache(options: seq, userOpName: string) : unit = + use _ = + Activity.start "TransparentCompiler.ClearCache" [| Activity.Tags.userOpName, userOpName |] + + backgroundCompiler.ClearCache(options, userOpName) + this.Caches.Clear(options |> Seq.map (fun o -> o.GetProjectIdentifier()) |> Set) + + member _.ClearCaches() : unit = + backgroundCompiler.ClearCaches() + caches <- CompilerCaches(100) // TODO: check + + member _.DownsizeCaches() : unit = backgroundCompiler.DownsizeCaches() + + member _.BeforeBackgroundFileCheck = beforeFileChecked.Publish + + member _.FileParsed = fileParsed.Publish + + member _.FileChecked = fileChecked.Publish + + member _.ProjectChecked = projectChecked.Publish + + member this.FindReferencesInFile + ( + fileName: string, + options: FSharpProjectOptions, + symbol: FSharpSymbol, + canInvalidateProject: bool, + userOpName: string + ) : NodeCode> = + node { + ignore canInvalidateProject + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + + return! this.FindReferencesInFile(fileName, snapshot.ProjectSnapshot, symbol, userOpName) + } + + member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = + this.FindReferencesInFile(fileName, projectSnapshot.ProjectSnapshot, symbol, userOpName) + + member _.FrameworkImportsCache: FrameworkImportsCache = + backgroundCompiler.FrameworkImportsCache + + member this.GetAssemblyData(options: FSharpProjectOptions, fileName, userOpName: string) : NodeCode = + node { + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + return! this.GetAssemblyData(snapshot.ProjectSnapshot, fileName, userOpName) + } + + member this.GetAssemblyData + ( + projectSnapshot: FSharpProjectSnapshot, + fileName, + userOpName: string + ) : NodeCode = + this.GetAssemblyData(projectSnapshot.ProjectSnapshot, fileName, userOpName) + + member this.GetBackgroundCheckResultsForFileInProject + ( + fileName: string, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + node { + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + + match! this.ParseAndCheckFileInProject(fileName, snapshot.ProjectSnapshot, userOpName) with + | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return parseResult, checkResult + | parseResult, FSharpCheckFileAnswer.Aborted -> return parseResult, FSharpCheckFileResults.MakeEmpty(fileName, [||], true) + } + + member this.GetBackgroundParseResultsForFileInProject + ( + fileName: string, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + node { + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + return! this.ParseFile(fileName, snapshot.ProjectSnapshot, userOpName) + } + + member this.GetCachedCheckFileResult + ( + builder: IncrementalBuilder, + fileName: string, + sourceText: ISourceText, + options: FSharpProjectOptions + ) : NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> = + node { + ignore builder + + let! snapshot = + FSharpProjectSnapshot.FromOptions(options, fileName, 1, sourceText) + |> NodeCode.AwaitAsync + + match! this.ParseAndCheckFileInProject(fileName, snapshot.ProjectSnapshot, "GetCachedCheckFileResult") with + | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return Some(parseResult, checkResult) + | _, FSharpCheckFileAnswer.Aborted -> return None + } + + member this.GetProjectOptionsFromScript + ( + fileName: string, + sourceText: ISourceText, + previewEnabled: bool option, + loadedTimeStamp: DateTime option, + otherFlags: string array option, + useFsiAuxLib: bool option, + useSdkRefs: bool option, + sdkDirOverride: string option, + assumeDotNetFramework: bool option, + optionsStamp: int64 option, + userOpName: string + ) : Async = + backgroundCompiler.GetProjectOptionsFromScript( + fileName, + sourceText, + previewEnabled, + loadedTimeStamp, + otherFlags, + useFsiAuxLib, + useSdkRefs, + sdkDirOverride, + assumeDotNetFramework, + optionsStamp, + userOpName + ) + + member this.GetSemanticClassificationForFile(fileName: string, snapshot: FSharpProjectSnapshot, userOpName: string) = + node { + ignore userOpName + return! ComputeSemanticClassification(fileName, snapshot.ProjectSnapshot) + } + + member this.GetSemanticClassificationForFile + ( + fileName: string, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + node { + ignore userOpName + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + return! ComputeSemanticClassification(fileName, snapshot.ProjectSnapshot) + } + + member this.InvalidateConfiguration(options: FSharpProjectOptions, userOpName: string) : unit = + backgroundCompiler.InvalidateConfiguration(options, userOpName) + + member this.NotifyFileChanged(fileName: string, options: FSharpProjectOptions, userOpName: string) : NodeCode = + backgroundCompiler.NotifyFileChanged(fileName, options, userOpName) + + member this.NotifyProjectCleaned(options: FSharpProjectOptions, userOpName: string) : Async = + backgroundCompiler.NotifyProjectCleaned(options, userOpName) + + member this.ParseAndCheckFileInProject + ( + fileName: string, + fileVersion: int, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + node { + let! snapshot = + FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) + |> NodeCode.AwaitAsync + + return! this.ParseAndCheckFileInProject(fileName, snapshot.ProjectSnapshot, userOpName) + } + + member this.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = + this.ParseAndCheckFileInProject(fileName, projectSnapshot.ProjectSnapshot, userOpName) + + member this.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = + node { + ignore userOpName + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + return! ComputeParseAndCheckProject snapshot.ProjectSnapshot + } + + member this.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = + node { + ignore userOpName + return! ComputeParseAndCheckProject projectSnapshot.ProjectSnapshot + } + + member this.ParseFile(fileName, projectSnapshot, userOpName) = + this.ParseFile(fileName, projectSnapshot.ProjectSnapshot, userOpName) + |> Async.AwaitNodeCode + + member this.ParseFile + ( + fileName: string, + sourceText: ISourceText, + options: FSharpParsingOptions, + cache: bool, + flatErrors: bool, + userOpName: string + ) = + backgroundCompiler.ParseFile(fileName, sourceText, options, cache, flatErrors, userOpName) + + member this.TryGetRecentCheckResultsForFile + ( + fileName: string, + options: FSharpProjectOptions, + sourceText: ISourceText option, + userOpName: string + ) : (FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash) option = + backgroundCompiler.TryGetRecentCheckResultsForFile(fileName, options, sourceText, userOpName) diff --git a/src/Compiler/Service/TransparentCompiler.fsi b/src/Compiler/Service/TransparentCompiler.fsi new file mode 100644 index 00000000000..00167ccc67f --- /dev/null +++ b/src/Compiler/Service/TransparentCompiler.fsi @@ -0,0 +1,175 @@ +namespace FSharp.Compiler.CodeAnalysis.TransparentCompiler + +open Internal.Utilities.Collections + +open FSharp.Compiler.AbstractIL.ILBinaryReader +open FSharp.Compiler.BuildGraph +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CompilerConfig +open FSharp.Compiler.CompilerImports +open FSharp.Compiler.CheckBasics +open FSharp.Compiler.Diagnostics +open FSharp.Compiler.DiagnosticsLogger +open FSharp.Compiler.ScriptClosure +open FSharp.Compiler.Symbols +open FSharp.Compiler.TcGlobals +open FSharp.Compiler.Text +open FSharp.Compiler.ParseAndCheckInputs +open FSharp.Compiler.GraphChecking +open FSharp.Compiler.Syntax +open FSharp.Compiler.NameResolution +open FSharp.Compiler.TypedTree +open FSharp.Compiler.CheckDeclarations +open FSharp.Compiler.EditorServices + +/// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. +[] +type internal TcInfo = + { + tcState: TcState + tcEnvAtEndOfFile: TcEnv + + /// Disambiguation table for module names + moduleNamesDict: ModuleNamesDict + + topAttribs: TopAttribs option + + latestCcuSigForFile: ModuleOrNamespaceType option + + /// Accumulated diagnostics, last file first + tcDiagnosticsRev: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] list + + tcDependencyFiles: string list + + sigNameOpt: (string * QualifiedNameOfFile) option + + graphNode: NodeToTypeCheck option + + stateContainsNodes: Set + + sink: TcResultsSinkImpl list + } + +[] +type internal TcIntermediate = + { + finisher: Finisher + + /// Disambiguation table for module names + moduleNamesDict: ModuleNamesDict + + /// Accumulated diagnostics, last file first + tcDiagnosticsRev: (PhasedDiagnostic * FSharpDiagnosticSeverity) array list + tcDependencyFiles: string list + sink: TcResultsSinkImpl + } + +/// Things we need to start parsing and checking files for a given project snapshot +type internal BootstrapInfo = + { Id: int + AssemblyName: string + OutFile: string + TcConfig: TcConfig + TcImports: TcImports + TcGlobals: TcGlobals + InitialTcInfo: TcInfo + LoadedSources: (range * ProjectSnapshot.FSharpFileSnapshot) list + LoadClosure: LoadClosure option + LastFileName: string } + +type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string + +[] +type internal DependencyGraphType = + + /// A dependency graph for a single file - it will be missing files which this file does not depend on + | File + + /// A dependency graph for a project - it will contain all files in the project + | Project + +[] +type internal Extensions = + + [] + static member Key: + fileSnapshots: #ProjectSnapshot.IFileSnapshot list * ?extraKeyFlag: DependencyGraphType -> + ICacheKey<(DependencyGraphType option * byte array), string> + +type internal CompilerCaches = + + new: sizeFactor: int -> CompilerCaches + + member AssemblyData: AsyncMemoize<(string * string), (string * string), ProjectAssemblyDataResult> + + member BootstrapInfo: AsyncMemoize<(string * string), string, (BootstrapInfo option * FSharpDiagnostic array)> + + member BootstrapInfoStatic: + AsyncMemoize<(string * string), (string * string), (int * TcImports * TcGlobals * TcInfo * Event)> + + member DependencyGraph: + AsyncMemoize<(DependencyGraphType option * byte array), string, (Graph * Graph)> + + member FrameworkImports: AsyncMemoize + + member ItemKeyStore: AsyncMemoize<(string * (string * string)), string, ItemKeyStore option> + + member ParseAndCheckAllFilesInProject: AsyncMemoizeDisabled + + member ParseAndCheckFileInProject: + AsyncMemoize<(string * (string * string)), string, (FSharpParseFileResults * FSharpCheckFileAnswer)> + + member ParseAndCheckProject: AsyncMemoize<(string * string), string, FSharpCheckProjectResults> + + member ParseFile: + AsyncMemoize<((string * string) * string), (string * string * bool), ProjectSnapshot.FSharpParsedFile> + + member ProjectExtras: AsyncMemoizeDisabled + + member SemanticClassification: AsyncMemoize<(string * (string * string)), string, SemanticClassificationView option> + + member SizeFactor: int + + member TcIntermediate: AsyncMemoize<(string * (string * string)), (string * int), TcIntermediate> + + member TcLastFile: AsyncMemoizeDisabled + +type internal TransparentCompiler = + interface IBackgroundCompiler + + new: + legacyReferenceResolver: LegacyReferenceResolver * + projectCacheSize: int * + keepAssemblyContents: bool * + keepAllBackgroundResolutions: bool * + tryGetMetadataSnapshot: ILReaderTryGetMetadataSnapshot * + suggestNamesForErrors: bool * + keepAllBackgroundSymbolUses: bool * + enableBackgroundItemKeyStoreAndSemanticClassification: bool * + enablePartialTypeChecking: bool * + parallelReferenceResolution: ParallelReferenceResolution * + captureIdentifiersWhenParsing: bool * + getSource: (string -> Async) option * + useChangeNotifications: bool * + useSyntaxTreeCache: bool -> + TransparentCompiler + + member FindReferencesInFile: + fileName: string * projectSnapshot: ProjectSnapshot.ProjectSnapshot * symbol: FSharpSymbol * userOpName: string -> + NodeCode + + member GetAssemblyData: + projectSnapshot: ProjectSnapshot.ProjectSnapshot * fileName: string * _userOpName: string -> + NodeCode + + member ParseAndCheckFileInProject: + fileName: string * projectSnapshot: ProjectSnapshot.ProjectSnapshot * userOpName: string -> + NodeCode + + member ParseFile: + fileName: string * projectSnapshot: ProjectSnapshot.ProjectSnapshot * _userOpName: 'a -> + NodeCode + + member SetCacheSizeFactor: sizeFactor: int -> unit + + member Caches: CompilerCaches diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index c5b6e64ffdc..492ff2da497 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -17,6 +17,7 @@ open FSharp.Compiler.AbstractIL.IL open FSharp.Compiler.AbstractIL.ILBinaryReader open FSharp.Compiler.AbstractIL.ILDynamicAssemblyWriter open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CodeAnalysis.TransparentCompiler open FSharp.Compiler.CompilerConfig open FSharp.Compiler.CompilerDiagnostics open FSharp.Compiler.CompilerImports @@ -36,19 +37,6 @@ open FSharp.Compiler.Text.Range open FSharp.Compiler.TcGlobals open FSharp.Compiler.BuildGraph -[] -module EnvMisc = - let braceMatchCacheSize = GetEnvInteger "FCS_BraceMatchCacheSize" 5 - let parseFileCacheSize = GetEnvInteger "FCS_ParseFileCacheSize" 2 - let checkFileInProjectCacheSize = GetEnvInteger "FCS_CheckFileInProjectCacheSize" 10 - - let projectCacheSizeDefault = GetEnvInteger "FCS_ProjectCacheSizeDefault" 3 - let frameworkTcImportsCacheStrongSize = GetEnvInteger "FCS_frameworkTcImportsCacheStrongSizeDefault" 8 - -//---------------------------------------------------------------------------- -// BackgroundCompiler -// - [] type DocumentSource = | FileSystem @@ -58,46 +46,6 @@ type DocumentSource = [] type IsResultObsolete = IsResultObsolete of (unit -> bool) -[] -module Helpers = - - /// Determine whether two (fileName,options) keys are identical w.r.t. affect on checking - let AreSameForChecking2 ((fileName1: string, options1: FSharpProjectOptions), (fileName2, options2)) = - (fileName1 = fileName2) - && FSharpProjectOptions.AreSameForChecking(options1, options2) - - /// Determine whether two (fileName,options) keys should be identical w.r.t. resource usage - let AreSubsumable2 ((fileName1: string, o1: FSharpProjectOptions), (fileName2: string, o2: FSharpProjectOptions)) = - (fileName1 = fileName2) && FSharpProjectOptions.UseSameProject(o1, o2) - - /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. parsing - let AreSameForParsing ((fileName1: string, source1Hash: int64, options1), (fileName2, source2Hash, options2)) = - fileName1 = fileName2 && options1 = options2 && source1Hash = source2Hash - - let AreSimilarForParsing ((fileName1, _, _), (fileName2, _, _)) = fileName1 = fileName2 - - /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. checking - let AreSameForChecking3 ((fileName1: string, source1Hash: int64, options1: FSharpProjectOptions), (fileName2, source2Hash, options2)) = - (fileName1 = fileName2) - && FSharpProjectOptions.AreSameForChecking(options1, options2) - && source1Hash = source2Hash - - /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. resource usage - let AreSubsumable3 ((fileName1: string, _, o1: FSharpProjectOptions), (fileName2: string, _, o2: FSharpProjectOptions)) = - (fileName1 = fileName2) && FSharpProjectOptions.UseSameProject(o1, o2) - - /// If a symbol is an attribute check if given set of names contains its name without the Attribute suffix - let rec NamesContainAttribute (symbol: FSharpSymbol) names = - match symbol with - | :? FSharpMemberOrFunctionOrValue as mofov -> - mofov.DeclaringEntity - |> Option.map (fun entity -> NamesContainAttribute entity names) - |> Option.defaultValue false - | :? FSharpEntity as entity when entity.IsAttributeType && symbol.DisplayNameCore.EndsWithOrdinal "Attribute" -> - let nameWithoutAttribute = String.dropSuffix symbol.DisplayNameCore "Attribute" - names |> Set.contains nameWithoutAttribute - | _ -> false - module CompileHelpers = let mkCompilationDiagnosticsHandlers (flatErrors) = let diagnostics = ResizeArray<_>() @@ -165,1190 +113,6 @@ module CompileHelpers = Console.SetError error | None -> () -type SourceTextHash = int64 -type CacheStamp = int64 -type FileName = string -type FilePath = string -type ProjectPath = string -type FileVersion = int - -type ParseCacheLockToken() = - interface LockToken - -type ScriptClosureCacheToken() = - interface LockToken - -type CheckFileCacheKey = FileName * SourceTextHash * FSharpProjectOptions -type CheckFileCacheValue = FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash * DateTime - -// There is only one instance of this type, held in FSharpChecker -type BackgroundCompiler - ( - legacyReferenceResolver, - projectCacheSize, - keepAssemblyContents, - keepAllBackgroundResolutions, - tryGetMetadataSnapshot, - suggestNamesForErrors, - keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification, - enablePartialTypeChecking, - parallelReferenceResolution, - captureIdentifiersWhenParsing, - getSource: (string -> Async) option, - useChangeNotifications, - useSyntaxTreeCache - ) as self = - - let beforeFileChecked = Event() - let fileParsed = Event() - let fileChecked = Event() - let projectChecked = Event() - - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.scriptClosureCache - /// Information about the derived script closure. - let scriptClosureCache = - MruCache( - projectCacheSize, - areSame = FSharpProjectOptions.AreSameForChecking, - areSimilar = FSharpProjectOptions.UseSameProject - ) - - let frameworkTcImportsCache = FrameworkImportsCache(frameworkTcImportsCacheStrongSize) - - // We currently share one global dependency provider for all scripts for the FSharpChecker. - // For projects, one is used per project. - // - // Sharing one for all scripts is necessary for good performance from GetProjectOptionsFromScript, - // which requires a dependency provider to process through the project options prior to working out - // if the cached incremental builder can be used for the project. - let dependencyProviderForScripts = new DependencyProvider() - - let getProjectReferences (options: FSharpProjectOptions) userOpName = - [ - for r in options.ReferencedProjects do - - match r with - | FSharpReferencedProject.FSharpReference(nm, opts) -> - // Don't use cross-project references for FSharp.Core, since various bits of code - // require a concrete FSharp.Core to exist on-disk. The only solutions that have - // these cross-project references to FSharp.Core are VisualFSharp.sln and FSharp.sln. The ramification - // of this is that you need to build FSharp.Core to get intellisense in those projects. - - if - (try - Path.GetFileNameWithoutExtension(nm) - with _ -> - "") - <> GetFSharpCoreLibraryName() - then - { new IProjectReference with - member x.EvaluateRawContents() = - node { - Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) - return! self.GetAssemblyData(opts, userOpName + ".CheckReferencedProject(" + nm + ")") - } - - member x.TryGetLogicalTimeStamp(cache) = - self.TryGetLogicalTimeStampForProject(cache, opts) - - member x.FileName = nm - } - - | FSharpReferencedProject.PEReference(getStamp, delayedReader) -> - { new IProjectReference with - member x.EvaluateRawContents() = - node { - let! ilReaderOpt = delayedReader.TryGetILModuleReader() |> NodeCode.FromCancellable - - match ilReaderOpt with - | Some ilReader -> - let ilModuleDef, ilAsmRefs = ilReader.ILModuleDef, ilReader.ILAssemblyRefs - let data = RawFSharpAssemblyData(ilModuleDef, ilAsmRefs) :> IRawFSharpAssemblyData - return ProjectAssemblyDataResult.Available data - | _ -> - // Note 'false' - if a PEReference doesn't find an ILModuleReader then we don't - // continue to try to use an on-disk DLL - return ProjectAssemblyDataResult.Unavailable false - } - - member x.TryGetLogicalTimeStamp _ = getStamp () |> Some - member x.FileName = delayedReader.OutputFile - } - - | FSharpReferencedProject.ILModuleReference(nm, getStamp, getReader) -> - { new IProjectReference with - member x.EvaluateRawContents() = - cancellable { - let ilReader = getReader () - let ilModuleDef, ilAsmRefs = ilReader.ILModuleDef, ilReader.ILAssemblyRefs - let data = RawFSharpAssemblyData(ilModuleDef, ilAsmRefs) :> IRawFSharpAssemblyData - return ProjectAssemblyDataResult.Available data - } - |> NodeCode.FromCancellable - - member x.TryGetLogicalTimeStamp _ = getStamp () |> Some - member x.FileName = nm - } - ] - - /// CreateOneIncrementalBuilder (for background type checking). Note that fsc.fs also - /// creates an incremental builder used by the command line compiler. - let CreateOneIncrementalBuilder (options: FSharpProjectOptions, userOpName) = - node { - use _ = - Activity.start "BackgroundCompiler.CreateOneIncrementalBuilder" [| Activity.Tags.project, options.ProjectFileName |] - - Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CreateOneIncrementalBuilder", options.ProjectFileName) - let projectReferences = getProjectReferences options userOpName - - let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) - - let dependencyProvider = - if options.UseScriptResolutionRules then - Some dependencyProviderForScripts - else - None - - let! builderOpt, diagnostics = - IncrementalBuilder.TryCreateIncrementalBuilderForProjectOptions( - legacyReferenceResolver, - FSharpCheckerResultsSettings.defaultFSharpBinariesDir, - frameworkTcImportsCache, - loadClosure, - Array.toList options.SourceFiles, - Array.toList options.OtherOptions, - projectReferences, - options.ProjectDirectory, - options.UseScriptResolutionRules, - keepAssemblyContents, - keepAllBackgroundResolutions, - tryGetMetadataSnapshot, - suggestNamesForErrors, - keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification, - enablePartialTypeChecking, - dependencyProvider, - parallelReferenceResolution, - captureIdentifiersWhenParsing, - getSource, - useChangeNotifications, - useSyntaxTreeCache - ) - - match builderOpt with - | None -> () - | Some builder -> - -#if !NO_TYPEPROVIDERS - // Register the behaviour that responds to CCUs being invalidated because of type - // provider Invalidate events. This invalidates the configuration in the build. - builder.ImportsInvalidatedByTypeProvider.Add(fun () -> self.InvalidateConfiguration(options, userOpName)) -#endif - - // Register the callback called just before a file is typechecked by the background builder (without recording - // errors or intellisense information). - // - // This indicates to the UI that the file type check state is dirty. If the file is open and visible then - // the UI will sooner or later request a typecheck of the file, recording errors and intellisense information. - builder.BeforeFileChecked.Add(fun file -> beforeFileChecked.Trigger(file, options)) - builder.FileParsed.Add(fun file -> fileParsed.Trigger(file, options)) - builder.FileChecked.Add(fun file -> fileChecked.Trigger(file, options)) - builder.ProjectChecked.Add(fun () -> projectChecked.Trigger options) - - return (builderOpt, diagnostics) - } - - let parseCacheLock = Lock() - - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.parseFileInProjectCache. Most recently used cache for parsing files. - let parseFileCache = - MruCache(parseFileCacheSize, areSimilar = AreSimilarForParsing, areSame = AreSameForParsing) - - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.checkFileInProjectCache - // - /// Cache which holds recently seen type-checks. - /// This cache may hold out-of-date entries, in two senses - /// - there may be a more recent antecedent state available because the background build has made it available - /// - the source for the file may have changed - - // Also keyed on source. This can only be out of date if the antecedent is out of date - let checkFileInProjectCache = - MruCache>( - keepStrongly = checkFileInProjectCacheSize, - areSame = AreSameForChecking3, - areSimilar = AreSubsumable3 - ) - - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.incrementalBuildersCache. This root typically holds more - // live information than anything else in the F# Language Service, since it holds up to 3 (projectCacheStrongSize) background project builds - // strongly. - // - /// Cache of builds keyed by options. - let gate = obj () - - let incrementalBuildersCache = - MruCache>( - keepStrongly = projectCacheSize, - keepMax = projectCacheSize, - areSame = FSharpProjectOptions.AreSameForChecking, - areSimilar = FSharpProjectOptions.UseSameProject - ) - - let tryGetBuilderNode options = - incrementalBuildersCache.TryGet(AnyCallerThread, options) - - let tryGetBuilder options : NodeCode option = - tryGetBuilderNode options |> Option.map (fun x -> x.GetOrComputeValue()) - - let tryGetSimilarBuilder options : NodeCode option = - incrementalBuildersCache.TryGetSimilar(AnyCallerThread, options) - |> Option.map (fun x -> x.GetOrComputeValue()) - - let tryGetAnyBuilder options : NodeCode option = - incrementalBuildersCache.TryGetAny(AnyCallerThread, options) - |> Option.map (fun x -> x.GetOrComputeValue()) - - let createBuilderNode (options, userOpName, ct: CancellationToken) = - lock gate (fun () -> - if ct.IsCancellationRequested then - GraphNode.FromResult(None, [||]) - else - let getBuilderNode = GraphNode(CreateOneIncrementalBuilder(options, userOpName)) - incrementalBuildersCache.Set(AnyCallerThread, options, getBuilderNode) - getBuilderNode) - - let createAndGetBuilder (options, userOpName) = - node { - let! ct = NodeCode.CancellationToken - let getBuilderNode = createBuilderNode (options, userOpName, ct) - return! getBuilderNode.GetOrComputeValue() - } - - let getOrCreateBuilder (options, userOpName) : NodeCode = - match tryGetBuilder options with - | Some getBuilder -> - node { - match! getBuilder with - | builderOpt, creationDiags when builderOpt.IsNone || not builderOpt.Value.IsReferencesInvalidated -> return builderOpt, creationDiags - | _ -> - // The builder could be re-created, - // clear the check file caches that are associated with it. - // We must do this in order to not return stale results when references - // in the project get changed/added/removed. - parseCacheLock.AcquireLock(fun ltok -> - options.SourceFiles - |> Array.iter (fun sourceFile -> - let key = (sourceFile, 0L, options) - checkFileInProjectCache.RemoveAnySimilar(ltok, key))) - - return! createAndGetBuilder (options, userOpName) - } - | _ -> createAndGetBuilder (options, userOpName) - - let getSimilarOrCreateBuilder (options, userOpName) = - match tryGetSimilarBuilder options with - | Some res -> res - // The builder does not exist at all. Create it. - | None -> getOrCreateBuilder (options, userOpName) - - let getOrCreateBuilderWithInvalidationFlag (options, canInvalidateProject, userOpName) = - if canInvalidateProject then - getOrCreateBuilder (options, userOpName) - else - getSimilarOrCreateBuilder (options, userOpName) - - let getAnyBuilder (options, userOpName) = - match tryGetAnyBuilder options with - | Some getBuilder -> getBuilder - | _ -> getOrCreateBuilder (options, userOpName) - - static let mutable actualParseFileCount = 0 - - static let mutable actualCheckFileCount = 0 - - /// Should be a fast operation. Ensures that we have only one async lazy object per file and its hash. - let getCheckFileNode (parseResults, sourceText, fileName, options, _fileVersion, builder, tcPrior, tcInfo, creationDiags) = - - // Here we lock for the creation of the node, not its execution - parseCacheLock.AcquireLock(fun ltok -> - let key = (fileName, sourceText.GetHashCode() |> int64, options) - - match checkFileInProjectCache.TryGet(ltok, key) with - | Some res -> res - | _ -> - let res = - GraphNode( - node { - let! res = self.CheckOneFileImplAux(parseResults, sourceText, fileName, options, builder, tcPrior, tcInfo, creationDiags) - Interlocked.Increment(&actualCheckFileCount) |> ignore - return res - } - ) - - checkFileInProjectCache.Set(ltok, key, res) - res) - - member _.ParseFile(fileName: string, sourceText: ISourceText, options: FSharpParsingOptions, cache: bool, flatErrors: bool, userOpName: string) = - async { - use _ = - Activity.start - "BackgroundCompiler.ParseFile" - [| - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - Activity.Tags.cache, cache.ToString() - |] - - let! ct = Async.CancellationToken - use _ = Cancellable.UsingToken(ct) - - if cache then - let hash = sourceText.GetHashCode() |> int64 - - match parseCacheLock.AcquireLock(fun ltok -> parseFileCache.TryGet(ltok, (fileName, hash, options))) with - | Some res -> return res - | None -> - Interlocked.Increment(&actualParseFileCount) |> ignore - let! ct = Async.CancellationToken - - let parseDiagnostics, parseTree, anyErrors = - ParseAndCheckFile.parseFile ( - sourceText, - fileName, - options, - userOpName, - suggestNamesForErrors, - flatErrors, - captureIdentifiersWhenParsing, - ct - ) - - let res = FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, options.SourceFiles) - parseCacheLock.AcquireLock(fun ltok -> parseFileCache.Set(ltok, (fileName, hash, options), res)) - return res - else - let! ct = Async.CancellationToken - - let parseDiagnostics, parseTree, anyErrors = - ParseAndCheckFile.parseFile (sourceText, fileName, options, userOpName, false, flatErrors, captureIdentifiersWhenParsing, ct) - - return FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, options.SourceFiles) - } - - /// Fetch the parse information from the background compiler (which checks w.r.t. the FileSystem API) - member _.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName) = - node { - use _ = - Activity.start - "BackgroundCompiler.GetBackgroundParseResultsForFileInProject" - [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, userOpName |] - - let! ct = NodeCode.CancellationToken - use _ = Cancellable.UsingToken(ct) - - let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> - let parseTree = EmptyParsedInput(fileName, (false, false)) - return FSharpParseFileResults(creationDiags, parseTree, true, [||]) - | Some builder -> - let parseTree, _, _, parseDiagnostics = builder.GetParseResultsForFile fileName - - let parseDiagnostics = - DiagnosticHelpers.CreateDiagnostics( - builder.TcConfig.diagnosticsOptions, - false, - fileName, - parseDiagnostics, - suggestNamesForErrors, - builder.TcConfig.flatErrors, - None - ) - - let diagnostics = [| yield! creationDiags; yield! parseDiagnostics |] - - let parseResults = - FSharpParseFileResults( - diagnostics = diagnostics, - input = parseTree, - parseHadErrors = false, - dependencyFiles = builder.AllDependenciesDeprecated - ) - - return parseResults - } - - member _.GetCachedCheckFileResult(builder: IncrementalBuilder, fileName, sourceText: ISourceText, options) = - node { - use _ = - Activity.start "BackgroundCompiler.GetCachedCheckFileResult" [| Activity.Tags.fileName, fileName |] - - let hash = sourceText.GetHashCode() |> int64 - let key = (fileName, hash, options) - let cachedResultsOpt = parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.TryGet(ltok, key)) - - match cachedResultsOpt with - | Some cachedResults -> - match! cachedResults.GetOrComputeValue() with - | parseResults, checkResults, _, priorTimeStamp when - (match builder.GetCheckResultsBeforeFileInProjectEvenIfStale fileName with - | None -> false - | Some(tcPrior) -> - tcPrior.ProjectTimeStamp = priorTimeStamp - && builder.AreCheckResultsBeforeFileInProjectReady(fileName)) - -> - return Some(parseResults, checkResults) - | _ -> - parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.RemoveAnySimilar(ltok, key)) - return None - | _ -> return None - } - - member private _.CheckOneFileImplAux - ( - parseResults: FSharpParseFileResults, - sourceText: ISourceText, - fileName: string, - options: FSharpProjectOptions, - builder: IncrementalBuilder, - tcPrior: PartialCheckResults, - tcInfo: TcInfo, - creationDiags: FSharpDiagnostic[] - ) : NodeCode = - - node { - // Get additional script #load closure information if applicable. - // For scripts, this will have been recorded by GetProjectOptionsFromScript. - let tcConfig = tcPrior.TcConfig - let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) - - let! checkAnswer = - FSharpCheckFileResults.CheckOneFile( - parseResults, - sourceText, - fileName, - options.ProjectFileName, - tcConfig, - tcPrior.TcGlobals, - tcPrior.TcImports, - tcInfo.tcState, - tcInfo.moduleNamesDict, - loadClosure, - tcInfo.TcDiagnostics, - options.IsIncompleteTypeCheckEnvironment, - options, - builder, - Array.ofList tcInfo.tcDependencyFiles, - creationDiags, - parseResults.Diagnostics, - keepAssemblyContents, - suggestNamesForErrors - ) - |> NodeCode.FromCancellable - - GraphNode.SetPreferredUILang tcConfig.preferredUiLang - return (parseResults, checkAnswer, sourceText.GetHashCode() |> int64, tcPrior.ProjectTimeStamp) - } - - member private bc.CheckOneFileImpl - ( - parseResults: FSharpParseFileResults, - sourceText: ISourceText, - fileName: string, - options: FSharpProjectOptions, - fileVersion: int, - builder: IncrementalBuilder, - tcPrior: PartialCheckResults, - tcInfo: TcInfo, - creationDiags: FSharpDiagnostic[] - ) = - - node { - match! bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) with - | Some(_, results) -> return FSharpCheckFileAnswer.Succeeded results - | _ -> - let lazyCheckFile = - getCheckFileNode (parseResults, sourceText, fileName, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) - - let! _, results, _, _ = lazyCheckFile.GetOrComputeValue() - return FSharpCheckFileAnswer.Succeeded results - } - - /// Type-check the result obtained by parsing, but only if the antecedent type checking context is available. - member bc.CheckFileInProjectAllowingStaleCachedResults - ( - parseResults: FSharpParseFileResults, - fileName, - fileVersion, - sourceText: ISourceText, - options, - userOpName - ) = - node { - use _ = - Activity.start - "BackgroundCompiler.CheckFileInProjectAllowingStaleCachedResults" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - |] - - let! ct = NodeCode.CancellationToken - use _ = Cancellable.UsingToken(ct) - - let! cachedResults = - node { - let! builderOpt, creationDiags = getAnyBuilder (options, userOpName) - - match builderOpt with - | Some builder -> - match! bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) with - | Some(_, checkResults) -> return Some(builder, creationDiags, Some(FSharpCheckFileAnswer.Succeeded checkResults)) - | _ -> return Some(builder, creationDiags, None) - | _ -> return None // the builder wasn't ready - } - - match cachedResults with - | None -> return None - | Some(_, _, Some x) -> return Some x - | Some(builder, creationDiags, None) -> - Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CheckFileInProjectAllowingStaleCachedResults.CacheMiss", fileName) - - match builder.GetCheckResultsBeforeFileInProjectEvenIfStale fileName with - | Some tcPrior -> - match tcPrior.TryPeekTcInfo() with - | Some tcInfo -> - let! checkResults = - bc.CheckOneFileImpl(parseResults, sourceText, fileName, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) - - return Some checkResults - | None -> return None - | None -> return None // the incremental builder was not up to date - } - - /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. - member bc.CheckFileInProject(parseResults: FSharpParseFileResults, fileName, fileVersion, sourceText: ISourceText, options, userOpName) = - node { - use _ = - Activity.start - "BackgroundCompiler.CheckFileInProject" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - |] - - let! ct = NodeCode.CancellationToken - use _ = Cancellable.UsingToken(ct) - - let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> return FSharpCheckFileAnswer.Succeeded(FSharpCheckFileResults.MakeEmpty(fileName, creationDiags, keepAssemblyContents)) - | Some builder -> - // Check the cache. We can only use cached results when there is no work to do to bring the background builder up-to-date - let! cachedResults = bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) - - match cachedResults with - | Some(_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults - | _ -> - let! tcPrior = builder.GetCheckResultsBeforeFileInProject fileName - let! tcInfo = tcPrior.GetOrComputeTcInfo() - return! bc.CheckOneFileImpl(parseResults, sourceText, fileName, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) - } - - /// Parses and checks the source file and returns untyped AST and check results. - member bc.ParseAndCheckFileInProject(fileName: string, fileVersion, sourceText: ISourceText, options: FSharpProjectOptions, userOpName) = - node { - use _ = - Activity.start - "BackgroundCompiler.ParseAndCheckFileInProject" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - |] - - let! ct = NodeCode.CancellationToken - use _ = Cancellable.UsingToken(ct) - - let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> - let parseTree = EmptyParsedInput(fileName, (false, false)) - let parseResults = FSharpParseFileResults(creationDiags, parseTree, true, [||]) - return (parseResults, FSharpCheckFileAnswer.Aborted) - - | Some builder -> - let! cachedResults = bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) - - match cachedResults with - | Some(parseResults, checkResults) -> return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) - | _ -> - let! tcPrior = builder.GetCheckResultsBeforeFileInProject fileName - let! tcInfo = tcPrior.GetOrComputeTcInfo() - // Do the parsing. - let parsingOptions = - FSharpParsingOptions.FromTcConfig(builder.TcConfig, Array.ofList builder.SourceFiles, options.UseScriptResolutionRules) - - GraphNode.SetPreferredUILang tcPrior.TcConfig.preferredUiLang - let! ct = NodeCode.CancellationToken - - let parseDiagnostics, parseTree, anyErrors = - ParseAndCheckFile.parseFile ( - sourceText, - fileName, - parsingOptions, - userOpName, - suggestNamesForErrors, - builder.TcConfig.flatErrors, - captureIdentifiersWhenParsing, - ct - ) - - let parseResults = - FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, builder.AllDependenciesDeprecated) - - let! checkResults = - bc.CheckOneFileImpl(parseResults, sourceText, fileName, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) - - return (parseResults, checkResults) - } - - member _.NotifyFileChanged(fileName, options, userOpName) = - node { - use _ = - Activity.start - "BackgroundCompiler.NotifyFileChanged" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - |] - - let! ct = NodeCode.CancellationToken - use _ = Cancellable.UsingToken(ct) - - let! builderOpt, _ = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> return () - | Some builder -> do! builder.NotifyFileChanged(fileName, DateTime.UtcNow) - } - - /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) - member _.GetBackgroundCheckResultsForFileInProject(fileName, options, userOpName) = - node { - use _ = - Activity.start - "BackgroundCompiler.ParseAndCheckFileInProject" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - |] - - let! ct = NodeCode.CancellationToken - use _ = Cancellable.UsingToken(ct) - - let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> - let parseTree = EmptyParsedInput(fileName, (false, false)) - let parseResults = FSharpParseFileResults(creationDiags, parseTree, true, [||]) - let typedResults = FSharpCheckFileResults.MakeEmpty(fileName, creationDiags, true) - return (parseResults, typedResults) - | Some builder -> - let parseTree, _, _, parseDiagnostics = builder.GetParseResultsForFile fileName - let! tcProj = builder.GetFullCheckResultsAfterFileInProject fileName - - let! tcInfo, tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() - - let tcResolutions = tcInfoExtras.tcResolutions - let tcSymbolUses = tcInfoExtras.tcSymbolUses - let tcOpenDeclarations = tcInfoExtras.tcOpenDeclarations - let latestCcuSigForFile = tcInfo.latestCcuSigForFile - let tcState = tcInfo.tcState - let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile - let latestImplementationFile = tcInfoExtras.latestImplFile - let tcDependencyFiles = tcInfo.tcDependencyFiles - let tcDiagnostics = tcInfo.TcDiagnostics - let diagnosticsOptions = builder.TcConfig.diagnosticsOptions - - let symbolEnv = - SymbolEnv(tcProj.TcGlobals, tcInfo.tcState.Ccu, Some tcInfo.tcState.CcuSig, tcProj.TcImports) - |> Some - - let parseDiagnostics = - DiagnosticHelpers.CreateDiagnostics( - diagnosticsOptions, - false, - fileName, - parseDiagnostics, - suggestNamesForErrors, - builder.TcConfig.flatErrors, - None - ) - - let parseDiagnostics = [| yield! creationDiags; yield! parseDiagnostics |] - - let tcDiagnostics = - DiagnosticHelpers.CreateDiagnostics( - diagnosticsOptions, - false, - fileName, - tcDiagnostics, - suggestNamesForErrors, - builder.TcConfig.flatErrors, - symbolEnv - ) - - let tcDiagnostics = [| yield! creationDiags; yield! tcDiagnostics |] - - let parseResults = - FSharpParseFileResults( - diagnostics = parseDiagnostics, - input = parseTree, - parseHadErrors = false, - dependencyFiles = builder.AllDependenciesDeprecated - ) - - let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) - - let typedResults = - FSharpCheckFileResults.Make( - fileName, - options.ProjectFileName, - tcProj.TcConfig, - tcProj.TcGlobals, - options.IsIncompleteTypeCheckEnvironment, - builder, - options, - Array.ofList tcDependencyFiles, - creationDiags, - parseResults.Diagnostics, - tcDiagnostics, - keepAssemblyContents, - Option.get latestCcuSigForFile, - tcState.Ccu, - tcProj.TcImports, - tcEnvAtEnd.AccessRights, - tcResolutions, - tcSymbolUses, - tcEnvAtEnd.NameEnv, - loadClosure, - latestImplementationFile, - tcOpenDeclarations - ) - - return (parseResults, typedResults) - } - - member _.FindReferencesInFile - ( - fileName: string, - options: FSharpProjectOptions, - symbol: FSharpSymbol, - canInvalidateProject: bool, - userOpName: string - ) = - node { - use _ = - Activity.start - "BackgroundCompiler.FindReferencesInFile" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - "symbol", symbol.FullName - |] - - let! builderOpt, _ = getOrCreateBuilderWithInvalidationFlag (options, canInvalidateProject, userOpName) - - match builderOpt with - | None -> return Seq.empty - | Some builder -> - if builder.ContainsFile fileName then - let! checkResults = builder.GetFullCheckResultsAfterFileInProject fileName - let! keyStoreOpt = checkResults.GetOrComputeItemKeyStoreIfEnabled() - - match keyStoreOpt with - | None -> return Seq.empty - | Some reader -> return reader.FindAll symbol.Item - else - return Seq.empty - } - - member _.GetSemanticClassificationForFile(fileName: string, options: FSharpProjectOptions, userOpName: string) = - node { - use _ = - Activity.start - "BackgroundCompiler.GetSemanticClassificationForFile" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - |] - - let! ct = NodeCode.CancellationToken - use _ = Cancellable.UsingToken(ct) - - let! builderOpt, _ = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> return None - | Some builder -> - let! checkResults = builder.GetFullCheckResultsAfterFileInProject fileName - let! scopt = checkResults.GetOrComputeSemanticClassificationIfEnabled() - - match scopt with - | None -> return None - | Some sc -> return Some(sc.GetView()) - } - - /// Try to get recent approximate type check results for a file. - member _.TryGetRecentCheckResultsForFile(fileName: string, options: FSharpProjectOptions, sourceText: ISourceText option, _userOpName: string) = - use _ = - Activity.start - "BackgroundCompiler.GetSemanticClassificationForFile" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, _userOpName - |] - - match sourceText with - | Some sourceText -> - let hash = sourceText.GetHashCode() |> int64 - - let resOpt = - parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.TryGet(ltok, (fileName, hash, options))) - - match resOpt with - | Some res -> - match res.TryPeekValue() with - | ValueSome(a, b, c, _) -> Some(a, b, c) - | ValueNone -> None - | None -> None - | None -> None - - /// Parse and typecheck the whole project (the implementation, called recursively as project graph is evaluated) - member private _.ParseAndCheckProjectImpl(options, userOpName) = - node { - let! ct = NodeCode.CancellationToken - use _ = Cancellable.UsingToken(ct) - - let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> - let emptyResults = - FSharpCheckProjectResults(options.ProjectFileName, None, keepAssemblyContents, creationDiags, None) - - return emptyResults - | Some builder -> - let! tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt = builder.GetFullCheckResultsAndImplementationsForProject() - let diagnosticsOptions = tcProj.TcConfig.diagnosticsOptions - let fileName = DummyFileNameForRangesWithoutASpecificLocation - - // Although we do not use 'tcInfoExtras', computing it will make sure we get an extra info. - let! tcInfo, _tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() - - let topAttribs = tcInfo.topAttribs - let tcState = tcInfo.tcState - let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile - let tcDiagnostics = tcInfo.TcDiagnostics - let tcDependencyFiles = tcInfo.tcDependencyFiles - - let symbolEnv = - SymbolEnv(tcProj.TcGlobals, tcInfo.tcState.Ccu, Some tcInfo.tcState.CcuSig, tcProj.TcImports) - |> Some - - let tcDiagnostics = - DiagnosticHelpers.CreateDiagnostics( - diagnosticsOptions, - true, - fileName, - tcDiagnostics, - suggestNamesForErrors, - builder.TcConfig.flatErrors, - symbolEnv - ) - - let diagnostics = [| yield! creationDiags; yield! tcDiagnostics |] - - let getAssemblyData () = - match tcAssemblyDataOpt with - | ProjectAssemblyDataResult.Available data -> Some data - | _ -> None - - let details = - (tcProj.TcGlobals, - tcProj.TcImports, - tcState.Ccu, - tcState.CcuSig, - Choice1Of2 builder, - topAttribs, - getAssemblyData, - ilAssemRef, - tcEnvAtEnd.AccessRights, - tcAssemblyExprOpt, - Array.ofList tcDependencyFiles, - options) - - let results = - FSharpCheckProjectResults(options.ProjectFileName, Some tcProj.TcConfig, keepAssemblyContents, diagnostics, Some details) - - return results - } - - member _.GetAssemblyData(options, userOpName) = - node { - use _ = - Activity.start - "BackgroundCompiler.GetAssemblyData" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.userOpName, userOpName - |] - - let! builderOpt, _ = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> return ProjectAssemblyDataResult.Unavailable true - | Some builder -> - let! _, _, tcAssemblyDataOpt, _ = builder.GetCheckResultsAndImplementationsForProject() - return tcAssemblyDataOpt - } - - /// Get the timestamp that would be on the output if fully built immediately - member private _.TryGetLogicalTimeStampForProject(cache, options) = - match tryGetBuilderNode options with - | Some lazyWork -> - match lazyWork.TryPeekValue() with - | ValueSome(Some builder, _) -> Some(builder.GetLogicalTimeStampForProject(cache)) - | _ -> None - | _ -> None - - /// Parse and typecheck the whole project. - member bc.ParseAndCheckProject(options, userOpName) = - use _ = - Activity.start - "BackgroundCompiler.ParseAndCheckProject" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.userOpName, userOpName - |] - - bc.ParseAndCheckProjectImpl(options, userOpName) - - member _.GetProjectOptionsFromScript - ( - fileName, - sourceText, - previewEnabled, - loadedTimeStamp, - otherFlags, - useFsiAuxLib: bool option, - useSdkRefs: bool option, - sdkDirOverride: string option, - assumeDotNetFramework: bool option, - optionsStamp: int64 option, - _userOpName - ) = - use _ = - Activity.start - "BackgroundCompiler.GetProjectOptionsFromScript" - [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, _userOpName |] - - cancellable { - // Do we add a reference to FSharp.Compiler.Interactive.Settings by default? - let useFsiAuxLib = defaultArg useFsiAuxLib true - let useSdkRefs = defaultArg useSdkRefs true - let reduceMemoryUsage = ReduceMemoryFlag.Yes - let previewEnabled = defaultArg previewEnabled false - - // Do we assume .NET Framework references for scripts? - let assumeDotNetFramework = defaultArg assumeDotNetFramework true - - let! ct = Cancellable.token () - use _ = Cancellable.UsingToken(ct) - - let extraFlags = - if previewEnabled then - [| "--langversion:preview" |] - else - [||] - - let otherFlags = defaultArg otherFlags extraFlags - - use diagnostics = new DiagnosticsScope(otherFlags |> Array.contains "--flaterrors") - - let useSimpleResolution = otherFlags |> Array.exists (fun x -> x = "--simpleresolution") - - let loadedTimeStamp = defaultArg loadedTimeStamp DateTime.MaxValue // Not 'now', we don't want to force reloading - - let applyCompilerOptions tcConfigB = - let fsiCompilerOptions = GetCoreFsiCompilerOptions tcConfigB - ParseCompilerOptions(ignore, fsiCompilerOptions, Array.toList otherFlags) - - let loadClosure = - LoadClosure.ComputeClosureOfScriptText( - legacyReferenceResolver, - FSharpCheckerResultsSettings.defaultFSharpBinariesDir, - fileName, - sourceText, - CodeContext.Editing, - useSimpleResolution, - useFsiAuxLib, - useSdkRefs, - sdkDirOverride, - Lexhelp.LexResourceManager(), - applyCompilerOptions, - assumeDotNetFramework, - tryGetMetadataSnapshot, - reduceMemoryUsage, - dependencyProviderForScripts - ) - - let otherFlags = - [| - yield "--noframework" - yield "--warn:3" - yield! otherFlags - for r in loadClosure.References do - yield "-r:" + fst r - for code, _ in loadClosure.NoWarns do - yield "--nowarn:" + code - |] - - let options = - { - ProjectFileName = fileName + ".fsproj" // Make a name that is unique in this directory. - ProjectId = None - SourceFiles = loadClosure.SourceFiles |> List.map fst |> List.toArray - OtherOptions = otherFlags - ReferencedProjects = [||] - IsIncompleteTypeCheckEnvironment = false - UseScriptResolutionRules = true - LoadTime = loadedTimeStamp - UnresolvedReferences = Some(FSharpUnresolvedReferencesSet(loadClosure.UnresolvedReferences)) - OriginalLoadReferences = loadClosure.OriginalLoadReferences - Stamp = optionsStamp - } - - scriptClosureCache.Set(AnyCallerThread, options, loadClosure) // Save the full load closure for later correlation. - - let diags = - loadClosure.LoadClosureRootFileDiagnostics - |> List.map (fun (exn, isError) -> - FSharpDiagnostic.CreateFromException( - exn, - isError, - range.Zero, - false, - options.OtherOptions |> Array.contains "--flaterrors", - None - )) - - return options, (diags @ diagnostics.Diagnostics) - } - |> Cancellable.toAsync - - member bc.InvalidateConfiguration(options: FSharpProjectOptions, userOpName) = - use _ = - Activity.start - "BackgroundCompiler.InvalidateConfiguration" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.userOpName, userOpName - |] - - if incrementalBuildersCache.ContainsSimilarKey(AnyCallerThread, options) then - parseCacheLock.AcquireLock(fun ltok -> - for sourceFile in options.SourceFiles do - checkFileInProjectCache.RemoveAnySimilar(ltok, (sourceFile, 0L, options))) - - let _ = createBuilderNode (options, userOpName, CancellationToken.None) - () - - member bc.ClearCache(options: seq, _userOpName) = - use _ = Activity.start "BackgroundCompiler.ClearCache" [| Activity.Tags.userOpName, _userOpName |] - - lock gate (fun () -> - options - |> Seq.iter (fun options -> - incrementalBuildersCache.RemoveAnySimilar(AnyCallerThread, options) - - parseCacheLock.AcquireLock(fun ltok -> - for sourceFile in options.SourceFiles do - checkFileInProjectCache.RemoveAnySimilar(ltok, (sourceFile, 0L, options))))) - - member _.NotifyProjectCleaned(options: FSharpProjectOptions, userOpName) = - use _ = - Activity.start - "BackgroundCompiler.NotifyProjectCleaned" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.userOpName, userOpName - |] - - async { - let! ct = Async.CancellationToken - use _ = Cancellable.UsingToken(ct) - - let! ct = Async.CancellationToken - // If there was a similar entry (as there normally will have been) then re-establish an empty builder . This - // is a somewhat arbitrary choice - it will have the effect of releasing memory associated with the previous - // builder, but costs some time. - if incrementalBuildersCache.ContainsSimilarKey(AnyCallerThread, options) then - let _ = createBuilderNode (options, userOpName, ct) - () - } - - member _.BeforeBackgroundFileCheck = beforeFileChecked.Publish - - member _.FileParsed = fileParsed.Publish - - member _.FileChecked = fileChecked.Publish - - member _.ProjectChecked = projectChecked.Publish - - member _.ClearCaches() = - use _ = Activity.startNoTags "BackgroundCompiler.ClearCaches" - - lock gate (fun () -> - parseCacheLock.AcquireLock(fun ltok -> - checkFileInProjectCache.Clear(ltok) - parseFileCache.Clear(ltok)) - - incrementalBuildersCache.Clear(AnyCallerThread) - frameworkTcImportsCache.Clear() - scriptClosureCache.Clear AnyCallerThread) - - member _.DownsizeCaches() = - use _ = Activity.startNoTags "BackgroundCompiler.DownsizeCaches" - - lock gate (fun () -> - parseCacheLock.AcquireLock(fun ltok -> - checkFileInProjectCache.Resize(ltok, newKeepStrongly = 1) - parseFileCache.Resize(ltok, newKeepStrongly = 1)) - - incrementalBuildersCache.Resize(AnyCallerThread, newKeepStrongly = 1, newKeepMax = 1) - frameworkTcImportsCache.Downsize() - scriptClosureCache.Resize(AnyCallerThread, newKeepStrongly = 1, newKeepMax = 1)) - - member _.FrameworkImportsCache = frameworkTcImportsCache - - static member ActualParseFileCount = actualParseFileCount - - static member ActualCheckFileCount = actualCheckFileCount - [] // There is typically only one instance of this type in an IDE process. type FSharpChecker @@ -1366,26 +130,47 @@ type FSharpChecker captureIdentifiersWhenParsing, getSource, useChangeNotifications, - useSyntaxTreeCache + useSyntaxTreeCache, + useTransparentCompiler ) = let backgroundCompiler = - BackgroundCompiler( - legacyReferenceResolver, - projectCacheSize, - keepAssemblyContents, - keepAllBackgroundResolutions, - tryGetMetadataSnapshot, - suggestNamesForErrors, - keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification, - enablePartialTypeChecking, - parallelReferenceResolution, - captureIdentifiersWhenParsing, - getSource, - useChangeNotifications, - useSyntaxTreeCache - ) + if useTransparentCompiler = Some true then + TransparentCompiler( + legacyReferenceResolver, + projectCacheSize, + keepAssemblyContents, + keepAllBackgroundResolutions, + tryGetMetadataSnapshot, + suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + parallelReferenceResolution, + captureIdentifiersWhenParsing, + getSource, + useChangeNotifications, + useSyntaxTreeCache + ) + :> IBackgroundCompiler + else + BackgroundCompiler( + legacyReferenceResolver, + projectCacheSize, + keepAssemblyContents, + keepAllBackgroundResolutions, + tryGetMetadataSnapshot, + suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + parallelReferenceResolution, + captureIdentifiersWhenParsing, + getSource, + useChangeNotifications, + useSyntaxTreeCache + ) + :> IBackgroundCompiler static let globalInstance = lazy FSharpChecker.Create() @@ -1429,7 +214,8 @@ type FSharpChecker ?parallelReferenceResolution: bool, ?captureIdentifiersWhenParsing: bool, ?documentSource: DocumentSource, - ?useSyntaxTreeCache: bool + ?useSyntaxTreeCache: bool, + ?useTransparentCompiler: bool ) = use _ = Activity.startNoTags "FSharpChecker.Create" @@ -1480,9 +266,19 @@ type FSharpChecker | Some(DocumentSource.Custom f) -> Some f | _ -> None), useChangeNotifications, - useSyntaxTreeCache + useSyntaxTreeCache, + useTransparentCompiler ) + member _.UsesTransparentCompiler = useTransparentCompiler = Some true + + member _.TransparentCompiler = + match useTransparentCompiler with + | Some true -> backgroundCompiler :?> TransparentCompiler + | _ -> failwith "Transparent Compiler is not enabled." + + member this.Caches = this.TransparentCompiler.Caches + member _.ReferenceResolver = legacyReferenceResolver member _.MatchBraces(fileName, sourceText: ISourceText, options: FSharpParsingOptions, ?userOpName: string) = @@ -1521,6 +317,11 @@ type FSharpChecker let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.ParseFile(fileName, sourceText, options, cache, false, userOpName) + member _.ParseFile(fileName, projectSnapshot, ?userOpName) = + let userOpName = defaultArg userOpName "Unknown" + + backgroundCompiler.ParseFile(fileName, projectSnapshot, userOpName) + member ic.ParseFileInProject(fileName, source: string, options, ?cache: bool, ?userOpName: string) = let parsingOptions, _ = ic.GetParsingOptionsFromProjectOptions(options) ic.ParseFile(fileName, SourceText.ofString source, parsingOptions, ?cache = cache, ?userOpName = userOpName) @@ -1547,9 +348,6 @@ type FSharpChecker use _ = Activity.start "FSharpChecker.Compile" [| Activity.Tags.userOpName, _userOpName |] async { - let! ct = Async.CancellationToken - use _ = Cancellable.UsingToken(ct) - let ctok = CompilationThreadToken() return CompileHelpers.compileFromArgs (ctok, argv, legacyReferenceResolver, None, None) } @@ -1564,7 +362,6 @@ type FSharpChecker backgroundCompiler.ClearCaches() ClearAllILModuleReaderCache() - // This is for unit testing only member ic.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() = use _ = Activity.startNoTags "FsharpChecker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients" @@ -1585,6 +382,10 @@ type FSharpChecker let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.ClearCache(options, userOpName) + member _.ClearCache(projects: ProjectSnapshot.FSharpProjectIdentifier seq, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.ClearCache(projects, userOpName) + /// This function is called when a project has been cleaned, and thus type providers should be refreshed. member _.NotifyProjectCleaned(options: FSharpProjectOptions, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" @@ -1650,12 +451,24 @@ type FSharpChecker backgroundCompiler.ParseAndCheckFileInProject(fileName, fileVersion, sourceText, options, userOpName) |> Async.AwaitNodeCode - member _.ParseAndCheckProject(options, ?userOpName: string) = + member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + + backgroundCompiler.ParseAndCheckFileInProject(fileName, projectSnapshot, userOpName) + |> Async.AwaitNodeCode + + member _.ParseAndCheckProject(options: FSharpProjectOptions, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.ParseAndCheckProject(options, userOpName) |> Async.AwaitNodeCode + member _.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + + backgroundCompiler.ParseAndCheckProject(projectSnapshot, userOpName) + |> Async.AwaitNodeCode + member _.FindBackgroundReferencesInFile ( fileName: string, @@ -1669,9 +482,6 @@ type FSharpChecker let userOpName = defaultArg userOpName "Unknown" node { - let! ct = NodeCode.CancellationToken - use _ = Cancellable.UsingToken(ct) - if fastCheck <> Some true || not captureIdentifiersWhenParsing then return! backgroundCompiler.FindReferencesInFile(fileName, options, symbol, canInvalidateProject, userOpName) else @@ -1687,12 +497,36 @@ type FSharpChecker } |> Async.AwaitNodeCode + member _.FindBackgroundReferencesInFile(fileName: string, projectSnapshot: FSharpProjectSnapshot, symbol: FSharpSymbol, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + + node { + let! parseResults = + backgroundCompiler.ParseFile(fileName, projectSnapshot, userOpName) + |> NodeCode.AwaitAsync + + if + parseResults.ParseTree.Identifiers |> Set.contains symbol.DisplayNameCore + || parseResults.ParseTree.Identifiers |> NamesContainAttribute symbol + then + return! backgroundCompiler.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) + else + return Seq.empty + } + |> Async.AwaitNodeCode + member _.GetBackgroundSemanticClassificationForFile(fileName: string, options: FSharpProjectOptions, ?userOpName) = let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.GetSemanticClassificationForFile(fileName, options, userOpName) |> Async.AwaitNodeCode + member _.GetBackgroundSemanticClassificationForFile(fileName: string, snapshot: FSharpProjectSnapshot, ?userOpName) = + let userOpName = defaultArg userOpName "Unknown" + + backgroundCompiler.GetSemanticClassificationForFile(fileName, snapshot, userOpName) + |> Async.AwaitNodeCode + /// For a given script file, get the ProjectOptions implied by the #load closure member _.GetProjectOptionsFromScript ( diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index 297d7b367bc..14124fbda6b 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -6,8 +6,11 @@ namespace FSharp.Compiler.CodeAnalysis open System open System.IO +open System.Threading +open System.Threading.Tasks open FSharp.Compiler.AbstractIL.ILBinaryReader open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CodeAnalysis.TransparentCompiler open FSharp.Compiler.CompilerConfig open FSharp.Compiler.Diagnostics open FSharp.Compiler.EditorServices @@ -42,6 +45,7 @@ type public FSharpChecker = /// When set to true we create a set of all identifiers for each parsed file which can be used to speed up finding references. /// Default: FileSystem. You can use Custom source to provide a function that will return the source for a given file path instead of reading it from the file system. Note that with this option the FSharpChecker will also not monitor the file system for file changes. It will expect to be notified of changes via the NotifyFileChanged method. /// Default: true. Indicates whether to keep parsing results in a cache. + /// Default: false. Indicates whether we use a new experimental background compiler. This does not yet support all features static member Create: ?projectCacheSize: int * ?keepAssemblyContents: bool * @@ -57,9 +61,13 @@ type public FSharpChecker = [] ?documentSource: DocumentSource * [] ?useSyntaxTreeCache: + bool * + [] ?useTransparentCompiler: bool -> FSharpChecker + member internal UsesTransparentCompiler: bool + /// /// Parse a source code file, returning information about brace matching in the file. /// Return an enumeration of the matching parenthetical tokens in the file. @@ -100,6 +108,10 @@ type public FSharpChecker = fileName: string * sourceText: ISourceText * options: FSharpParsingOptions * ?cache: bool * ?userOpName: string -> Async + [] + member ParseFile: + fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async + /// /// Parses a source code for a file. Returns an AST that can be traversed for various features. /// @@ -193,6 +205,11 @@ type public FSharpChecker = ?userOpName: string -> Async + [] + member ParseAndCheckFileInProject: + fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> + Async + /// /// Parse and typecheck all files in a project. /// All files are read from the FileSystem API @@ -203,6 +220,10 @@ type public FSharpChecker = /// An optional string used for tracing compiler operations associated with this request. member ParseAndCheckProject: options: FSharpProjectOptions * ?userOpName: string -> Async + [] + member ParseAndCheckProject: + projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async + /// /// For a given script file, get the FSharpProjectOptions implied by the #load closure. /// All files are read from the FileSystem API, except the file being checked. @@ -323,6 +344,11 @@ type public FSharpChecker = ?userOpName: string -> Async + [] + member FindBackgroundReferencesInFile: + fileName: string * projectSnapshot: FSharpProjectSnapshot * symbol: FSharpSymbol * ?userOpName: string -> + Async + /// /// Get semantic classification for a file. /// All files are read from the FileSystem API, including the file being checked. @@ -336,6 +362,18 @@ type public FSharpChecker = fileName: string * options: FSharpProjectOptions * ?userOpName: string -> Async + /// + /// Get semantic classification for a file. + /// + /// + /// The file name for the file. + /// The project snapshot for which we want to get the semantic classification. + /// An optional string used for tracing compiler operations associated with this request. + [] + member GetBackgroundSemanticClassificationForFile: + fileName: string * snapshot: FSharpProjectSnapshot * ?userOpName: string -> + Async + /// /// Compile using the given flags. Source files names are resolved via the FileSystem API. /// The output file must be given by a -o flag. @@ -377,6 +415,8 @@ type public FSharpChecker = /// An optional string used for tracing compiler operations associated with this request. member ClearCache: options: FSharpProjectOptions seq * ?userOpName: string -> unit + member ClearCache: projects: ProjectSnapshot.FSharpProjectIdentifier seq * ?userOpName: string -> unit + /// Report a statistic for testability static member ActualParseFileCount: int @@ -421,6 +461,10 @@ type public FSharpChecker = /// The event may be raised on a background thread. member ProjectChecked: IEvent + member internal TransparentCompiler: TransparentCompiler + + member internal Caches: CompilerCaches + [] static member Instance: FSharpChecker diff --git a/src/Compiler/Symbols/SymbolHelpers.fs b/src/Compiler/Symbols/SymbolHelpers.fs index 3c669d7a64c..af89176ff6e 100644 --- a/src/Compiler/Symbols/SymbolHelpers.fs +++ b/src/Compiler/Symbols/SymbolHelpers.fs @@ -284,7 +284,7 @@ module internal SymbolHelpers = | Item.DelegateCtor ty | Item.Types(_, ty :: _) -> match ty with - | AbbrevOrAppTy tcref -> + | AbbrevOrAppTy(tcref, _) -> mkXmlComment (GetXmlDocSigOfEntityRef infoReader m tcref) | _ -> FSharpXmlDoc.None @@ -462,8 +462,8 @@ module internal SymbolHelpers = | Item.UnqualifiedType tcrefs1, Item.UnqualifiedType tcrefs2 -> (tcrefs1, tcrefs2) ||> List.forall2 (fun tcref1 tcref2 -> tyconRefEq g tcref1 tcref2) - | Item.Types(_, [AbbrevOrAppTy tcref1]), Item.UnqualifiedType([tcref2]) -> tyconRefEq g tcref1 tcref2 - | Item.UnqualifiedType([tcref1]), Item.Types(_, [AbbrevOrAppTy tcref2]) -> tyconRefEq g tcref1 tcref2 + | Item.Types(_, [AbbrevOrAppTy(tcref1, _)]), Item.UnqualifiedType([tcref2]) -> tyconRefEq g tcref1 tcref2 + | Item.UnqualifiedType([tcref1]), Item.Types(_, [AbbrevOrAppTy(tcref2, _)]) -> tyconRefEq g tcref1 tcref2 | _ -> false) member x.GetHashCode item = @@ -648,7 +648,7 @@ module internal SymbolHelpers = | Item.Types(_, tys) -> let doc = match tys with - | AbbrevOrAppTy tcref :: _ -> + | AbbrevOrAppTy(tcref, _) :: _ -> if tyconRefUsesLocalXmlDoc g.compilingFSharpCore tcref || tcref.XmlDoc.NonEmpty then Some tcref.XmlDoc else diff --git a/src/Compiler/Symbols/Symbols.fs b/src/Compiler/Symbols/Symbols.fs index 6319af1089c..b5b244a6e5c 100644 --- a/src/Compiler/Symbols/Symbols.fs +++ b/src/Compiler/Symbols/Symbols.fs @@ -292,12 +292,12 @@ type FSharpSymbol(cenv: SymbolEnv, item: unit -> Item, access: FSharpSymbol -> C | Item.CtorGroup(_, cinfo :: _) -> FSharpMemberOrFunctionOrValue(cenv, C cinfo, item) :> _ - | Item.DelegateCtor (AbbrevOrAppTy tcref) -> - FSharpEntity(cenv, tcref) :>_ + | Item.DelegateCtor (AbbrevOrAppTy(tcref, tyargs)) + | Item.Types(_, AbbrevOrAppTy(tcref, tyargs) :: _) -> + FSharpEntity(cenv, tcref, tyargs) :>_ - | Item.UnqualifiedType(tcref :: _) - | Item.Types(_, AbbrevOrAppTy tcref :: _) -> - FSharpEntity(cenv, tcref) :>_ + | Item.UnqualifiedType(tcref :: _) -> + FSharpEntity(cenv, tcref) :> _ | Item.ModuleOrNamespaces(modref :: _) -> FSharpEntity(cenv, modref) :> _ @@ -355,7 +355,7 @@ type FSharpSymbol(cenv: SymbolEnv, item: unit -> Item, access: FSharpSymbol -> C member sym.TryGetAttribute<'T>() = sym.Attributes |> Seq.tryFind (fun attr -> attr.IsAttribute<'T>()) -type FSharpEntity(cenv: SymbolEnv, entity: EntityRef) = +type FSharpEntity(cenv: SymbolEnv, entity: EntityRef, tyargs: TType list) = inherit FSharpSymbol(cenv, (fun () -> checkEntityIsResolved entity @@ -383,6 +383,10 @@ type FSharpEntity(cenv: SymbolEnv, entity: EntityRef) = | None -> false | Some ccu -> ccuEq ccu cenv.g.fslibCcu + new(cenv: SymbolEnv, tcref: TyconRef) = + let _, _, tyargs = FreshenTypeInst cenv.g range0 (tcref.Typars range0) + FSharpEntity(cenv, tcref, tyargs) + member _.Entity = entity member _.LogicalName = @@ -480,6 +484,10 @@ type FSharpEntity(cenv: SymbolEnv, entity: EntityRef) = checkIsResolved() entity.TyparsNoRange |> List.map (fun tp -> FSharpGenericParameter(cenv, tp)) |> makeReadOnlyCollection + member _.GenericArguments = + checkIsResolved() + tyargs |> List.map (fun ty -> FSharpType(cenv, ty)) |> makeReadOnlyCollection + member _.IsMeasure = isResolvedAndFSharp() && (entity.TypeOrMeasureKind = TyparKind.Measure) @@ -718,7 +726,7 @@ type FSharpEntity(cenv: SymbolEnv, entity: EntityRef) = if isUnresolved() then makeReadOnlyCollection [] else entity.ModuleOrNamespaceType.AllEntities |> QueueList.toList - |> List.map (fun x -> FSharpEntity(cenv, entity.NestedTyconRef x)) + |> List.map (fun x -> FSharpEntity(cenv, entity.NestedTyconRef x, tyargs)) |> makeReadOnlyCollection member _.UnionCases = @@ -1688,7 +1696,7 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = | Some v -> v | None -> failwith "DeclarationLocation property not available" - member _.DeclaringEntity = + member _.DeclaringEntity: FSharpEntity option = checkIsResolved() match d with | E e -> FSharpEntity(cenv, e.DeclaringTyconRef) |> Some @@ -1699,12 +1707,16 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = | ParentNone -> None | Parent p -> FSharpEntity(cenv, p) |> Some - member _.ApparentEnclosingEntity = + member _.ApparentEnclosingEntity: FSharpEntity = + let createEntity (ttype: TType) = + let tcref, tyargs = destAppTy cenv.g ttype + FSharpEntity(cenv, tcref, tyargs) + checkIsResolved() match d with - | E e -> FSharpEntity(cenv, e.ApparentEnclosingTyconRef) - | P p -> FSharpEntity(cenv, p.ApparentEnclosingTyconRef) - | M m | C m -> FSharpEntity(cenv, m.ApparentEnclosingTyconRef) + | E e -> createEntity e.ApparentEnclosingType + | P p -> createEntity p.ApparentEnclosingType + | M m | C m -> createEntity m.ApparentEnclosingType | V v -> match v.ApparentEnclosingEntity with | ParentNone -> invalidOp "the value or member doesn't have a logical parent" @@ -2453,7 +2465,7 @@ type FSharpType(cenv, ty:TType) = let isUnresolved() = DiagnosticsLogger.protectAssemblyExploration true <| fun () -> match stripTyparEqns ty with - | TType_app (tcref, _, _) -> FSharpEntity(cenv, tcref).IsUnresolved + | TType_app (tcref, tyargs, _) -> FSharpEntity(cenv, tcref, tyargs).IsUnresolved | TType_measure (Measure.Const tcref) -> FSharpEntity(cenv, tcref).IsUnresolved | TType_measure (Measure.Prod _) -> FSharpEntity(cenv, cenv.g.measureproduct_tcr).IsUnresolved | TType_measure Measure.One -> FSharpEntity(cenv, cenv.g.measureone_tcr).IsUnresolved @@ -2497,7 +2509,7 @@ type FSharpType(cenv, ty:TType) = member _.TypeDefinition = protect <| fun () -> match stripTyparEqns ty with - | TType_app (tcref, _, _) -> FSharpEntity(cenv, tcref) + | TType_app (tcref, tyargs, _) -> FSharpEntity(cenv, tcref, tyargs) | TType_measure (Measure.Const tcref) -> FSharpEntity(cenv, tcref) | TType_measure (Measure.Prod _) -> FSharpEntity(cenv, cenv.g.measureproduct_tcr) | TType_measure Measure.One -> FSharpEntity(cenv, cenv.g.measureone_tcr) diff --git a/src/Compiler/Symbols/Symbols.fsi b/src/Compiler/Symbols/Symbols.fsi index 4050073c704..5530ba53ec5 100644 --- a/src/Compiler/Symbols/Symbols.fsi +++ b/src/Compiler/Symbols/Symbols.fsi @@ -265,6 +265,10 @@ type FSharpEntity = /// Get the generic parameters, possibly including unit-of-measure parameters member GenericParameters: IList + + /// Get the generic parameters, possibly including unit-of-measure parameters + member GenericArguments: IList + #if !NO_TYPEPROVIDERS /// Get the static parameters for a provided type member StaticParameters: IList diff --git a/src/Compiler/SyntaxTree/LexFilter.fs b/src/Compiler/SyntaxTree/LexFilter.fs index 3e7dcfa7262..79afdca04c3 100644 --- a/src/Compiler/SyntaxTree/LexFilter.fs +++ b/src/Compiler/SyntaxTree/LexFilter.fs @@ -182,18 +182,20 @@ let infixTokenLength token = // // LBRACK_LESS and GREATER_RBRACK are not here because adding them in these active patterns // causes more offside warnings, while removing them doesn't add offside warnings in attributes. +[] let (|TokenLExprParen|_|) token = match token with | BEGIN | LPAREN | LBRACE _ | LBRACE_BAR | LBRACK | LBRACK_BAR | LQUOTE _ | LESS true - -> Some () - | _ -> None + -> ValueSome () + | _ -> ValueNone /// Matches against a right-parenthesis-like token that is valid in expressions. +[] let (|TokenRExprParen|_|) token = match token with | END | RPAREN | RBRACE _ | BAR_RBRACE | RBRACK | BAR_RBRACK | RQUOTE _ | GREATER true - -> Some () - | _ -> None + -> ValueSome () + | _ -> ValueNone /// Determine the tokens that may align with the 'if' of an 'if/then/elif/else' without closing /// the construct @@ -512,55 +514,68 @@ type TokenTupPool() = // Utilities for the tokenizer that are needed in other places //--------------------------------------------------------------------------*) +[] +let (|Equals|_|) (s: string) (span: ReadOnlySpan) = + if span.SequenceEqual(s.AsSpan()) then ValueSome Equals + else ValueNone + +[] +let (|StartsWith|_|) (s: string) (span: ReadOnlySpan) = + if span.StartsWith(s.AsSpan()) then ValueSome StartsWith + else ValueNone + // Strip a bunch of leading '>' of a token, at the end of a typar application // Note: this is used in the 'service.fs' to do limited postprocessing +[] let (|TyparsCloseOp|_|) (txt: string) = - let angles = txt |> Seq.takeWhile (fun c -> c = '>') |> Seq.toList - let afterAngles = txt |> Seq.skipWhile (fun c -> c = '>') |> Seq.toList - if List.isEmpty angles then None else - - let afterOp = - match (System.String(Array.ofSeq afterAngles)) with - | "." -> Some DOT - | "]" -> Some RBRACK - | "-" -> Some MINUS - | ".." -> Some DOT_DOT - | "?" -> Some QMARK - | "??" -> Some QMARK_QMARK - | ":=" -> Some COLON_EQUALS - | "::" -> Some COLON_COLON - | "*" -> Some STAR - | "&" -> Some AMP - | "->" -> Some RARROW - | "<-" -> Some LARROW - | "=" -> Some EQUALS - | "<" -> Some (LESS false) - | "$" -> Some DOLLAR - | "%" -> Some (PERCENT_OP("%") ) - | "%%" -> Some (PERCENT_OP("%%")) - | s when String.IsNullOrEmpty(s) -> None - | s -> - match List.ofSeq afterAngles with - | '=' :: _ - | '!' :: '=' :: _ - | '<' :: _ - | '>' :: _ - | '$' :: _ -> Some (INFIX_COMPARE_OP s) - | '&' :: _ -> Some (INFIX_AMP_OP s) - | '|' :: _ -> Some (INFIX_BAR_OP s) - | '!' :: _ - | '?' :: _ - | '~' :: _ -> Some (PREFIX_OP s) - | '@' :: _ - | '^' :: _ -> Some (INFIX_AT_HAT_OP s) - | '+' :: _ - | '-' :: _ -> Some (PLUS_MINUS_OP s) - | '*' :: '*' :: _ -> Some (INFIX_STAR_STAR_OP s) - | '*' :: _ - | '/' :: _ - | '%' :: _ -> Some (INFIX_STAR_DIV_MOD_OP s) - | _ -> None - Some([| for _c in angles do yield GREATER |], afterOp) + if not (txt.StartsWith ">") then + ValueNone + else + match txt.AsSpan().IndexOfAnyExcept '>' with + | -1 -> ValueSome(struct (Array.init txt.Length (fun _ -> GREATER), ValueNone)) + | angles -> + let afterAngles = txt.AsSpan angles + + let afterOp = + match afterAngles with + | Equals "." -> ValueSome DOT + | Equals "]" -> ValueSome RBRACK + | Equals "-" -> ValueSome MINUS + | Equals ".." -> ValueSome DOT_DOT + | Equals "?" -> ValueSome QMARK + | Equals "??" -> ValueSome QMARK_QMARK + | Equals ":=" -> ValueSome COLON_EQUALS + | Equals "::" -> ValueSome COLON_COLON + | Equals "*" -> ValueSome STAR + | Equals "&" -> ValueSome AMP + | Equals "->" -> ValueSome RARROW + | Equals "<-" -> ValueSome LARROW + | Equals "=" -> ValueSome EQUALS + | Equals "<" -> ValueSome (LESS false) + | Equals "$" -> ValueSome DOLLAR + | Equals "%" -> ValueSome (PERCENT_OP "%") + | Equals "%%" -> ValueSome (PERCENT_OP "%%") + | StartsWith "=" + | StartsWith "!=" + | StartsWith "<" + | StartsWith ">" + | StartsWith "$" -> ValueSome (INFIX_COMPARE_OP (afterAngles.ToString())) + | StartsWith "&" -> ValueSome (INFIX_AMP_OP (afterAngles.ToString())) + | StartsWith "|" -> ValueSome (INFIX_BAR_OP (afterAngles.ToString())) + | StartsWith "!" + | StartsWith "?" + | StartsWith "~" -> ValueSome (PREFIX_OP (afterAngles.ToString())) + | StartsWith "@" + | StartsWith "^" -> ValueSome (INFIX_AT_HAT_OP (afterAngles.ToString())) + | StartsWith "+" + | StartsWith "-" -> ValueSome (PLUS_MINUS_OP (afterAngles.ToString())) + | StartsWith "**" -> ValueSome (INFIX_STAR_STAR_OP (afterAngles.ToString())) + | StartsWith "*" + | StartsWith "/" + | StartsWith "%" -> ValueSome (INFIX_STAR_DIV_MOD_OP (afterAngles.ToString())) + | _ -> ValueNone + + ValueSome(struct (Array.init angles (fun _ -> GREATER), afterOp)) [] type PositionWithColumn = @@ -1196,8 +1211,8 @@ type LexFilterImpl ( pool.Return tokenTup | INFIX_COMPARE_OP (TyparsCloseOp(greaters, afterOp) as opstr) -> match afterOp with - | None -> () - | Some tok -> delayToken (pool.UseShiftedLocation(tokenTup, tok, greaters.Length, 0)) + | ValueNone -> () + | ValueSome tok -> delayToken (pool.UseShiftedLocation(tokenTup, tok, greaters.Length, 0)) for i = greaters.Length - 1 downto 0 do delayToken (pool.UseShiftedLocation(tokenTup, greaters[i] res, i, -opstr.Length + i + 1)) pool.Return tokenTup diff --git a/src/Compiler/SyntaxTree/LexFilter.fsi b/src/Compiler/SyntaxTree/LexFilter.fsi index 319fd5ecd90..7d39b8325df 100644 --- a/src/Compiler/SyntaxTree/LexFilter.fsi +++ b/src/Compiler/SyntaxTree/LexFilter.fsi @@ -10,7 +10,8 @@ open FSharp.Compiler.Parser /// Match the close of '>' of a set of type parameters. /// This is done for tokens such as '>>' by smashing the token -val (|TyparsCloseOp|_|): txt: string -> ((bool -> token)[] * token option) option +[] +val (|TyparsCloseOp|_|): txt: string -> struct ((bool -> token)[] * token voption) voption /// A stateful filter over the token stream that adjusts it for indentation-aware syntax rules /// Process the token stream prior to parsing. Implements the offside rule and other lexical transformations. diff --git a/src/Compiler/SyntaxTree/ParseHelpers.fs b/src/Compiler/SyntaxTree/ParseHelpers.fs index bb2e7755767..cd4b41787e1 100644 --- a/src/Compiler/SyntaxTree/ParseHelpers.fs +++ b/src/Compiler/SyntaxTree/ParseHelpers.fs @@ -1159,7 +1159,7 @@ let mkSynField let mType, mStart = idOpt - |> Option.map _.idRange + |> Option.map (fun x -> x.idRange) |> Option.orElseWith (fun _ -> vis |> Option.map (fun v -> v.Range)) |> Option.orElse isMutable |> Option.orElseWith (fun _ -> leadingKeyword |> Option.map (fun k -> k.Range)) diff --git a/src/Compiler/SyntaxTree/SyntaxTree.fs b/src/Compiler/SyntaxTree/SyntaxTree.fs index 97f40bee62c..8b034740be4 100644 --- a/src/Compiler/SyntaxTree/SyntaxTree.fs +++ b/src/Compiler/SyntaxTree/SyntaxTree.fs @@ -1184,7 +1184,7 @@ type SynTypeDefnSimpleRepr = fields: SynField list * isConcrete: bool * isIncrClass: bool * - implicitCtorSynPats: SynSimplePats option * + implicitCtorSynPats: SynPat option * range: range | LibraryOnlyILAssembly of @@ -1433,7 +1433,7 @@ type SynMemberDefn = | ImplicitCtor of accessibility: SynAccess option * attributes: SynAttributes * - ctorArgs: SynSimplePats * + ctorArgs: SynPat * selfIdentifier: Ident option * xmlDoc: PreXmlDoc * range: range * diff --git a/src/Compiler/SyntaxTree/SyntaxTree.fsi b/src/Compiler/SyntaxTree/SyntaxTree.fsi index 72e4a9d216a..3d9ef06dcad 100644 --- a/src/Compiler/SyntaxTree/SyntaxTree.fsi +++ b/src/Compiler/SyntaxTree/SyntaxTree.fsi @@ -1343,7 +1343,7 @@ type SynTypeDefnSimpleRepr = fields: SynField list * isConcrete: bool * isIncrClass: bool * - implicitCtorSynPats: SynSimplePats option * + implicitCtorSynPats: SynPat option * range: range /// A type defined by using an IL assembly representation. Only used in FSharp.Core. @@ -1614,7 +1614,7 @@ type SynMemberDefn = | ImplicitCtor of accessibility: SynAccess option * attributes: SynAttributes * - ctorArgs: SynSimplePats * + ctorArgs: SynPat * selfIdentifier: Ident option * xmlDoc: PreXmlDoc * range: range * diff --git a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs index d44395dbcc1..d6c65234237 100644 --- a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs +++ b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs @@ -104,67 +104,76 @@ let rec pushUnaryArg expr arg = errorR (Error(FSComp.SR.tcDotLambdaAtNotSupportedExpression (), expr.Range)) expr +[] let (|SynSingleIdent|_|) x = match x with - | SynLongIdent([ id ], _, _) -> Some id - | _ -> None + | SynLongIdent([ id ], _, _) -> ValueSome id + | _ -> ValueNone /// Match a long identifier, including the case for single identifiers which gets a more optimized node in the syntax tree. +[] let (|LongOrSingleIdent|_|) inp = match inp with - | SynExpr.LongIdent(isOpt, lidwd, altId, _m) -> Some(isOpt, lidwd, altId, lidwd.RangeWithoutAnyExtraDot) - | SynExpr.Ident id -> Some(false, SynLongIdent([ id ], [], [ None ]), None, id.idRange) + | SynExpr.LongIdent(isOpt, lidwd, altId, _m) -> ValueSome(isOpt, lidwd, altId, lidwd.RangeWithoutAnyExtraDot) + | SynExpr.Ident id -> ValueSome(false, SynLongIdent([ id ], [], [ None ]), None, id.idRange) | SynExpr.DiscardAfterMissingQualificationAfterDot(synExpr, dotRange, _) -> match synExpr with - | SynExpr.Ident ident -> Some(false, SynLongIdent([ ident ], [ dotRange ], [ None ]), None, ident.idRange) + | SynExpr.Ident ident -> ValueSome(false, SynLongIdent([ ident ], [ dotRange ], [ None ]), None, ident.idRange) | SynExpr.LongIdent(false, SynLongIdent(idents, dotRanges, trivia), _, range) -> - Some(false, SynLongIdent(idents, dotRanges @ [ dotRange ], trivia), None, range) - | _ -> None + ValueSome(false, SynLongIdent(idents, dotRanges @ [ dotRange ], trivia), None, range) + | _ -> ValueNone - | _ -> None + | _ -> ValueNone +[] let (|SingleIdent|_|) inp = match inp with - | SynExpr.LongIdent(false, SynSingleIdent(id), None, _) -> Some id - | SynExpr.Ident id -> Some id - | _ -> None + | SynExpr.LongIdent(false, SynSingleIdent(id), None, _) -> ValueSome id + | SynExpr.Ident id -> ValueSome id + | _ -> ValueNone +[] let (|SynBinOp|_|) input = match input with | SynExpr.App(ExprAtomicFlag.NonAtomic, false, SynExpr.App(ExprAtomicFlag.NonAtomic, true, SynExpr.LongIdent(longDotId = SynLongIdent(id = [ synId ])), x1, _m1), x2, - _m2) -> Some(synId, x1, x2) - | _ -> None + _m2) -> ValueSome(synId, x1, x2) + | _ -> ValueNone +[] let (|SynPipeRight|_|) input = match input with - | SynBinOp(synId, x1, x2) when synId.idText = "op_PipeRight" -> Some(x1, x2) - | _ -> None + | SynBinOp(synId, x1, x2) when synId.idText = "op_PipeRight" -> ValueSome(x1, x2) + | _ -> ValueNone +[] let (|SynPipeRight2|_|) input = match input with | SynBinOp(synId, SynExpr.Paren(SynExpr.Tuple(false, [ x1a; x1b ], _, _), _, _, _), x2) when synId.idText = "op_PipeRight2" -> - Some(x1a, x1b, x2) - | _ -> None + ValueSome(x1a, x1b, x2) + | _ -> ValueNone +[] let (|SynPipeRight3|_|) input = match input with | SynBinOp(synId, SynExpr.Paren(SynExpr.Tuple(false, [ x1a; x1b; x1c ], _, _), _, _, _), x2) when synId.idText = "op_PipeRight3" -> - Some(x1a, x1b, x1c, x2) - | _ -> None + ValueSome(x1a, x1b, x1c, x2) + | _ -> ValueNone +[] let (|SynAndAlso|_|) input = match input with - | SynBinOp(synId, x1, x2) when synId.idText = "op_BooleanAnd" -> Some(x1, x2) - | _ -> None + | SynBinOp(synId, x1, x2) when synId.idText = "op_BooleanAnd" -> ValueSome(x1, x2) + | _ -> ValueNone +[] let (|SynOrElse|_|) input = match input with - | SynBinOp(synId, x1, x2) when synId.idText = "op_BooleanOr" -> Some(x1, x2) - | _ -> None + | SynBinOp(synId, x1, x2) when synId.idText = "op_BooleanOr" -> ValueSome(x1, x2) + | _ -> ValueNone /// This affects placement of debug points let rec IsControlFlowExpression e = @@ -237,26 +246,29 @@ let mkSynPatMaybeVar lidwd vis m = SynPat.LongIdent(lidwd, None, None, SynArgPats.Pats [], vis, m) /// Extract the argument for patterns corresponding to the declaration of 'new ... = ...' +[] let (|SynPatForConstructorDecl|_|) x = match x with - | SynPat.LongIdent(longDotId = SynSingleIdent _; argPats = SynArgPats.Pats [ arg ]) -> Some arg - | _ -> None + | SynPat.LongIdent(longDotId = SynSingleIdent _; argPats = SynArgPats.Pats [ arg ]) -> ValueSome arg + | _ -> ValueNone /// Recognize the '()' in 'new()' +[] let (|SynPatForNullaryArgs|_|) x = match x with - | SynPat.Paren(SynPat.Const(SynConst.Unit, _), _) -> Some() - | _ -> None + | SynPat.Paren(SynPat.Const(SynConst.Unit, _), _) -> ValueSome() + | _ -> ValueNone let (|SynExprErrorSkip|) (p: SynExpr) = match p with | SynExpr.FromParseError(p, _) -> p | _ -> p +[] let (|SynExprParen|_|) (e: SynExpr) = match e with - | SynExpr.Paren(SynExprErrorSkip e, a, b, c) -> Some(e, a, b, c) - | _ -> None + | SynExpr.Paren(SynExprErrorSkip e, a, b, c) -> ValueSome(e, a, b, c) + | _ -> ValueNone let (|SynPatErrorSkip|) (p: SynPat) = match p with @@ -1025,6 +1037,7 @@ let getTypeFromTuplePath (path: SynTupleTypeSegment list) : SynType list = | SynTupleTypeSegment.Type t -> Some t | _ -> None) +[] let (|MultiDimensionArrayType|_|) (t: SynType) = match t with | SynType.App(StripParenTypes(SynType.LongIdent(SynLongIdent([ identifier ], _, _))), _, [ elementType ], _, _, true, m) -> @@ -1036,10 +1049,10 @@ let (|MultiDimensionArrayType|_|) (t: SynType) = |> System.String |> int - Some(rank, elementType, m) + ValueSome(rank, elementType, m) else - None - | _ -> None + ValueNone + | _ -> ValueNone let (|TypesForTypar|) (t: SynType) = let rec visit continuation t = diff --git a/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi b/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi index 03fc0be628d..5ee198564ed 100644 --- a/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi +++ b/src/Compiler/SyntaxTree/SyntaxTreeOps.fsi @@ -44,10 +44,12 @@ val mkSynCompGenSimplePatVar: id: Ident -> SynSimplePat val pushUnaryArg: expr: SynExpr -> arg: Ident -> SynExpr /// Match a long identifier, including the case for single identifiers which gets a more optimized node in the syntax tree. +[] val (|LongOrSingleIdent|_|): - inp: SynExpr -> (bool * SynLongIdent * SynSimplePatAlternativeIdInfo ref option * range) option + inp: SynExpr -> (bool * SynLongIdent * SynSimplePatAlternativeIdInfo ref option * range) voption -val (|SingleIdent|_|): inp: SynExpr -> Ident option +[] +val (|SingleIdent|_|): inp: SynExpr -> Ident voption /// This affects placement of debug points val IsControlFlowExpression: e: SynExpr -> bool @@ -66,14 +68,17 @@ val mkSynThisPatVar: id: Ident -> SynPat val mkSynPatMaybeVar: lidwd: SynLongIdent -> vis: SynAccess option -> m: range -> SynPat -val (|SynPatForConstructorDecl|_|): x: SynPat -> SynPat option +[] +val (|SynPatForConstructorDecl|_|): x: SynPat -> SynPat voption /// Recognize the '()' in 'new()' -val (|SynPatForNullaryArgs|_|): x: SynPat -> unit option +[] +val (|SynPatForNullaryArgs|_|): x: SynPat -> unit voption val (|SynExprErrorSkip|): p: SynExpr -> SynExpr -val (|SynExprParen|_|): e: SynExpr -> (SynExpr * range * range option * range) option +[] +val (|SynExprParen|_|): e: SynExpr -> (SynExpr * range * range option * range) voption val (|SynPatErrorSkip|): p: SynPat -> SynPat @@ -317,19 +322,24 @@ val synExprContainsError: inpExpr: SynExpr -> bool val (|ParsedHashDirectiveArguments|): ParsedHashDirectiveArgument list -> string list /// 'e1 && e2' -val (|SynAndAlso|_|): SynExpr -> (SynExpr * SynExpr) option +[] +val (|SynAndAlso|_|): SynExpr -> (SynExpr * SynExpr) voption /// 'e1 || e2' -val (|SynOrElse|_|): SynExpr -> (SynExpr * SynExpr) option +[] +val (|SynOrElse|_|): SynExpr -> (SynExpr * SynExpr) voption /// 'e1 |> e2' -val (|SynPipeRight|_|): SynExpr -> (SynExpr * SynExpr) option +[] +val (|SynPipeRight|_|): SynExpr -> (SynExpr * SynExpr) voption /// 'e1 ||> e2' -val (|SynPipeRight2|_|): SynExpr -> (SynExpr * SynExpr * SynExpr) option +[] +val (|SynPipeRight2|_|): SynExpr -> (SynExpr * SynExpr * SynExpr) voption /// 'e1 |||> e2' -val (|SynPipeRight3|_|): SynExpr -> (SynExpr * SynExpr * SynExpr * SynExpr) option +[] +val (|SynPipeRight3|_|): SynExpr -> (SynExpr * SynExpr * SynExpr * SynExpr) voption val prependIdentInLongIdentWithTrivia: ident: SynIdent -> mDot: range -> lid: SynLongIdent -> SynLongIdent @@ -341,6 +351,7 @@ val desugarGetSetMembers: memberDefns: SynMemberDefns -> SynMemberDefns val getTypeFromTuplePath: path: SynTupleTypeSegment list -> SynType list -val (|MultiDimensionArrayType|_|): t: SynType -> (int * SynType * range) option +[] +val (|MultiDimensionArrayType|_|): t: SynType -> (int * SynType * range) voption val (|TypesForTypar|): t: SynType -> SynType list diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index 0bbee0c3e55..3830dd008e5 100644 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -1852,11 +1852,11 @@ type TcGlobals( member _.DebuggerBrowsableNeverAttribute = debuggerBrowsableNeverAttribute - member _.mkDebuggableAttributeV2(jitTracking, jitOptimizerDisabled, enableEnC) = + member _.mkDebuggableAttributeV2(jitTracking, jitOptimizerDisabled) = let debuggingMode = + 0x3 (* Default ||| IgnoreSymbolStoreSequencePoints *) ||| (if jitTracking then 1 else 0) ||| - (if jitOptimizerDisabled then 256 else 0) ||| - (if enableEnC then 4 else 0) + (if jitOptimizerDisabled then 256 else 0) let tref_DebuggableAttribute_DebuggingModes = mkILTyRefInTyRef (tref_DebuggableAttribute, tname_DebuggableAttribute_DebuggingModes) mkILCustomAttribute (tref_DebuggableAttribute, [mkILNonGenericValueTy tref_DebuggableAttribute_DebuggingModes], diff --git a/src/Compiler/TypedTree/TypedTree.fs b/src/Compiler/TypedTree/TypedTree.fs index 2109361291e..da8e43bfba5 100644 --- a/src/Compiler/TypedTree/TypedTree.fs +++ b/src/Compiler/TypedTree/TypedTree.fs @@ -4565,6 +4565,17 @@ type DecisionTreeCase = member x.DebugText = x.ToString() override x.ToString() = sprintf "DecisionTreeCase(...)" + +[] +type ActivePatternReturnKind = + | RefTypeWrapper + | StructTypeWrapper + | Boolean + member this.IsStruct with get () = + match this with + | RefTypeWrapper -> false + | StructTypeWrapper + | Boolean -> true [] type DecisionTreeTest = @@ -4585,20 +4596,20 @@ type DecisionTreeTest = /// Test if the input to a decision tree is an instance of the given type | IsInst of source: TType * target: TType - /// Test.ActivePatternCase(activePatExpr, activePatResTys, isStructRetTy, activePatIdentity, idx, activePatInfo) + /// Test.ActivePatternCase(activePatExpr, activePatResTys, activePatRetKind, activePatIdentity, idx, activePatInfo) /// /// Run the active pattern and bind a successful result to a /// variable in the remaining tree. /// activePatExpr -- The active pattern function being called, perhaps applied to some active pattern parameters. /// activePatResTys -- The result types (case types) of the active pattern. - /// isStructRetTy -- Is the active pattern a struct return + /// activePatRetKind -- Indicating what is returning from the active pattern /// activePatIdentity -- The value and the types it is applied to. If there are any active pattern parameters then this is empty. /// idx -- The case number of the active pattern which the test relates to. /// activePatternInfo -- The extracted info for the active pattern. | ActivePatternCase of activePatExpr: Expr * activePatResTys: TTypes * - isStructRetTy: bool * + activePatRetKind: ActivePatternReturnKind * activePatIdentity: (ValRef * TypeInst) option * idx: int * activePatternInfo: ActivePatternInfo @@ -4667,7 +4678,7 @@ type ActivePatternElemRef = activePatternInfo: ActivePatternInfo * activePatternVal: ValRef * caseIndex: int * - isStructRetTy: bool + activePatRetKind: ActivePatternReturnKind /// Get the full information about the active pattern being referred to member x.ActivePatternInfo = (let (APElemRef(info, _, _, _)) = x in info) @@ -4676,7 +4687,7 @@ type ActivePatternElemRef = member x.ActivePatternVal = (let (APElemRef(_, vref, _, _)) = x in vref) /// Get a reference to the value for the active pattern being referred to - member x.IsStructReturn = (let (APElemRef(_, _, _, isStructRetTy)) = x in isStructRetTy) + member x.ActivePatternRetKind = (let (APElemRef(_, _, _, activePatRetKind)) = x in activePatRetKind) /// Get the index of the active pattern element within the overall active pattern member x.CaseIndex = (let (APElemRef(_, _, n, _)) = x in n) diff --git a/src/Compiler/TypedTree/TypedTree.fsi b/src/Compiler/TypedTree/TypedTree.fsi index 3eb47b5eb47..7f0a0ef4ed3 100644 --- a/src/Compiler/TypedTree/TypedTree.fsi +++ b/src/Compiler/TypedTree/TypedTree.fsi @@ -3267,6 +3267,18 @@ type DecisionTreeCase = /// Get the discriminator associated with the case member Discriminator: DecisionTreeTest +/// Indicating what is returning from an AP +[] +type ActivePatternReturnKind = + /// Returning `_ option` or `Choice<_, _, .., _>` + | RefTypeWrapper + /// Returning `_ voption` + | StructTypeWrapper + /// Returning bool + | Boolean + + member IsStruct: bool + [] type DecisionTreeTest = @@ -3287,20 +3299,20 @@ type DecisionTreeTest = /// Test if the input to a decision tree is an instance of the given type | IsInst of source: TType * target: TType - /// Test.ActivePatternCase(activePatExpr, activePatResTys, isStructRetTy, activePatIdentity, idx, activePatInfo) + /// Test.ActivePatternCase(activePatExpr, activePatResTys, activePatRetKind, activePatIdentity, idx, activePatInfo) /// /// Run the active pattern type bind a successful result to a /// variable in the remaining tree. /// activePatExpr -- The active pattern function being called, perhaps applied to some active pattern parameters. /// activePatResTys -- The result types (case types) of the active pattern. - /// isStructRetTy -- Is the active pattern a struct return + /// activePatRetKind -- Indicating what is returning from the active pattern /// activePatIdentity -- The value type the types it is applied to. If there are any active pattern parameters then this is empty. /// idx -- The case number of the active pattern which the test relates to. /// activePatternInfo -- The extracted info for the active pattern. | ActivePatternCase of activePatExpr: Expr * activePatResTys: TTypes * - isStructRetTy: bool * + activePatRetKind: ActivePatternReturnKind * activePatIdentity: (ValRef * TypeInst) option * idx: int * activePatternInfo: Syntax.PrettyNaming.ActivePatternInfo @@ -3359,7 +3371,7 @@ type ActivePatternElemRef = activePatternInfo: Syntax.PrettyNaming.ActivePatternInfo * activePatternVal: ValRef * caseIndex: int * - isStructRetTy: bool + activePatRetKind: ActivePatternReturnKind override ToString: unit -> string @@ -3376,7 +3388,7 @@ type ActivePatternElemRef = member DebugText: string /// Get a reference to the value for the active pattern being referred to - member IsStructReturn: bool + member ActivePatternRetKind: ActivePatternReturnKind /// Records the "extra information" for a value compiled as a method (rather /// than a closure or a local), including argument names, attributes etc. diff --git a/src/Compiler/TypedTree/TypedTreeBasics.fs b/src/Compiler/TypedTree/TypedTreeBasics.fs index 651eadd0d65..c71994685d3 100644 --- a/src/Compiler/TypedTree/TypedTreeBasics.fs +++ b/src/Compiler/TypedTree/TypedTreeBasics.fs @@ -265,7 +265,7 @@ let stripUnitEqns unt = stripUnitEqnsAux false unt /// Detect a use of a nominal type, including type abbreviations. let (|AbbrevOrAppTy|_|) (ty: TType) = match stripTyparEqns ty with - | TType_app (tcref, _, _) -> Some tcref + | TType_app (tcref, tinst, _) -> Some(tcref, tinst) | _ -> None //--------------------------------------------------------------------------- diff --git a/src/Compiler/TypedTree/TypedTreeBasics.fsi b/src/Compiler/TypedTree/TypedTreeBasics.fsi index 8a73a609316..ea1f5979262 100644 --- a/src/Compiler/TypedTree/TypedTreeBasics.fsi +++ b/src/Compiler/TypedTree/TypedTreeBasics.fsi @@ -135,7 +135,7 @@ val stripTyparEqns: ty: TType -> TType val stripUnitEqns: unt: Measure -> Measure /// Detect a use of a nominal type, including type abbreviations. -val (|AbbrevOrAppTy|_|): ty: TType -> TyconRef option +val (|AbbrevOrAppTy|_|): ty: TType -> (TyconRef * TypeInst) option val mkLocalValRef: v: Val -> ValRef diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 8616a7e43fa..8ef6ce62182 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -3597,7 +3597,7 @@ let isSpanTyconRef g m tcref = let isSpanTy g m ty = ty |> stripTyEqns g |> (function TType_app(tcref, _, _) -> isSpanTyconRef g m tcref | _ -> false) -let rec tryDestSpanTy g m ty = +let tryDestSpanTy g m ty = match tryAppTy g ty with | ValueSome(tcref, [ty]) when isSpanTyconRef g m tcref -> Some(tcref, ty) | _ -> None @@ -3672,6 +3672,13 @@ let mkNullableTy (g: TcGlobals) ty = TType_app (g.system_Nullable_tcref, [ty], g let mkListTy (g: TcGlobals) ty = TType_app (g.list_tcr_nice, [ty], g.knownWithoutNull) +let isBoolTy (g: TcGlobals) ty = + match tryTcrefOfAppTy g ty with + | ValueNone -> false + | ValueSome tcref -> + tyconRefEq g g.system_Bool_tcref tcref || + tyconRefEq g g.bool_tcr tcref + let isValueOptionTy (g: TcGlobals) ty = match tryTcrefOfAppTy g ty with | ValueNone -> false @@ -4626,11 +4633,11 @@ module DebugPrint = let body = moduleOrNamespaceTypeL ms.ModuleOrNamespaceType (header @@-- body) @@ footer - let rec implFilesL implFiles = - aboveListL (List.map implFileL implFiles) - - and implFileL (CheckedImplFile (signature=implFileTy; contents=implFileContents)) = + let implFileL (CheckedImplFile (signature=implFileTy; contents=implFileContents)) = aboveListL [(wordL(tagText "top implementation ")) @@-- mexprL implFileTy implFileContents] + + let implFilesL implFiles = + aboveListL (List.map implFileL implFiles) let showType x = showL (typeL x) @@ -5097,6 +5104,33 @@ let tryGetFreeVarsCacheValue opts cache = if opts.canCache then tryGetCacheValue cache else ValueNone +let accFreeLocalVal opts v fvs = + if not opts.includeLocals then fvs else + if Zset.contains v fvs.FreeLocals then fvs + else + let fvs = accFreevarsInVal opts v fvs + {fvs with FreeLocals=Zset.add v fvs.FreeLocals} + +let accFreeInValFlags opts flag acc = + let isMethLocal = + match flag with + | VSlotDirectCall + | CtorValUsedAsSelfInit + | CtorValUsedAsSuperInit -> true + | PossibleConstrainedCall _ + | NormalValUse -> false + let acc = accUsesFunctionLocalConstructs isMethLocal acc + match flag with + | PossibleConstrainedCall ty -> accFreeTyvars opts accFreeInType ty acc + | _ -> acc + +let accLocalTyconRepr opts b fvs = + if not opts.includeLocalTyconReprs then fvs else + if Zset.contains b fvs.FreeLocalTyconReprs then fvs + else { fvs with FreeLocalTyconReprs = Zset.add b fvs.FreeLocalTyconReprs } + +let inline accFreeExnRef _exnc fvs = fvs // Note: this exnc (TyconRef) should be collected the surround types, e.g. tinst of Expr.Op + let rec accBindRhs opts (TBind(_, repr, _)) acc = accFreeInExpr opts repr acc and accFreeInSwitchCases opts csl dflt (acc: FreeVars) = @@ -5123,31 +5157,6 @@ and accFreeInDecisionTree opts x (acc: FreeVars) = | TDSwitch(e1, csl, dflt, _) -> accFreeInExpr opts e1 (accFreeInSwitchCases opts csl dflt acc) | TDSuccess (es, _) -> accFreeInFlatExprs opts es acc | TDBind (bind, body) -> unionFreeVars (bindLhs opts bind (accBindRhs opts bind (freeInDecisionTree opts body))) acc - -and accFreeInValFlags opts flag acc = - let isMethLocal = - match flag with - | VSlotDirectCall - | CtorValUsedAsSelfInit - | CtorValUsedAsSuperInit -> true - | PossibleConstrainedCall _ - | NormalValUse -> false - let acc = accUsesFunctionLocalConstructs isMethLocal acc - match flag with - | PossibleConstrainedCall ty -> accFreeTyvars opts accFreeInType ty acc - | _ -> acc - -and accFreeLocalVal opts v fvs = - if not opts.includeLocals then fvs else - if Zset.contains v fvs.FreeLocals then fvs - else - let fvs = accFreevarsInVal opts v fvs - {fvs with FreeLocals=Zset.add v fvs.FreeLocals} - -and accLocalTyconRepr opts b fvs = - if not opts.includeLocalTyconReprs then fvs else - if Zset.contains b fvs.FreeLocalTyconReprs then fvs - else { fvs with FreeLocalTyconReprs = Zset.add b fvs.FreeLocalTyconReprs } and accUsedRecdOrUnionTyconRepr opts (tc: Tycon) fvs = if (match tc.TypeReprInfo with TFSharpTyconRepr _ -> true | _ -> false) then @@ -5170,8 +5179,7 @@ and accFreeRecdFieldRef opts rfref fvs = let fvs = fvs |> accUsedRecdOrUnionTyconRepr opts rfref.Tycon let fvs = fvs |> accFreevarsInTycon opts rfref.TyconRef { fvs with FreeRecdFields = Zset.add rfref fvs.FreeRecdFields } - -and accFreeExnRef _exnc fvs = fvs // Note: this exnc (TyconRef) should be collected the surround types, e.g. tinst of Expr.Op + and accFreeValRef opts (vref: ValRef) fvs = match vref.IsLocalRef with | true -> accFreeLocalVal opts vref.ResolvedTarget fvs @@ -6609,7 +6617,7 @@ let isExpansiveUnderInstantiation g fty0 tyargs pargs argsl = | _ :: t -> not (isFunTy g fty) || loop (rangeOfFunTy g fty) t loop fty1 argsl) -let rec mkExprAppAux g f fty argsl m = +let mkExprAppAux g f fty argsl m = match argsl with | [] -> f | _ -> @@ -6780,7 +6788,7 @@ let foldLinearBindingTargetsOfMatch tree (targets: _[]) = treeR, targetsR // Simplify a little as we go, including dead target elimination -let rec simplifyTrivialMatch spBind mExpr mMatch ty tree (targets : _[]) = +let simplifyTrivialMatch spBind mExpr mMatch ty tree (targets : _[]) = match tree with | TDSuccess(es, n) -> if n >= targets.Length then failwith "simplifyTrivialMatch: target out of range" @@ -9227,14 +9235,17 @@ type ActivePatternInfo with member x.DisplayNameByIdx idx = x.ActiveTags[idx] |> ConvertLogicalNameToDisplayName - member apinfo.ResultType g m retTys isStruct = + member apinfo.ResultType g m retTys retKind = let choicety = mkChoiceTy g m retTys if apinfo.IsTotal then choicety - elif isStruct then mkValueOptionTy g choicety - else mkOptionTy g choicety + else + match retKind with + | ActivePatternReturnKind.RefTypeWrapper -> mkOptionTy g choicety + | ActivePatternReturnKind.StructTypeWrapper -> mkValueOptionTy g choicety + | ActivePatternReturnKind.Boolean -> g.bool_ty - member apinfo.OverallType g m argTy retTys isStruct = - mkFunTy g argTy (apinfo.ResultType g m retTys isStruct) + member apinfo.OverallType g m argTy retTys retKind = + mkFunTy g argTy (apinfo.ResultType g m retTys retKind) //--------------------------------------------------------------------------- // Active pattern validation @@ -10722,7 +10733,7 @@ let rec serializeNode (writer: IndentedTextWriter) (addTrailingComma:bool) (node else writer.WriteLine("}") -let rec serializeEntity path (entity: Entity) = +let serializeEntity path (entity: Entity) = let root = visitEntity entity use sw = new System.IO.StringWriter() use writer = new IndentedTextWriter(sw) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index 2345ac5eb40..bec1e031185 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -1528,6 +1528,9 @@ val mkVoidPtrTy: TcGlobals -> TType /// Build a single-dimensional array type val mkArrayType: TcGlobals -> TType -> TType +/// Determine if a type is a bool type +val isBoolTy: TcGlobals -> TType -> bool + /// Determine if a type is a value option type val isValueOptionTy: TcGlobals -> TType -> bool @@ -2449,10 +2452,11 @@ type PrettyNaming.ActivePatternInfo with member DisplayNameByIdx: idx: int -> string /// Get the result type for the active pattern - member ResultType: g: TcGlobals -> range -> TType list -> bool -> TType + member ResultType: g: TcGlobals -> range -> TType list -> ActivePatternReturnKind -> TType /// Get the overall type for a function that implements the active pattern - member OverallType: g: TcGlobals -> m: range -> argTy: TType -> retTys: TType list -> isStruct: bool -> TType + member OverallType: + g: TcGlobals -> m: range -> argTy: TType -> retTys: TType list -> retKind: ActivePatternReturnKind -> TType val doesActivePatternHaveFreeTypars: TcGlobals -> ValRef -> bool diff --git a/src/Compiler/Utilities/Activity.fs b/src/Compiler/Utilities/Activity.fs index 79697eae494..5f1d9c3354f 100644 --- a/src/Compiler/Utilities/Activity.fs +++ b/src/Compiler/Utilities/Activity.fs @@ -33,6 +33,7 @@ module internal Activity = let gc2 = "gc2" let outputDllFile = "outputDllFile" let buildPhase = "buildPhase" + let version = "version" let AllKnownTags = [| diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index 94784c97f00..afce0f3b554 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -1,5 +1,4 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - namespace FSharp.Compiler.Diagnostics open System @@ -29,6 +28,7 @@ module internal Activity = val length: string val cache: string val buildPhase: string + val version: string module Events = val cacheHit: string diff --git a/src/Compiler/Utilities/Cancellable.fs b/src/Compiler/Utilities/Cancellable.fs index c702e3b7a0b..59e7def4c10 100644 --- a/src/Compiler/Utilities/Cancellable.fs +++ b/src/Compiler/Utilities/Cancellable.fs @@ -2,32 +2,47 @@ namespace FSharp.Compiler open System open System.Threading +open Internal.Utilities.Library [] type Cancellable = [] - static val mutable private token: CancellationToken - - static member UsingToken(ct) = - let oldCt = Cancellable.token - - Cancellable.token <- ct + static val mutable private tokens: CancellationToken list + static let disposable = { new IDisposable with - member this.Dispose() = Cancellable.token <- oldCt + member this.Dispose() = + Cancellable.Tokens <- Cancellable.Tokens |> List.tail } - static member Token - with get () = Cancellable.token - and internal set v = Cancellable.token <- v + static member Tokens + with private get () = + match box Cancellable.tokens with + | Null -> [] + | _ -> Cancellable.tokens + and private set v = Cancellable.tokens <- v + + static member UsingToken(ct) = + Cancellable.Tokens <- ct :: Cancellable.Tokens + disposable + + static member Token = + match Cancellable.Tokens with + | [] -> CancellationToken.None + | token :: _ -> token + /// There may be multiple tokens if `UsingToken` is called multiple times, producing scoped structure. + /// We're interested in the current, i.e. the most recent, one. static member CheckAndThrow() = - Cancellable.token.ThrowIfCancellationRequested() + match Cancellable.Tokens with + | [] -> () + | token :: _ -> token.ThrowIfCancellationRequested() namespace Internal.Utilities.Library open System open System.Threading +open FSharp.Compiler #if !FSHARPCORE_USE_PACKAGE open FSharp.Core.CompilerServices.StateMachineHelpers @@ -48,6 +63,7 @@ module Cancellable = ValueOrCancelled.Cancelled(OperationCanceledException ct) else try + use _ = Cancellable.UsingToken(ct) oper ct with :? OperationCanceledException as e -> ValueOrCancelled.Cancelled(OperationCanceledException e.CancellationToken) diff --git a/src/Compiler/Utilities/Cancellable.fsi b/src/Compiler/Utilities/Cancellable.fsi index 6e36d7ecb6d..23515432bdd 100644 --- a/src/Compiler/Utilities/Cancellable.fsi +++ b/src/Compiler/Utilities/Cancellable.fsi @@ -7,7 +7,6 @@ open System.Threading type Cancellable = static member internal UsingToken: CancellationToken -> IDisposable static member Token: CancellationToken - static member internal Token: CancellationToken with set static member CheckAndThrow: unit -> unit namespace Internal.Utilities.Library diff --git a/src/Compiler/Utilities/LruCache.fs b/src/Compiler/Utilities/LruCache.fs new file mode 100644 index 00000000000..c75ed1d88cf --- /dev/null +++ b/src/Compiler/Utilities/LruCache.fs @@ -0,0 +1,273 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Internal.Utilities.Collections + +open System +open System.Collections.Generic +open System.Diagnostics + +open Internal.Utilities.Library.Extras + +[] +type internal CacheEvent = + | Evicted + | Collected + | Weakened + | Strengthened + | Cleared + +[] +type internal ValueLink<'T when 'T: not struct> = + | Strong of 'T + | Weak of WeakReference<'T> + +[] +type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality and 'TValue: not struct> + (keepStrongly, ?keepWeakly, ?requiredToKeep, ?event) = + + let keepWeakly = defaultArg keepWeakly 100 + let requiredToKeep = defaultArg requiredToKeep (fun _ -> false) + let event = defaultArg event (fun _ _ -> ()) + + let dictionary = Dictionary<'TKey, Dictionary<'TVersion, _>>() + + // Lists to keep track of when items were last accessed. First item is most recently accessed. + let strongList = LinkedList<'TKey * 'TVersion * string * ValueLink<'TValue>>() + let weakList = LinkedList<'TKey * 'TVersion * string * ValueLink<'TValue>>() + + let rec removeCollected (node: LinkedListNode<_>) = + if node <> null then + let key, version, label, value = node.Value + + match value with + | Weak w -> + let next = node.Next + + match w.TryGetTarget() with + | false, _ -> + weakList.Remove node + dictionary[key].Remove version |> ignore + + if dictionary[key].Count = 0 then + dictionary.Remove key |> ignore + + event CacheEvent.Collected (label, key, version) + | _ -> () + + removeCollected next + | _ -> failwith "Illegal state, strong reference in weak list" + + let cutWeakListIfTooLong () = + if weakList.Count > keepWeakly then + removeCollected weakList.First + + let mutable node = weakList.Last + + while weakList.Count > keepWeakly && node <> null do + let previous = node.Previous + let key, version, label, _ = node.Value + weakList.Remove node + dictionary[key].Remove version |> ignore + + if dictionary[key].Count = 0 then + dictionary.Remove key |> ignore + + event CacheEvent.Evicted (label, key, version) + node <- previous + + let cutStrongListIfTooLong () = + let mutable node = strongList.Last + + let mutable anythingWeakened = false + + while strongList.Count > keepStrongly && node <> null do + let previous = node.Previous + + match node.Value with + | _, _, _, Strong v when requiredToKeep v -> () + | key, version, label, Strong v -> + strongList.Remove node + node.Value <- key, version, label, Weak(WeakReference<_> v) + weakList.AddFirst node + event CacheEvent.Weakened (label, key, version) + anythingWeakened <- true + | _key, _version, _label, _ -> failwith "Invalid state, weak reference in strong list" + + node <- previous + + if anythingWeakened then + cutWeakListIfTooLong () + + let pushNodeToTop (node: LinkedListNode<_>) = + match node.Value with + | _, _, _, Strong _ -> + strongList.AddFirst node + cutStrongListIfTooLong () + | _, _, _, Weak _ -> failwith "Invalid operation, pushing weak reference to strong list" + + let pushValueToTop key version label value = + strongList.AddFirst(value = (key, version, label, Strong value)) + + member _.DebuggerDisplay = $"Cache(S:{strongList.Count} W:{weakList.Count})" + + member _.Set(key, version, label, value) = + match dictionary.TryGetValue key with + | true, versionDict -> + + if versionDict.ContainsKey version then + // TODO this is normal for unversioned cache; + // failwith "Suspicious - overwriting existing version" + + let node: LinkedListNode<_> = versionDict[version] + + match node.Value with + | _, _, _, Strong _ -> strongList.Remove node + | _, _, _, Weak _ -> + weakList.Remove node + event CacheEvent.Strengthened (label, key, version) + + node.Value <- key, version, label, Strong value + pushNodeToTop node + + else + let node = pushValueToTop key version label value + versionDict[version] <- node + // weaken all other versions (unless they're required to be kept) + let versionsToWeaken = versionDict.Keys |> Seq.filter ((<>) version) |> Seq.toList + + let mutable anythingWeakened = false + + for otherVersion in versionsToWeaken do + let node = versionDict[otherVersion] + + match node.Value with + | _, _, _, Strong value when not (requiredToKeep value) -> + strongList.Remove node + node.Value <- key, otherVersion, label, Weak(WeakReference<_> value) + weakList.AddFirst node + event CacheEvent.Weakened (label, key, otherVersion) + anythingWeakened <- true + | _ -> () + + if anythingWeakened then + cutWeakListIfTooLong () + else + cutStrongListIfTooLong () + + | false, _ -> + let node = pushValueToTop key version label value + cutStrongListIfTooLong () + dictionary[key] <- Dictionary() + dictionary[key][version] <- node + + member this.Set(key, version, value) = + this.Set(key, version, "[no label]", value) + + member _.TryGet(key, version) = + + match dictionary.TryGetValue key with + | false, _ -> None + | true, versionDict -> + match versionDict.TryGetValue version with + | false, _ -> None + | true, node -> + match node.Value with + | _, _, _, Strong v -> + strongList.Remove node + pushNodeToTop node + Some v + + | _, _, label, Weak w -> + match w.TryGetTarget() with + | true, value -> + weakList.Remove node + let node = pushValueToTop key version label value + event CacheEvent.Strengthened (label, key, version) + cutStrongListIfTooLong () + versionDict[version] <- node + Some value + | _ -> + weakList.Remove node + versionDict.Remove version |> ignore + + if versionDict.Count = 0 then + dictionary.Remove key |> ignore + + event CacheEvent.Collected (label, key, version) + None + + /// Returns an option of a value for given key and version, and also a list of all other versions for given key + member this.GetAll(key, version) = + this.TryGet(key, version), + + match dictionary.TryGetValue key with + | false, _ -> [] + | true, versionDict -> + versionDict.Values + |> Seq.map (fun node -> node.Value) + |> Seq.filter (p24 >> ((<>) version)) + |> Seq.choose (function + | _, ver, _, Strong v -> Some(ver, v) + | _, ver, _, Weak r -> + match r.TryGetTarget() with + | true, x -> Some(ver, x) + | _ -> None) + |> Seq.toList + + member _.Remove(key, version) = + match dictionary.TryGetValue key with + | false, _ -> () + | true, versionDict -> + match versionDict.TryGetValue version with + | true, node -> + versionDict.Remove version |> ignore + + if versionDict.Count = 0 then + dictionary.Remove key |> ignore + + match node.Value with + | _, _, _, Strong _ -> strongList.Remove node + | _, _, _, Weak _ -> weakList.Remove node + | _ -> () + + member this.Set(key, value) = + this.Set(key, Unchecked.defaultof<_>, value) + + member this.TryGet(key) = + this.TryGet(key, Unchecked.defaultof<_>) + + member this.Remove(key) = + this.Remove(key, Unchecked.defaultof<_>) + + member _.Clear() = + dictionary.Clear() + strongList.Clear() + weakList.Clear() + + member _.Clear(predicate) = + let keysToRemove = dictionary.Keys |> Seq.filter predicate |> Seq.toList + + for key in keysToRemove do + match dictionary.TryGetValue key with + | true, versionDict -> + versionDict.Values + |> Seq.iter (fun node -> + match node.Value with + | _, _, _, Strong _ -> strongList.Remove node + | _, _, _, Weak _ -> weakList.Remove node + + match node.Value with + | key, version, label, _ -> event CacheEvent.Cleared (label, key, version)) + + dictionary.Remove key |> ignore + | _ -> () + + member _.GetValues() = + strongList + |> Seq.append weakList + |> Seq.choose (function + | _k, version, label, Strong value -> Some(label, version, value) + | _k, version, label, Weak w -> + match w.TryGetTarget() with + | true, value -> Some(label, version, value) + | _ -> None) diff --git a/src/Compiler/Utilities/LruCache.fsi b/src/Compiler/Utilities/LruCache.fsi new file mode 100644 index 00000000000..d9aefd2a240 --- /dev/null +++ b/src/Compiler/Utilities/LruCache.fsi @@ -0,0 +1,52 @@ +namespace Internal.Utilities.Collections + +[] +type internal CacheEvent = + | Evicted + | Collected + | Weakened + | Strengthened + | Cleared + +/// A cache where least recently used items are removed when the cache is full. +/// +/// It's also versioned, meaning each key can have multiple versions and only the latest one is kept strongly. +/// Older versions are kept weakly and can be collected by GC. +type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality and 'TValue: not struct> = + + /// Maximum number of strongly held results to keep in the cache + /// Maximum number of weakly held results to keep in the cache + /// A predicate that determines if a value should be kept strongly (no matter what) + /// An event that is called when an item is evicted, collected, weakened or strengthened + new: + keepStrongly: int * + ?keepWeakly: int * + ?requiredToKeep: ('TValue -> bool) * + ?event: (CacheEvent -> string * 'TKey * 'TVersion -> unit) -> + LruCache<'TKey, 'TVersion, 'TValue> + + member Clear: unit -> unit + + /// Clear any keys that match the given predicate + member Clear: predicate: ('TKey -> bool) -> unit + + /// Returns an option of a value for given key and version, and also a list of all other versions for given key + member GetAll: key: 'TKey * version: 'TVersion -> 'TValue option * ('TVersion * 'TValue) list + + member GetValues: unit -> (string * 'TVersion * 'TValue) seq + + member Remove: key: 'TKey -> unit + + member Remove: key: 'TKey * version: 'TVersion -> unit + + member Set: key: 'TKey * value: 'TValue -> unit + + member Set: key: 'TKey * version: 'TVersion * value: 'TValue -> unit + + member Set: key: 'TKey * version: 'TVersion * label: string * value: 'TValue -> unit + + member TryGet: key: 'TKey -> 'TValue option + + member TryGet: key: 'TKey * version: 'TVersion -> 'TValue option + + member DebuggerDisplay: string diff --git a/src/Compiler/Utilities/ReadOnlySpan.fs b/src/Compiler/Utilities/ReadOnlySpan.fs index ec673f18fd3..05683eadb9e 100644 --- a/src/Compiler/Utilities/ReadOnlySpan.fs +++ b/src/Compiler/Utilities/ReadOnlySpan.fs @@ -34,6 +34,18 @@ type ReadOnlySpanExtensions = if found then i else -1 + [] + static member IndexOfAnyExcept(span: ReadOnlySpan, value: char) = + let mutable i = 0 + let mutable found = false + + while not found && i < span.Length do + let c = span[i] + + if c <> value then found <- true else i <- i + 1 + + if found then i else -1 + [] static member LastIndexOfAnyInRange(span: ReadOnlySpan, lowInclusive: char, highInclusive: char) = let mutable i = span.Length - 1 diff --git a/src/Compiler/Utilities/ReadOnlySpan.fsi b/src/Compiler/Utilities/ReadOnlySpan.fsi index 875ffba28ad..67591a03f88 100644 --- a/src/Compiler/Utilities/ReadOnlySpan.fsi +++ b/src/Compiler/Utilities/ReadOnlySpan.fsi @@ -12,6 +12,9 @@ type internal ReadOnlySpanExtensions = [] static member IndexOfAnyExcept: span: ReadOnlySpan * values: ReadOnlySpan -> int + [] + static member IndexOfAnyExcept: span: ReadOnlySpan * value: char -> int + [] static member LastIndexOfAnyInRange: span: ReadOnlySpan * lowInclusive: char * highInclusive: char -> int #endif diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 37f64c31e0b..6c5415667d0 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -1612,7 +1612,7 @@ tyconDefn: SynTypeDefn($1, SynTypeDefnRepr.ObjectModel(SynTypeDefnKind.Augmentation mWithKwd, [], m), classDefns, None, m, trivia) } | typeNameInfo opt_attributes opt_access opt_HIGH_PRECEDENCE_APP opt_simplePatterns optAsSpec EQUALS tyconDefnRhsBlock - { let vis, spats, az = $3, $5, $6 + { let vis, pat, az = $3, $5, $6 let nameRange = rhs parseState 1 let (tcDefRepr, mWith, members) = $8 nameRange let (SynComponentInfo(_, _, _, lid, _, _, _, _)) = $1 @@ -1624,8 +1624,8 @@ tyconDefn: let m = match lid with [] -> rhs parseState 1 | _ -> rangeOfLid lid let memberCtorPattern = - spats |> Option.map (fun spats -> - SynMemberDefn.ImplicitCtor(vis, $2, spats, Option.bind snd az, xmlDoc, m, { AsKeyword = Option.map fst az }) + pat |> Option.map (fun pat -> + SynMemberDefn.ImplicitCtor(vis, $2, pat, Option.bind snd az, xmlDoc, m, { AsKeyword = Option.map fst az }) ) let tcDefRepr = @@ -1663,16 +1663,16 @@ tyconDefn: SynTypeDefn($1, tcDefRepr, members, memberCtorPattern, mWhole, trivia) } | typeNameInfo opt_attributes opt_access opt_HIGH_PRECEDENCE_APP opt_simplePatterns optAsSpec recover - { let vis, spats, az = $3, $5, $6 + { let vis, pat, az = $3, $5, $6 let (SynComponentInfo(longId = lid)) = $1 // Gets the XML doc comments prior to the implicit constructor let xmlDoc = grabXmlDoc (parseState, $2, 2) let m = match lid with [] -> rhs parseState 1 | _ -> rangeOfLid lid let mName = $1.Range let members, mWhole = - match spats, vis, az with - | Some spats, _, _ -> - let memberCtorPattern = SynMemberDefn.ImplicitCtor(vis, $2, spats, Option.bind snd az, xmlDoc, m, { AsKeyword = Option.map fst az }) + match pat, vis, az with + | Some pat, _, _ -> + let memberCtorPattern = SynMemberDefn.ImplicitCtor(vis, $2, pat, Option.bind snd az, xmlDoc, m, { AsKeyword = Option.map fst az }) [memberCtorPattern], unionRanges mName memberCtorPattern.Range | _, _, Some(mAs, asId) -> @@ -3412,38 +3412,6 @@ bindingPattern: | headBindingPattern { $1, $1.Range } -// Subset of patterns allowed to be used in implicit ctors. -// For a better error recovery we could replace these rules with the actual SynPat parsing -// and use allowed patterns only at a later analysis stage reporting errors along the way. -simplePattern: - | ident - { let m = rhs parseState 1 - SynPat.Named(SynIdent($1, None), false, None, m) } - - | QMARK ident - { SynPat.OptionalVal($2, rhs parseState 2) } - - | simplePattern COLON typeWithTypeConstraints - { SynPat.Typed($1, $3, lhs parseState) } - - | simplePattern COLON recover - { let mColon = rhs parseState 2 - let ty = SynType.FromParseError(mColon.EndRange) - SynPat.Typed($1, ty, unionRanges $1.Range mColon) } - - | attributes simplePattern %prec paren_pat_attribs - { SynPat.Attrib($2, $1, lhs parseState) } - -simplePatternCommaList: - | simplePattern - { $1 } - - | simplePattern COMMA simplePatternCommaList - { let mComma = rhs parseState 2 - match $3 with - | SynPat.Tuple(_, pats, commas, _) -> SynPat.Tuple(false, $1 :: pats, mComma :: commas, rhs2 parseState 1 3) - | _ -> SynPat.Tuple(false, [$1; $3], [mComma], rhs2 parseState 1 3) } - opt_simplePatterns: | simplePatterns { Some $1 } @@ -3451,32 +3419,11 @@ opt_simplePatterns: | { None } simplePatterns: - | LPAREN simplePatternCommaList rparen - { let parenPat = SynPat.Paren($2, rhs2 parseState 1 3) - let simplePats, _ = SimplePatsOfPat parseState.SynArgNameGenerator parenPat - simplePats } + | LPAREN parenPattern rparen + { SynPat.Paren($2, rhs2 parseState 1 3) } | LPAREN rparen - { let pat = SynPat.Const(SynConst.Unit, rhs2 parseState 1 2) - let simplePats, _ = SimplePatsOfPat parseState.SynArgNameGenerator pat - simplePats } - - | LPAREN simplePatternCommaList recover - { reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsUnmatchedParen()) - let parenPat = SynPat.Paren(SynPat.Tuple(false, [], [], rhs2 parseState 1 2), rhs2 parseState 1 2) // todo: report parsed pats anyway? - let simplePats, _ = SimplePatsOfPat parseState.SynArgNameGenerator parenPat - simplePats } - - | LPAREN error rparen - { let parenPat = SynPat.Paren(SynPat.Wild(rhs parseState 2), rhs2 parseState 1 3) // silent recovery - let simplePats, _ = SimplePatsOfPat parseState.SynArgNameGenerator parenPat - simplePats } - - | LPAREN recover - { reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsUnmatchedParen()) - let pat = SynPat.Wild(lhs parseState) - let simplePats, _ = SimplePatsOfPat parseState.SynArgNameGenerator pat - simplePats } + { SynPat.Const(SynConst.Unit, rhs2 parseState 1 2) } headBindingPattern: diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index f5d07242c13..c7dbc64bac7 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -247,6 +247,11 @@ automatické generování vlastnosti Message pro deklarace exception + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules Povolit implicitní atribut Extension pro deklarující typy, moduly @@ -962,6 +967,11 @@ Tento přístup člena je nejednoznačný. Při vytváření objektu použijte závorky, např. (new SomeType(args)).MemberName' + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. Neúplná deklarace statického konstruktoru. Pro deklaraci použijte „static let“, „static do“, „static member“ nebo „static val“. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 22a1cd749f1..d5800420f00 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -247,6 +247,11 @@ Automatische Generierung der Eigenschaft „Message“ für „exception“-Deklarationen + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules Implizites Erweiterungsattribut für deklarierende Typen und Module zulassen @@ -962,6 +967,11 @@ Dieser Memberzugriff ist mehrdeutig. Setzen Sie Klammern um die Objekterstellung, z. B. "(new SomeType(args)). MemberName“ + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. Unvollständige Deklaration eines statischen Konstrukts. Verwenden Sie "static let", "static do", "static member" oder "static val" für die Deklaration. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 92200593d94..dcfce175697 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -247,6 +247,11 @@ generación automática de la propiedad 'Message' para declaraciones 'exception' + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules Permitir atributo Extension implícito en tipos declarativo, módulos @@ -962,6 +967,11 @@ Este acceso de miembro es ambiguo. Use paréntesis alrededor de la creación del objeto, por ejemplo, '(nuevo AlgúnTipo(args)).NombreMiembro' + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. Declaración incompleta de una construcción estática. Use "static let", "static do", "static member" o "static val" para la declaración. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index dba62915de7..590c4fb8e15 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -247,6 +247,11 @@ génération automatique de la propriété « Message » pour les déclarations « exception » + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules Autoriser l’attribut implicite Extension lors de la déclaration des types, modules @@ -962,6 +967,11 @@ L’accès à ce membre est ambigu. Utilisez des parenthèses autour de la création de l’objet, par exemple' (New SomeType (args)). MemberName + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. Déclaration incomplète d’une construction statique. Utilisez « static let », « static do », « static member » ou « static val » pour la déclaration. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 6572f8f11ee..70613984b76 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -247,6 +247,11 @@ generazione automatica della proprietà 'Messaggio' per le dichiarazioni 'eccezione' + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules Consentire l'attributo estensione implicito per i tipi dichiarabili, i moduli @@ -962,6 +967,11 @@ L'accesso ai membri è ambiguo. Utilizzare le parentesi intorno alla creazione oggetto, ad esempio “(New SomeType (args)). MemberName” + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. Dichiarazione incompleta di un costrutto statico. Usare 'static let','static do','static member' o 'static val' per la dichiarazione. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 062134d82c0..84a9ca6ef70 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -247,6 +247,11 @@ `exception` 宣言の `Message` プロパティの自動生成 + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules 型、モジュールの宣言で暗黙的な拡張属性を許可する @@ -962,6 +967,11 @@ このメンバーへのアクセスはあいまいです。オブジェクト作成の前後にはかっこを使用してください。例: '(new SomeType(args)).MemberName' + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. 静的コンストラクトの不完全な宣言。宣言には、'static let'、'static do'、'static member'、または 'static val' を使用します。 diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 78f1420a90e..38ffac7d32d 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -247,6 +247,11 @@ 'exception' 선언에 대한 'Message' 속성 자동 생성 + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules 유형, 모듈 선언에 암시적 확장 속성 허용 @@ -962,6 +967,11 @@ 이 구성원 액세스가 모호합니다. 개체 생성 주위에 괄호를 사용하세요. 예: '(새로운 SomeType(인수)).MemberName' + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. 정적 구문의 선언이 불완전합니다. 선언에 'static let','static do','static member' 또는 'static val'을 사용합니다. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index fdd7c310f6b..ac1d54f63c4 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -247,6 +247,11 @@ Automatyczne generowanie właściwości „Wiadomość“ dla deklaracji „Wyjątek“ + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules Zezwalaj na niejawny atrybut Rozszerzenie dla deklarujących typów, modułów @@ -962,6 +967,11 @@ Dostęp tego elementu członkowskiego jest niejednoznaczny. W celu utworzenia obiektu użyj nawiasów, na przykład „(nowy SomeType(args)).MemberName” + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. Niekompletna deklaracja konstrukcji statycznej. Użyj elementu „static let”, „static do”, „static member” lub „static val” na potrzeby deklaracji. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 2274d6b1a28..6beb1946267 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -247,6 +247,11 @@ geração automática da propriedade 'Message' para declarações de 'exception' + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules Permitir atributo de Extensão implícito em tipos declarativos, módulos @@ -962,6 +967,11 @@ Este acesso de membro é ambíguo. Use parênteses em torno da criação do objeto, por exemplo, '(new SomeType(args)).MemberName''. + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. Declaração incompleta de um constructo estático. Use "static let","static do","static member" ou "static val" para declaração. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 6fe5743748d..1b83dc7cc6b 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -247,6 +247,11 @@ автоматическое создание свойства “Message” для объявлений “exception” + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules Разрешить атрибут неявного расширения для объявляющих типов, модулей @@ -962,6 +967,11 @@ Неоднозначный доступ к этому элементу. Заключите операцию создания объекта в круглые скобки, например (new Объект(аргументы)).Элемент + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. Неполное объявление статической конструкции. Для объявления используйте «static let», «static do», «staticmember» или «static val». diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 592dc720f17..43f5069b2eb 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -247,6 +247,11 @@ 'exception' bildirimleri için 'Message' özelliğinin otomatik olarak oluşturulması + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules Türler, modüller bildirirken örtük Extension özniteliğine izin ver @@ -962,6 +967,11 @@ Bu üye erişimi belirsiz. Lütfen nesne oluşturma etrafında parantez kullanın, örneğin '(yeni SomeType (args)).MemberName’ + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. Statik yapının bildirimi eksik. Bildirim için 'static let','static do','static member' veya 'static val' kullanın. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 373eb45cac8..91cad01e99e 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -247,6 +247,11 @@ 自动生成“异常”声明的“消息”属性 + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules 允许对声明类型、模块使用隐式扩展属性 @@ -962,6 +967,11 @@ 此成员访问权限不明确。请在对象创建周围使用括号,例如 “(new SomeType(args)).MemberName” + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. 静态构造的声明不完整。使用“static let”、“static do”、“static member”或“static val”进行声明。 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index a17695469e6..f12b3fd0b5e 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -247,6 +247,11 @@ 自動產生 'exception' 宣告的 'Message' 屬性 + + Boolean-returning and return-type-directed partial active patterns + Boolean-returning and return-type-directed partial active patterns + + Allow implicit Extension attribute on declaring types, modules 允許宣告類型、模組上的隱含擴充屬性 @@ -962,6 +967,11 @@ 此成員存取不明確。請在物件建立前後加上括弧,例如「(new SomeType(args)).MemberName」 + + Only simple patterns are allowed in primary constructors + Only simple patterns are allowed in primary constructors + + Incomplete declaration of a static construct. Use 'static let','static do','static member' or 'static val' for declaration. 不完整的靜態建構宣告。使用 'static let'、'static do'、'static member' 或 'static val' 進行宣告。 diff --git a/src/FSharp.Core/Linq.fs b/src/FSharp.Core/Linq.fs index 257dd27edb2..cf7033e09db 100644 --- a/src/FSharp.Core/Linq.fs +++ b/src/FSharp.Core/Linq.fs @@ -59,11 +59,11 @@ module LeafExpressionConverter = |> System.Reflection.MethodInfo.GetMethodFromHandle :?> MethodInfo - let SubstHelperRaw (q:Expr, x:Var[], y:obj[]) : Expr = + let SubstHelperRaw (q:Expr, x:Var array, y:obj array) : Expr = let d = Map.ofArray (Array.zip x y) q.Substitute(fun v -> v |> d.TryFind |> Option.map (fun x -> Expr.Value (x, v.Type))) - let SubstHelper<'T> (q:Expr, x:Var[], y:obj[]) : Expr<'T> = + let SubstHelper<'T> (q:Expr, x:Var array, y:obj array) : Expr<'T> = SubstHelperRaw(q, x, y) |> Expr.Cast let showAll = @@ -390,15 +390,15 @@ module LeafExpressionConverter = let (|NewAnonymousObjectHelperQ|_|) = (|SpecificCallToMethod|_|) (methodhandleof (NewAnonymousObjectHelper)) let (|ArrayLookupQ|_|) = (|SpecificCallToMethod|_|) (methodhandleof (fun (x, y) -> LanguagePrimitives.IntrinsicFunctions.GetArray x y)) - //let (|ArrayAssignQ|_|) = (|SpecificCallToMethod|_|) (methodhandleof (fun -> LanguagePrimitives.IntrinsicFunctions.SetArray : int[] -> int -> int -> unit)) + //let (|ArrayAssignQ|_|) = (|SpecificCallToMethod|_|) (methodhandleof (fun -> LanguagePrimitives.IntrinsicFunctions.SetArray : int array -> int -> int -> unit)) //let (|ArrayTypeQ|_|) (ty:System.Type) = if ty.IsArray && ty.GetArrayRank() = 1 then Some (ty.GetElementType()) else None let substHelperMeth = - methodhandleof (fun (x:Expr, y:Var[], z:obj[]) -> SubstHelper (x, y, z)) + methodhandleof (fun (x:Expr, y:Var array, z:obj array) -> SubstHelper (x, y, z)) |> System.Reflection.MethodInfo.GetMethodFromHandle :?> MethodInfo let substHelperRawMeth = - methodhandleof (fun (x:Expr, y:Var[], z:obj[]) -> SubstHelperRaw (x, y, z)) + methodhandleof (fun (x:Expr, y:Var array, z:obj array) -> SubstHelperRaw (x, y, z)) |> System.Reflection.MethodInfo.GetMethodFromHandle :?> MethodInfo @@ -737,7 +737,7 @@ module LeafExpressionConverter = else Reflection.FSharpType.MakeTupleType(argTypes) let argsP = ConvExprsToLinq env args - let rec build ty (argsP: Expression[]) = + let rec build ty (argsP: Expression array) = match Reflection.FSharpValue.PreComputeTupleConstructorInfo ty with | ctorInfo, None -> Expression.New(ctorInfo, argsP) |> asExpr | ctorInfo, Some (nestedTy) -> @@ -879,7 +879,7 @@ module LeafExpressionConverter = | None -> null - and ConvExprsToLinq env es : Expression[] = + and ConvExprsToLinq env es : Expression array = es |> List.map (ConvExprToLinqInContext env) |> Array.ofList and ConvVarToLinq (v: Var) = diff --git a/src/FSharp.Core/Linq.fsi b/src/FSharp.Core/Linq.fsi index 13f3fa4187e..5064de12f55 100644 --- a/src/FSharp.Core/Linq.fsi +++ b/src/FSharp.Core/Linq.fsi @@ -78,14 +78,14 @@ module LeafExpressionConverter = /// /// /// - val SubstHelper: Expr * Var[] * obj[] -> Expr<'T> + val SubstHelper: Expr * Var array * obj array -> Expr<'T> /// /// A runtime helper used to evaluate nested quotation literals. /// /// /// - val SubstHelperRaw: Expr * Var[] * obj[] -> Expr + val SubstHelperRaw: Expr * Var array * obj array -> Expr val internal (|SpecificCallToMethod|_|): System.RuntimeMethodHandle -> (Expr -> (Expr option * Reflection.MethodInfo * Expr list) option) diff --git a/src/FSharp.Core/array.fs b/src/FSharp.Core/array.fs index 9902a063871..a2797467b7c 100644 --- a/src/FSharp.Core/array.fs +++ b/src/FSharp.Core/array.fs @@ -24,12 +24,12 @@ module Array = raise (KeyNotFoundException(SR.GetString(SR.keyNotFoundAlt))) [] - let length (array: _[]) = + let length (array: _ array) = checkNonNull "array" array array.Length [] - let inline last (array: 'T[]) = + let inline last (array: 'T array) = checkNonNull "array" array if array.Length = 0 then @@ -38,7 +38,7 @@ module Array = array.[array.Length - 1] [] - let tryLast (array: 'T[]) = + let tryLast (array: 'T array) = checkNonNull "array" array if array.Length = 0 then @@ -62,7 +62,8 @@ module Array = if count < 0 then invalidArgInputMustBeNonNegative "count" count - let array: 'T[] = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count + let array: 'T array = + Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked count for i = 0 to Operators.Checked.(-) array.Length 1 do // use checked arithmetic here to satisfy FxCop array.[i] <- value @@ -70,7 +71,7 @@ module Array = array [] - let tryHead (array: 'T[]) = + let tryHead (array: 'T array) = checkNonNull "array" array if array.Length = 0 then @@ -79,12 +80,12 @@ module Array = Some array.[0] [] - let isEmpty (array: 'T[]) = + let isEmpty (array: 'T array) = checkNonNull "array" array array.Length = 0 [] - let tail (array: 'T[]) = + let tail (array: 'T array) = checkNonNull "array" array if array.Length = 0 then @@ -93,13 +94,13 @@ module Array = Microsoft.FSharp.Primitives.Basics.Array.subUnchecked 1 (array.Length - 1) array [] - let empty<'T> : 'T[] = [||] + let empty<'T> : 'T array = [||] [] - let inline blit (source: 'T[]) (sourceIndex: int) (target: 'T[]) (targetIndex: int) (count: int) = + let inline blit (source: 'T array) (sourceIndex: int) (target: 'T array) (targetIndex: int) (count: int) = Array.Copy(source, sourceIndex, target, targetIndex, count) - let concatArrays (arrs: 'T[][]) : 'T[] = + let concatArrays (arrs: 'T array array) : 'T array = let mutable acc = 0 for h in arrs do @@ -118,11 +119,11 @@ module Array = res [] - let concat (arrays: seq<'T[]>) = + let concat (arrays: seq<'T array>) = checkNonNull "arrays" arrays match arrays with - | :? ('T[][]) as ts -> ts |> concatArrays // avoid a clone, since we only read the array + | :? ('T array array) as ts -> ts |> concatArrays // avoid a clone, since we only read the array | _ -> arrays |> Seq.toArray |> concatArrays [] @@ -139,10 +140,12 @@ module Array = arr [] - let collect (mapping: 'T -> 'U[]) (array: 'T[]) : 'U[] = + let collect (mapping: 'T -> 'U array) (array: 'T array) : 'U array = checkNonNull "array" array let len = array.Length - let result = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked<'U[]> len + + let result = + Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked<'U array> len for i = 0 to result.Length - 1 do result.[i] <- mapping array.[i] @@ -150,7 +153,7 @@ module Array = concatArrays result [] - let splitAt index (array: 'T[]) = + let splitAt index (array: 'T array) = checkNonNull "array" array if index < 0 then @@ -178,7 +181,7 @@ module Array = res1, res2 [] - let take count (array: 'T[]) = + let take count (array: 'T array) = checkNonNull "array" array if count < 0 then @@ -193,7 +196,7 @@ module Array = Microsoft.FSharp.Primitives.Basics.Array.subUnchecked 0 count array [] - let takeWhile predicate (array: 'T[]) = + let takeWhile predicate (array: 'T array) = checkNonNull "array" array if array.Length = 0 then @@ -210,7 +213,7 @@ module Array = (comparer: IEqualityComparer<'SafeKey>) ([] projection: 'T -> 'SafeKey) ([] getKey: 'SafeKey -> 'Key) - (array: 'T[]) + (array: 'T array) = let length = array.Length @@ -240,11 +243,11 @@ module Array = res // We avoid wrapping a StructBox, because under 64 JIT we get some "hard" tailcalls which affect performance - let countByValueType (projection: 'T -> 'Key) (array: 'T[]) = + let countByValueType (projection: 'T -> 'Key) (array: 'T array) = countByImpl HashIdentity.Structural<'Key> projection id array // Wrap a StructBox around all keys in case the key type is itself a type using null as a representation - let countByRefType (projection: 'T -> 'Key) (array: 'T[]) = + let countByRefType (projection: 'T -> 'Key) (array: 'T array) = countByImpl RuntimeHelpers.StructBox<'Key>.Comparer (projection >> RuntimeHelpers.StructBox) @@ -252,7 +255,7 @@ module Array = array [] - let countBy (projection: 'T -> 'Key) (array: 'T[]) = + let countBy (projection: 'T -> 'Key) (array: 'T array) = checkNonNull "array" array if typeof<'Key>.IsValueType then @@ -261,13 +264,13 @@ module Array = countByRefType projection array [] - let append (array1: 'T[]) (array2: 'T[]) = + let append (array1: 'T array) (array2: 'T array) = checkNonNull "array1" array1 checkNonNull "array2" array2 let n1 = array1.Length let n2 = array2.Length - let res: 'T[] = + let res: 'T array = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked (n1 + n2) Array.Copy(array1, 0, res, 0, n1) @@ -275,7 +278,7 @@ module Array = res [] - let head (array: 'T[]) = + let head (array: 'T array) = checkNonNull "array" array if array.Length = 0 then @@ -284,9 +287,9 @@ module Array = array.[0] [] - let copy (array: 'T[]) = + let copy (array: 'T array) = checkNonNull "array" array - (array.Clone() :?> 'T[]) // this is marginally faster + (array.Clone() :?> 'T array) // this is marginally faster //let len = array.Length //let res = zeroCreate len //for i = 0 to len - 1 do @@ -303,7 +306,7 @@ module Array = List.toArray list [] - let indexed (array: 'T[]) = + let indexed (array: 'T array) = checkNonNull "array" array let res = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked array.Length @@ -313,14 +316,14 @@ module Array = res [] - let inline iter ([] action) (array: 'T[]) = + let inline iter ([] action) (array: 'T array) = checkNonNull "array" array for i = 0 to array.Length - 1 do action array.[i] [] - let distinct (array: 'T[]) = + let distinct (array: 'T array) = checkNonNull "array" array let temp = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked array.Length let mutable i = 0 @@ -335,10 +338,10 @@ module Array = Microsoft.FSharp.Primitives.Basics.Array.subUnchecked 0 i temp [] - let inline map ([] mapping: 'T -> 'U) (array: 'T[]) = + let inline map ([] mapping: 'T -> 'U) (array: 'T array) = checkNonNull "array" array - let res: 'U[] = + let res: 'U array = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked array.Length for i = 0 to res.Length - 1 do @@ -347,7 +350,7 @@ module Array = res [] - let iter2 action (array1: 'T[]) (array2: 'U[]) = + let iter2 action (array1: 'T array) (array2: 'U array) = checkNonNull "array1" array1 checkNonNull "array2" array2 let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(action) @@ -359,7 +362,7 @@ module Array = f.Invoke(array1.[i], array2.[i]) [] - let distinctBy projection (array: 'T[]) = + let distinctBy projection (array: 'T array) = checkNonNull "array" array let length = array.Length @@ -379,7 +382,7 @@ module Array = Microsoft.FSharp.Primitives.Basics.Array.subUnchecked 0 i temp [] - let map2 mapping (array1: 'T[]) (array2: 'U[]) = + let map2 mapping (array1: 'T array) (array2: 'U array) = checkNonNull "array1" array1 checkNonNull "array2" array2 let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(mapping) @@ -395,7 +398,7 @@ module Array = res [] - let map3 mapping (array1: 'T1[]) (array2: 'T2[]) (array3: 'T3[]) = + let map3 mapping (array1: 'T1 array) (array2: 'T2 array) (array3: 'T3 array) = checkNonNull "array1" array1 checkNonNull "array2" array2 checkNonNull "array3" array3 @@ -413,7 +416,7 @@ module Array = res [] - let mapi2 mapping (array1: 'T[]) (array2: 'U[]) = + let mapi2 mapping (array1: 'T array) (array2: 'U array) = checkNonNull "array1" array1 checkNonNull "array2" array2 let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt(mapping) @@ -429,7 +432,7 @@ module Array = res [] - let iteri action (array: 'T[]) = + let iteri action (array: 'T array) = checkNonNull "array" array let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(action) @@ -437,7 +440,7 @@ module Array = f.Invoke(i, array.[i]) [] - let iteri2 action (array1: 'T[]) (array2: 'U[]) = + let iteri2 action (array1: 'T array) (array2: 'U array) = checkNonNull "array1" array1 checkNonNull "array2" array2 let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt(action) @@ -449,7 +452,7 @@ module Array = f.Invoke(i, array1.[i], array2.[i]) [] - let mapi (mapping: int -> 'T -> 'U) (array: 'T[]) = + let mapi (mapping: int -> 'T -> 'U) (array: 'T array) = checkNonNull "array" array let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(mapping) let res = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked array.Length @@ -470,7 +473,7 @@ module Array = Microsoft.FSharp.Primitives.Basics.Array.mapFoldBack mapping array state [] - let inline exists ([] predicate: 'T -> bool) (array: 'T[]) = + let inline exists ([] predicate: 'T -> bool) (array: 'T array) = checkNonNull "array" array let mutable state = false let mutable i = 0 @@ -482,7 +485,7 @@ module Array = state [] - let inline contains value (array: 'T[]) = + let inline contains value (array: 'T array) = checkNonNull "array" array let mutable state = false let mutable i = 0 @@ -494,7 +497,7 @@ module Array = state [] - let exists2 predicate (array1: _[]) (array2: _[]) = + let exists2 predicate (array1: _ array) (array2: _ array) = checkNonNull "array1" array1 checkNonNull "array2" array2 let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(predicate) @@ -509,7 +512,7 @@ module Array = loop 0 [] - let forall (predicate: 'T -> bool) (array: 'T[]) = + let forall (predicate: 'T -> bool) (array: 'T array) = checkNonNull "array" array let len = array.Length @@ -519,7 +522,7 @@ module Array = loop 0 [] - let forall2 predicate (array1: _[]) (array2: _[]) = + let forall2 predicate (array1: _ array) (array2: _ array) = checkNonNull "array1" array1 checkNonNull "array2" array2 let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(predicate) @@ -537,7 +540,7 @@ module Array = (comparer: IEqualityComparer<'SafeKey>) ([] keyf: 'T -> 'SafeKey) ([] getKey: 'SafeKey -> 'Key) - (array: 'T[]) + (array: 'T array) = let length = array.Length @@ -570,11 +573,11 @@ module Array = result // We avoid wrapping a StructBox, because under 64 JIT we get some "hard" tailcalls which affect performance - let groupByValueType (keyf: 'T -> 'Key) (array: 'T[]) = + let groupByValueType (keyf: 'T -> 'Key) (array: 'T array) = groupByImpl HashIdentity.Structural<'Key> keyf id array // Wrap a StructBox around all keys in case the key type is itself a type using null as a representation - let groupByRefType (keyf: 'T -> 'Key) (array: 'T[]) = + let groupByRefType (keyf: 'T -> 'Key) (array: 'T array) = groupByImpl RuntimeHelpers.StructBox<'Key>.Comparer (keyf >> RuntimeHelpers.StructBox) @@ -582,7 +585,7 @@ module Array = array [] - let groupBy (projection: 'T -> 'Key) (array: 'T[]) = + let groupBy (projection: 'T -> 'Key) (array: 'T array) = checkNonNull "array" array if typeof<'Key>.IsValueType then @@ -591,7 +594,7 @@ module Array = groupByRefType projection array [] - let pick chooser (array: _[]) = + let pick chooser (array: _ array) = checkNonNull "array" array let rec loop i = @@ -605,7 +608,7 @@ module Array = loop 0 [] - let tryPick chooser (array: _[]) = + let tryPick chooser (array: _ array) = checkNonNull "array" array let rec loop i = @@ -619,7 +622,7 @@ module Array = loop 0 [] - let choose (chooser: 'T -> 'U Option) (array: 'T[]) = + let choose (chooser: 'T -> 'U Option) (array: 'T array) = checkNonNull "array" array let mutable i = 0 @@ -637,7 +640,7 @@ module Array = if i <> array.Length then - let chunk1: 'U[] = + let chunk1: 'U array = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked ((array.Length >>> 2) + 1) chunk1.[0] <- first @@ -656,7 +659,7 @@ module Array = i <- i + 1 if i < array.Length then - let chunk2: 'U[] = + let chunk2: 'U array = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked (array.Length - i) count <- 0 @@ -672,7 +675,7 @@ module Array = i <- i + 1 - let res: 'U[] = + let res: 'U array = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked (chunk1.Length + count) Array.Copy(chunk1, res, chunk1.Length) @@ -1058,16 +1061,16 @@ module Array = | count -> filterViaMask maskArray leftOverMask count src [] - let filter predicate (array: _[]) = + let filter predicate (array: _ array) = checkNonNull "array" array Filter.filter predicate array [] - let where predicate (array: _[]) = + let where predicate (array: _ array) = filter predicate array [] - let except (itemsToExclude: seq<_>) (array: _[]) = + let except (itemsToExclude: seq<_>) (array: _ array) = checkNonNull "itemsToExclude" itemsToExclude checkNonNull "array" array @@ -1078,7 +1081,7 @@ module Array = array |> filter cached.Add [] - let partition predicate (array: _[]) = + let partition predicate (array: _ array) = checkNonNull "array" array let res = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked array.Length let mutable upCount = 0 @@ -1106,7 +1109,7 @@ module Array = res1, res2 [] - let find predicate (array: _[]) = + let find predicate (array: _ array) = checkNonNull "array" array let rec loop i = @@ -1120,7 +1123,7 @@ module Array = loop 0 [] - let tryFind predicate (array: _[]) = + let tryFind predicate (array: _ array) = checkNonNull "array" array let rec loop i = @@ -1134,7 +1137,7 @@ module Array = loop 0 [] - let skip count (array: 'T[]) = + let skip count (array: 'T array) = checkNonNull "array" array if count > array.Length then @@ -1147,7 +1150,7 @@ module Array = Microsoft.FSharp.Primitives.Basics.Array.subUnchecked count (array.Length - count) array [] - let skipWhile predicate (array: 'T[]) = + let skipWhile predicate (array: 'T array) = checkNonNull "array" array let mutable i = 0 @@ -1159,27 +1162,27 @@ module Array = | resLen -> Microsoft.FSharp.Primitives.Basics.Array.subUnchecked i resLen array [] - let findBack predicate (array: _[]) = + let findBack predicate (array: _ array) = checkNonNull "array" array Microsoft.FSharp.Primitives.Basics.Array.findBack predicate array [] - let tryFindBack predicate (array: _[]) = + let tryFindBack predicate (array: _ array) = checkNonNull "array" array Microsoft.FSharp.Primitives.Basics.Array.tryFindBack predicate array [] - let findIndexBack predicate (array: _[]) = + let findIndexBack predicate (array: _ array) = checkNonNull "array" array Microsoft.FSharp.Primitives.Basics.Array.findIndexBack predicate array [] - let tryFindIndexBack predicate (array: _[]) = + let tryFindIndexBack predicate (array: _ array) = checkNonNull "array" array Microsoft.FSharp.Primitives.Basics.Array.tryFindIndexBack predicate array [] - let windowed windowSize (array: 'T[]) = + let windowed windowSize (array: 'T array) = checkNonNull "array" array if windowSize <= 0 then @@ -1190,7 +1193,7 @@ module Array = if windowSize > len then empty else - let res: 'T[][] = + let res: 'T array array = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked (len - windowSize + 1) for i = 0 to len - windowSize do @@ -1199,7 +1202,7 @@ module Array = res [] - let chunkBySize chunkSize (array: 'T[]) = + let chunkBySize chunkSize (array: 'T array) = checkNonNull "array" array if chunkSize <= 0 then @@ -1214,7 +1217,7 @@ module Array = else let chunkCount = (len - 1) / chunkSize + 1 - let res: 'T[][] = + let res: 'T array array = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked chunkCount for i = 0 to len / chunkSize - 1 do @@ -1230,7 +1233,7 @@ module Array = res [] - let splitInto count (array: _[]) = + let splitInto count (array: _ array) = checkNonNull "array" array if count <= 0 then @@ -1239,7 +1242,7 @@ module Array = Microsoft.FSharp.Primitives.Basics.Array.splitInto count array [] - let zip (array1: _[]) (array2: _[]) = + let zip (array1: _ array) (array2: _ array) = checkNonNull "array1" array1 checkNonNull "array2" array2 let len1 = array1.Length @@ -1255,7 +1258,7 @@ module Array = res [] - let zip3 (array1: _[]) (array2: _[]) (array3: _[]) = + let zip3 (array1: _ array) (array2: _ array) (array3: _ array) = checkNonNull "array1" array1 checkNonNull "array2" array2 checkNonNull "array3" array3 @@ -1272,7 +1275,7 @@ module Array = res [] - let allPairs (array1: _[]) (array2: _[]) = + let allPairs (array1: _ array) (array2: _ array) = checkNonNull "array1" array1 checkNonNull "array2" array2 let len1 = array1.Length @@ -1300,7 +1303,7 @@ module Array = res.ToArray() [] - let unzip (array: _[]) = + let unzip (array: _ array) = checkNonNull "array" array let len = array.Length let res1 = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked len @@ -1314,7 +1317,7 @@ module Array = res1, res2 [] - let unzip3 (array: _[]) = + let unzip3 (array: _ array) = checkNonNull "array" array let len = array.Length let res1 = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked len @@ -1330,7 +1333,7 @@ module Array = res1, res2, res3 [] - let rev (array: _[]) = + let rev (array: _ array) = checkNonNull "array" array let res = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked array.Length let mutable j = array.Length - 1 @@ -1342,7 +1345,7 @@ module Array = res [] - let fold<'T, 'State> (folder: 'State -> 'T -> 'State) (state: 'State) (array: 'T[]) = + let fold<'T, 'State> (folder: 'State -> 'T -> 'State) (state: 'State) (array: 'T array) = checkNonNull "array" array let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(folder) let mutable state = state @@ -1353,7 +1356,7 @@ module Array = state [] - let foldBack<'T, 'State> (folder: 'T -> 'State -> 'State) (array: 'T[]) (state: 'State) = + let foldBack<'T, 'State> (folder: 'T -> 'State -> 'State) (array: 'T array) (state: 'State) = checkNonNull "array" array let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(folder) let mutable res = state @@ -1364,7 +1367,7 @@ module Array = res [] - let foldBack2<'T1, 'T2, 'State> folder (array1: 'T1[]) (array2: 'T2[]) (state: 'State) = + let foldBack2<'T1, 'T2, 'State> folder (array1: 'T1 array) (array2: 'T2 array) (state: 'State) = checkNonNull "array1" array1 checkNonNull "array2" array2 let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt(folder) @@ -1380,7 +1383,7 @@ module Array = res [] - let fold2<'T1, 'T2, 'State> folder (state: 'State) (array1: 'T1[]) (array2: 'T2[]) = + let fold2<'T1, 'T2, 'State> folder (state: 'State) (array1: 'T1 array) (array2: 'T2 array) = checkNonNull "array1" array1 checkNonNull "array2" array2 let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt(folder) @@ -1394,7 +1397,7 @@ module Array = state - let foldSubRight f (array: _[]) start fin acc = + let foldSubRight f (array: _ array) start fin acc = checkNonNull "array" array let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(f) let mutable res = acc @@ -1404,7 +1407,7 @@ module Array = res - let scanSubLeft f initState (array: _[]) start fin = + let scanSubLeft f initState (array: _ array) start fin = checkNonNull "array" array let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(f) let mutable state = initState @@ -1417,13 +1420,13 @@ module Array = res [] - let scan<'T, 'State> folder (state: 'State) (array: 'T[]) = + let scan<'T, 'State> folder (state: 'State) (array: 'T array) = checkNonNull "array" array let len = array.Length scanSubLeft folder state array 0 (len - 1) [] - let scanBack<'T, 'State> folder (array: 'T[]) (state: 'State) = + let scanBack<'T, 'State> folder (array: 'T array) (state: 'State) = checkNonNull "array" array Microsoft.FSharp.Primitives.Basics.Array.scanSubRight folder array 0 (array.Length - 1) state @@ -1432,7 +1435,7 @@ module Array = [| value |] [] - let pairwise (array: 'T[]) = + let pairwise (array: 'T array) = checkNonNull "array" array if array.Length < 2 then @@ -1441,7 +1444,7 @@ module Array = init (array.Length - 1) (fun i -> array.[i], array.[i + 1]) [] - let reduce reduction (array: _[]) = + let reduce reduction (array: _ array) = checkNonNull "array" array let len = array.Length @@ -1457,7 +1460,7 @@ module Array = res [] - let reduceBack reduction (array: _[]) = + let reduceBack reduction (array: _ array) = checkNonNull "array" array let len = array.Length @@ -1467,7 +1470,7 @@ module Array = foldSubRight reduction array 0 (len - 2) array.[len - 1] [] - let sortInPlaceWith comparer (array: 'T[]) = + let sortInPlaceWith comparer (array: 'T array) = checkNonNull "array" array let len = array.Length @@ -1484,17 +1487,17 @@ module Array = Array.Sort(array, ComparisonIdentity.FromFunction(comparer)) [] - let sortInPlaceBy (projection: 'T -> 'U) (array: 'T[]) = + let sortInPlaceBy (projection: 'T -> 'U) (array: 'T array) = checkNonNull "array" array Microsoft.FSharp.Primitives.Basics.Array.unstableSortInPlaceBy projection array [] - let sortInPlace (array: 'T[]) = + let sortInPlace (array: 'T array) = checkNonNull "array" array Microsoft.FSharp.Primitives.Basics.Array.unstableSortInPlace array [] - let sortWith (comparer: 'T -> 'T -> int) (array: 'T[]) = + let sortWith (comparer: 'T -> 'T -> int) (array: 'T array) = checkNonNull "array" array let result = copy array sortInPlaceWith comparer result @@ -1543,7 +1546,7 @@ module Array = Seq.toArray source [] - let findIndex predicate (array: _[]) = + let findIndex predicate (array: _ array) = checkNonNull "array" array let len = array.Length @@ -1555,7 +1558,7 @@ module Array = go 0 [] - let tryFindIndex predicate (array: _[]) = + let tryFindIndex predicate (array: _ array) = checkNonNull "array" array let len = array.Length @@ -1567,12 +1570,12 @@ module Array = go 0 [] - let permute indexMap (array: _[]) = + let permute indexMap (array: _ array) = checkNonNull "array" array Microsoft.FSharp.Primitives.Basics.Array.permute indexMap array [] - let inline sum (array: ^T[]) : ^T = + let inline sum (array: ^T array) : ^T = checkNonNull "array" array let mutable acc = LanguagePrimitives.GenericZero< ^T> @@ -1582,7 +1585,7 @@ module Array = acc [] - let inline sumBy ([] projection: 'T -> ^U) (array: 'T[]) : ^U = + let inline sumBy ([] projection: 'T -> ^U) (array: 'T array) : ^U = checkNonNull "array" array let mutable acc = LanguagePrimitives.GenericZero< ^U> @@ -1592,7 +1595,7 @@ module Array = acc [] - let inline min (array: _[]) = + let inline min (array: _ array) = checkNonNull "array" array if array.Length = 0 then @@ -1609,7 +1612,7 @@ module Array = acc [] - let inline minBy ([] projection) (array: _[]) = + let inline minBy ([] projection) (array: _ array) = checkNonNull "array" array if array.Length = 0 then @@ -1629,7 +1632,7 @@ module Array = accv [] - let inline max (array: _[]) = + let inline max (array: _ array) = checkNonNull "array" array if array.Length = 0 then @@ -1646,7 +1649,7 @@ module Array = acc [] - let inline maxBy projection (array: _[]) = + let inline maxBy projection (array: _ array) = checkNonNull "array" array if array.Length = 0 then @@ -1666,7 +1669,7 @@ module Array = accv [] - let inline average (array: 'T[]) = + let inline average (array: 'T array) = checkNonNull "array" array if array.Length = 0 then @@ -1680,7 +1683,7 @@ module Array = LanguagePrimitives.DivideByInt< ^T> acc array.Length [] - let inline averageBy ([] projection: 'T -> ^U) (array: 'T[]) : ^U = + let inline averageBy ([] projection: 'T -> ^U) (array: 'T array) : ^U = checkNonNull "array" array if array.Length = 0 then @@ -1694,7 +1697,7 @@ module Array = LanguagePrimitives.DivideByInt< ^U> acc array.Length [] - let inline compareWith ([] comparer: 'T -> 'T -> int) (array1: 'T[]) (array2: 'T[]) = + let inline compareWith ([] comparer: 'T -> 'T -> int) (array1: 'T array) (array2: 'T array) = checkNonNull "array1" array1 checkNonNull "array2" array2 @@ -1719,7 +1722,7 @@ module Array = else 1 [] - let sub (array: 'T[]) (startIndex: int) (count: int) = + let sub (array: 'T array) (startIndex: int) (count: int) = checkNonNull "array" array if startIndex < 0 then @@ -1734,11 +1737,11 @@ module Array = Microsoft.FSharp.Primitives.Basics.Array.subUnchecked startIndex count array [] - let item index (array: _[]) = + let item index (array: _ array) = array.[index] [] - let tryItem index (array: 'T[]) = + let tryItem index (array: 'T array) = checkNonNull "array" array if index < 0 || index >= array.Length then @@ -1747,15 +1750,15 @@ module Array = Some(array.[index]) [] - let get (array: _[]) index = + let get (array: _ array) index = array.[index] [] - let set (array: _[]) index value = + let set (array: _ array) index value = array.[index] <- value [] - let fill (target: 'T[]) (targetIndex: int) (count: int) (value: 'T) = + let fill (target: 'T array) (targetIndex: int) (count: int) (value: 'T) = checkNonNull "target" target if targetIndex < 0 then @@ -1768,7 +1771,7 @@ module Array = target.[i] <- value [] - let exactlyOne (array: 'T[]) = + let exactlyOne (array: 'T array) = checkNonNull "array" array if array.Length = 1 then @@ -1779,7 +1782,7 @@ module Array = invalidArg "array" (SR.GetString(SR.inputSequenceTooLong)) [] - let tryExactlyOne (array: 'T[]) = + let tryExactlyOne (array: 'T array) = checkNonNull "array" array if array.Length = 1 then @@ -1787,7 +1790,7 @@ module Array = else None - let transposeArrays (array: 'T[][]) = + let transposeArrays (array: 'T array array) = let len = array.Length if len = 0 then @@ -1803,7 +1806,7 @@ module Array = (String.Format("array.[{0}]", j)) array.[j].Length - let result: 'T[][] = + let result: 'T array array = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked lenInner for i in 0 .. lenInner - 1 do @@ -1815,15 +1818,15 @@ module Array = result [] - let transpose (arrays: seq<'T[]>) = + let transpose (arrays: seq<'T array>) = checkNonNull "arrays" arrays match arrays with - | :? ('T[][]) as ts -> ts |> transposeArrays // avoid a clone, since we only read the array + | :? ('T array array) as ts -> ts |> transposeArrays // avoid a clone, since we only read the array | _ -> arrays |> Seq.toArray |> transposeArrays [] - let truncate count (array: 'T[]) = + let truncate count (array: 'T array) = checkNonNull "array" array if count <= 0 then @@ -1834,7 +1837,7 @@ module Array = Microsoft.FSharp.Primitives.Basics.Array.subUnchecked 0 count' array [] - let removeAt (index: int) (source: 'T[]) : 'T[] = + let removeAt (index: int) (source: 'T array) : 'T array = checkNonNull "source" source if index < 0 || index >= source.Length then @@ -1852,7 +1855,7 @@ module Array = result [] - let removeManyAt (index: int) (count: int) (source: 'T[]) : 'T[] = + let removeManyAt (index: int) (count: int) (source: 'T array) : 'T array = checkNonNull "source" source if index < 0 || index > source.Length - count then @@ -1870,7 +1873,7 @@ module Array = result [] - let updateAt (index: int) (value: 'T) (source: 'T[]) : 'T[] = + let updateAt (index: int) (value: 'T) (source: 'T array) : 'T array = checkNonNull "source" source if index < 0 || index >= source.Length then @@ -1887,7 +1890,7 @@ module Array = result [] - let insertAt (index: int) (value: 'T) (source: 'T[]) : 'T[] = + let insertAt (index: int) (value: 'T) (source: 'T array) : 'T array = checkNonNull "source" source if index < 0 || index > source.Length then @@ -1907,7 +1910,7 @@ module Array = result [] - let insertManyAt (index: int) (values: seq<'T>) (source: 'T[]) : 'T[] = + let insertManyAt (index: int) (values: seq<'T>) (source: 'T array) : 'T array = checkNonNull "source" source if index < 0 || index > source.Length then @@ -1937,7 +1940,7 @@ module Array = open System.Collections.Concurrent [] - let exists (predicate: 'T -> bool) (array: 'T[]) = + let exists (predicate: 'T -> bool) (array: 'T array) = checkNonNull "array" array Parallel @@ -1952,12 +1955,12 @@ module Array = |> not [] - let forall (predicate: 'T -> bool) (array: 'T[]) = + let forall (predicate: 'T -> bool) (array: 'T array) = // Not exists $condition <==> (opposite of $condition is true forall) exists (predicate >> not) array |> not [] - let tryFindIndex predicate (array: _[]) = + let tryFindIndex predicate (array: _ array) = checkNonNull "array" array let pResult = @@ -1972,11 +1975,11 @@ module Array = pResult.LowestBreakIteration |> Option.ofNullable |> Option.map int [] - let tryFind predicate (array: _[]) = + let tryFind predicate (array: _ array) = array |> tryFindIndex predicate |> Option.map (fun i -> array[i]) [] - let tryPick chooser (array: _[]) = + let tryPick chooser (array: _ array) = checkNonNull "array" array let allChosen = new System.Collections.Concurrent.ConcurrentDictionary<_, _>() @@ -1997,14 +2000,14 @@ module Array = |> Option.bind (fun i -> allChosen[int i]) [] - let choose chooser (array: 'T[]) = + let choose chooser (array: 'T array) = checkNonNull "array" array let inputLength = array.Length - let isChosen: bool[] = + let isChosen: bool array = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked inputLength - let results: 'U[] = + let results: 'U array = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked inputLength let mutable outputLength = 0 @@ -2037,7 +2040,7 @@ module Array = output [] - let collect (mapping: 'T -> 'U[]) (array: 'T[]) : 'U[] = + let collect (mapping: 'T -> 'U array) (array: 'T array) : 'U array = checkNonNull "array" array let inputLength = array.Length @@ -2050,7 +2053,7 @@ module Array = concatArrays result [] - let map (mapping: 'T -> 'U) (array: 'T[]) : 'U[] = + let map (mapping: 'T -> 'U) (array: 'T array) : 'U array = checkNonNull "array" array let inputLength = array.Length @@ -2063,7 +2066,7 @@ module Array = result [] - let mapi mapping (array: 'T[]) = + let mapi mapping (array: 'T array) = checkNonNull "array" array let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(mapping) let inputLength = array.Length @@ -2081,7 +2084,7 @@ module Array = let private maxPartitions = Environment.ProcessorCount // The maximum number of partitions to use let private minChunkSize = 256 // The minimum size of a chunk to be sorted in parallel - let private createPartitionsUpToWithMinChunkSize maxIdxExclusive minChunkSize (array: 'T[]) = + let private createPartitionsUpToWithMinChunkSize maxIdxExclusive minChunkSize (array: 'T array) = [| let chunkSize = match maxIdxExclusive with @@ -2098,13 +2101,13 @@ module Array = yield new ArraySegment<'T>(array, offset, maxIdxExclusive - offset) |] - let private createPartitionsUpTo maxIdxExclusive (array: 'T[]) = + let private createPartitionsUpTo maxIdxExclusive (array: 'T array) = createPartitionsUpToWithMinChunkSize maxIdxExclusive minChunkSize array (* This function is there also as a support vehicle for other aggregations. It is public in order to be called from inlined functions, the benefit of inlining call into it is significant *) [] - let reduceBy (projection: 'T -> 'U) (reduction: 'U -> 'U -> 'U) (array: 'T[]) = + let reduceBy (projection: 'T -> 'U) (reduction: 'U -> 'U -> 'U) (array: 'T array) = checkNonNull "array" array if array.Length = 0 then @@ -2139,7 +2142,7 @@ module Array = finalResult [] - let inline reduce ([] reduction) (array: _[]) = + let inline reduce ([] reduction) (array: _ array) = array |> reduceBy id reduction let inline vFst struct (a, _) = @@ -2149,49 +2152,49 @@ module Array = b [] - let inline minBy ([] projection) (array: _[]) = + let inline minBy ([] projection) (array: _ array) = array |> reduceBy (fun x -> struct (projection x, x)) (fun a b -> if vFst a < vFst b then a else b) |> vSnd [] - let inline min (array: _[]) = + let inline min (array: _ array) = array |> reduce (fun a b -> if a < b then a else b) [] - let inline sumBy ([] projection: 'T -> ^U) (array: 'T[]) : ^U = + let inline sumBy ([] projection: 'T -> ^U) (array: 'T array) : ^U = if array.Length = 0 then LanguagePrimitives.GenericZero else array |> reduceBy projection Operators.Checked.(+) [] - let inline sum (array: ^T[]) : ^T = + let inline sum (array: ^T array) : ^T = array |> sumBy id [] - let inline maxBy projection (array: _[]) = + let inline maxBy projection (array: _ array) = array |> reduceBy (fun x -> struct (projection x, x)) (fun a b -> if vFst a > vFst b then a else b) |> vSnd [] - let inline max (array: _[]) = + let inline max (array: _ array) = array |> reduce (fun a b -> if a > b then a else b) [] - let inline averageBy ([] projection: 'T -> ^U) (array: 'T[]) : ^U = + let inline averageBy ([] projection: 'T -> ^U) (array: 'T array) : ^U = let sum = array |> reduceBy projection Operators.Checked.(+) LanguagePrimitives.DivideByInt sum (array.Length) [] - let inline average (array: 'T[]) = + let inline average (array: 'T array) = array |> averageBy id [] - let zip (array1: _[]) (array2: _[]) = + let zip (array1: _ array) (array2: _ array) = checkNonNull "array1" array1 checkNonNull "array2" array2 let len1 = array1.Length @@ -2219,7 +2222,7 @@ module Array = (comparer: IEqualityComparer<'SafeKey>) ([] keyf: 'T -> 'SafeKey) ([] getKey: 'SafeKey -> 'Key) - (array: 'T[]) + (array: 'T array) = let counts = new ConcurrentDictionary<_, _>( @@ -2255,7 +2258,7 @@ module Array = let mutable finalIdx = 0 let finalResultsLookup = - new Dictionary<'SafeKey, int ref * 'T[]>(capacity = counts.Count, comparer = comparer) + new Dictionary<'SafeKey, int ref * 'T array>(capacity = counts.Count, comparer = comparer) for kvp in counts do let arrayForThisGroup = @@ -2281,7 +2284,7 @@ module Array = finalResults - let groupByValueTypeParallel (keyf: 'T -> 'Key) (array: 'T[]) = + let groupByValueTypeParallel (keyf: 'T -> 'Key) (array: 'T array) = // Is it a bad idea to put floating points as keys for grouping? Yes // But would the implementation fail with KeyNotFound "nan" if we just leave it? Also yes // Here we enforce nan=nan equality to prevent throwing @@ -2297,7 +2300,7 @@ module Array = // Just like in regular Array.groupBy: Wrap a StructBox around all keys in order to avoid nulls // (dotnet doesn't allow null keys in dictionaries) - let groupByRefTypeParallel (keyf: 'T -> 'Key) (array: 'T[]) = + let groupByRefTypeParallel (keyf: 'T -> 'Key) (array: 'T array) = groupByImplParallel RuntimeHelpers.StructBox<'Key>.Comparer (keyf >> RuntimeHelpers.StructBox) @@ -2305,7 +2308,7 @@ module Array = array [] - let groupBy (projection: 'T -> 'Key) (array: 'T[]) = + let groupBy (projection: 'T -> 'Key) (array: 'T array) = checkNonNull "array" array if typeof<'Key>.IsValueType then @@ -2314,12 +2317,12 @@ module Array = groupByRefTypeParallel projection array [] - let iter action (array: 'T[]) = + let iter action (array: 'T array) = checkNonNull "array" array Parallel.For(0, array.Length, (fun i -> action array.[i])) |> ignore [] - let iteri action (array: 'T[]) = + let iteri action (array: 'T array) = checkNonNull "array" array let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(action) Parallel.For(0, array.Length, (fun i -> f.Invoke(i, array.[i]))) |> ignore @@ -2330,7 +2333,7 @@ module Array = Parallel.For(0, count, (fun i -> result.[i] <- initializer i)) |> ignore result - let countAndCollectTrueItems predicate (array: 'T[]) = + let countAndCollectTrueItems predicate (array: 'T array) = checkNonNull "array" array let inputLength = array.Length @@ -2356,7 +2359,7 @@ module Array = trueLength, isTrue [] - let filter predicate (array: 'T[]) = + let filter predicate (array: 'T array) = let trueLength, isTrue = countAndCollectTrueItems predicate array let res = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked trueLength let mutable resIdx = 0 @@ -2369,7 +2372,7 @@ module Array = res [] - let partition predicate (array: 'T[]) = + let partition predicate (array: 'T array) = let trueLength, isTrue = countAndCollectTrueItems predicate array let res1 = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked trueLength @@ -2389,7 +2392,7 @@ module Array = res1, res2 - let private createPartitions (array: 'T[]) = + let private createPartitions (array: 'T array) = createPartitionsUpTo array.Length array let inline pickPivot @@ -2459,7 +2462,7 @@ module Array = let pivotItem = array[pivotIdx] partitionIntoTwo (fun idx -> cmp array[idx] pivotItem) swap orig - let partitionIntoTwoUsingKeys (keys: 'A[]) (orig: ArraySegment<'T>) : ArraySegment<'T> * ArraySegment<'T> = + let partitionIntoTwoUsingKeys (keys: 'A array) (orig: ArraySegment<'T>) : ArraySegment<'T> * ArraySegment<'T> = let array = orig.Array let inline swap i j = @@ -2477,7 +2480,7 @@ module Array = partitionIntoTwo (fun idx -> compare keys[idx] pivotKey) swap orig let inline sortInPlaceHelper - (array: 'T[]) + (array: 'T array) ([] partitioningFunc: ArraySegment<'T> -> ArraySegment<'T> * ArraySegment<'T>) ([] sortingFunc: ArraySegment<'T> -> unit) = @@ -2513,7 +2516,7 @@ module Array = let sortInPlaceWithHelper (partitioningComparer: 'T -> 'T -> int) (sortingComparer: IComparer<'T>) - (inputArray: 'T[]) + (inputArray: 'T array) = let partitioningFunc = partitionIntoTwoUsingComparer partitioningComparer @@ -2522,7 +2525,7 @@ module Array = sortInPlaceHelper inputArray partitioningFunc sortingFunc - let sortKeysAndValuesInPlace (inputKeys: 'TKey[]) (values: 'TValue[]) = + let sortKeysAndValuesInPlace (inputKeys: 'TKey array) (values: 'TValue array) = let partitioningFunc = partitionIntoTwoUsingKeys inputKeys let sortingComparer = LanguagePrimitives.FastGenericComparerCanBeNull<'TKey> @@ -2533,16 +2536,16 @@ module Array = sortInPlaceHelper values partitioningFunc sortingFunc [] - let sortInPlaceWith comparer (array: 'T[]) = + let sortInPlaceWith comparer (array: 'T array) = checkNonNull "array" array let sortingComparer = ComparisonIdentity.FromFunction(comparer) sortInPlaceWithHelper comparer sortingComparer array [] - let sortInPlaceBy (projection: 'T -> 'U) (array: 'T[]) = + let sortInPlaceBy (projection: 'T -> 'U) (array: 'T array) = checkNonNull "array" array - let inputKeys: 'U[] = + let inputKeys: 'U array = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked array.Length let partitions = createPartitions array @@ -2561,7 +2564,7 @@ module Array = sortKeysAndValuesInPlace inputKeys array [] - let sortInPlace (array: 'T[]) = + let sortInPlace (array: 'T array) = checkNonNull "array" array let sortingComparer: IComparer<'T> = @@ -2571,13 +2574,13 @@ module Array = sortInPlaceWithHelper partioningFunc sortingComparer array [] - let sortWith (comparer: 'T -> 'T -> int) (array: 'T[]) = + let sortWith (comparer: 'T -> 'T -> int) (array: 'T array) = let result = copy array sortInPlaceWith comparer result result [] - let sortBy projection (array: 'T[]) = + let sortBy projection (array: 'T array) = checkNonNull "array" array let inputKeys = @@ -2609,7 +2612,7 @@ module Array = sortInPlace result result - let reverseInPlace (array: 'T[]) = + let reverseInPlace (array: 'T array) = let segments = createPartitionsUpTo (array.Length / 2) array let lastIdx = array.Length - 1 diff --git a/src/FSharp.Core/array.fsi b/src/FSharp.Core/array.fsi index 7ac42219058..8a5d51f0c9e 100644 --- a/src/FSharp.Core/array.fsi +++ b/src/FSharp.Core/array.fsi @@ -35,7 +35,7 @@ module Array = /// /// [] - val allPairs: array1: 'T1[] -> array2: 'T2[] -> ('T1 * 'T2)[] + val allPairs: array1: 'T1 array -> array2: 'T2 array -> ('T1 * 'T2) array /// Builds a new array that contains the elements of the first array followed by the elements of the second array. /// @@ -53,7 +53,7 @@ module Array = /// Evaluates to [| 1; 2; 3; 4 |]. /// [] - val append: array1: 'T[] -> array2: 'T[] -> 'T[] + val append: array1: 'T array -> array2: 'T array -> 'T array /// Returns the average of the elements in the array. /// @@ -79,7 +79,7 @@ module Array = /// [] val inline average: - array: ^T[] -> ^T + array: ^T array -> ^T when ^T: (static member (+): ^T * ^T -> ^T) and ^T: (static member DivideByInt: ^T * int -> ^T) and ^T: (static member Zero: ^T) @@ -110,7 +110,7 @@ module Array = /// /// type Foo = { Bar: float } /// - /// let input : Foo[] = [| |] + /// let input : Foo array = [| |] /// /// input |> Array.averageBy (fun foo -> foo.Bar) /// @@ -118,7 +118,7 @@ module Array = /// [] val inline averageBy: - projection: ('T -> ^U) -> array: 'T[] -> ^U + projection: ('T -> ^U) -> array: 'T array -> ^U when ^U: (static member (+): ^U * ^U -> ^U) and ^U: (static member DivideByInt: ^U * int -> ^U) and ^U: (static member Zero: ^U) @@ -154,7 +154,7 @@ module Array = /// After evaluation target contains [| 0; 1; 2; 13; 14; 5 |]. /// [] - val inline blit: source: 'T[] -> sourceIndex: int -> target: 'T[] -> targetIndex: int -> count: int -> unit + val inline blit: source: 'T array -> sourceIndex: int -> target: 'T array -> targetIndex: int -> count: int -> unit /// For each element of the array, applies the given function. Concatenates all the results and return the combined array. /// @@ -167,7 +167,7 @@ module Array = /// /// /// - /// type Foo = { Bar: int[] } + /// type Foo = { Bar: int array } /// /// let input = [| {Bar = [| 1; 2 |]}; {Bar = [| 3; 4 |]} |] /// @@ -185,7 +185,7 @@ module Array = /// Evaluates to [| 1; 2; 3; 4 |] /// [] - val collect: mapping: ('T -> 'U[]) -> array: 'T[] -> 'U[] + val collect: mapping: ('T -> 'U array) -> array: 'T array -> 'U array /// Compares two arrays using the given comparison function, element by element. /// @@ -268,7 +268,7 @@ module Array = /// Evaluates to -1 /// [] - val inline compareWith: comparer: ('T -> 'T -> int) -> array1: 'T[] -> array2: 'T[] -> int + val inline compareWith: comparer: ('T -> 'T -> int) -> array1: 'T array -> array2: 'T array -> int /// Builds a new array that contains the elements of each of the given sequence of arrays. /// @@ -287,7 +287,7 @@ module Array = /// Evaluates to [| 1; 2; 3; 4; 5 |] /// [] - val concat: arrays: seq<'T[]> -> 'T[] + val concat: arrays: seq<'T array> -> 'T array /// Tests if the array contains the specified element. /// @@ -305,7 +305,7 @@ module Array = /// /// [] - val inline contains: value: 'T -> array: 'T[] -> bool when 'T: equality + val inline contains: value: 'T -> array: 'T array -> bool when 'T: equality /// Builds a new array that contains the elements of the given array. /// @@ -324,7 +324,7 @@ module Array = /// Evaluates to a new array containing[| 12; 13; 14 |]. /// [] - val copy: array: 'T[] -> 'T[] + val copy: array: 'T array -> 'T array /// Applies a key-generating function to each element of an array and returns an array yielding unique /// keys and their number of occurrences in the original array. @@ -348,7 +348,7 @@ module Array = /// Evaluates to [| ("a", 2); ("b", 1) |] /// [] - val countBy: projection: ('T -> 'Key) -> array: 'T[] -> ('Key * int)[] when 'Key: equality + val countBy: projection: ('T -> 'Key) -> array: 'T array -> ('Key * int) array when 'Key: equality /// Creates an array whose elements are all initially the given value. /// @@ -378,7 +378,7 @@ module Array = /// Note each entry in the array is the same mutable cell object. /// [] - val create: count: int -> value: 'T -> 'T[] + val create: count: int -> value: 'T -> 'T array /// Returns the first element of the array, or /// None if the array is empty. @@ -400,14 +400,14 @@ module Array = /// /// /// - /// let inputs : int[] = [| |] + /// let inputs : int array = [| |] /// /// inputs |> Array.tryHead /// /// Evaluates to None /// [] - val tryHead: array: 'T[] -> 'T option + val tryHead: array: 'T array -> 'T option /// Applies the given function to successive elements, returning the first /// result where the function returns Some(x) for some x. If the function @@ -439,7 +439,7 @@ module Array = /// /// [] - val tryPick: chooser: ('T -> 'U option) -> array: 'T[] -> 'U option + val tryPick: chooser: ('T -> 'U option) -> array: 'T array -> 'U option /// Fills a range of elements of the array with the given value. /// @@ -460,7 +460,7 @@ module Array = /// After evaluation target contains [| 0; 1; 2; 100; 100; 5 |]. /// [] - val fill: target: 'T[] -> targetIndex: int -> count: int -> value: 'T -> unit + val fill: target: 'T array -> targetIndex: int -> count: int -> value: 'T -> unit /// Applies the given function to successive elements, returning the first /// result where the function returns Some(x) for some x. If the function @@ -494,7 +494,7 @@ module Array = /// /// [] - val pick: chooser: ('T -> 'U option) -> array: 'T[] -> 'U + val pick: chooser: ('T -> 'U option) -> array: 'T array -> 'U /// Applies the given function to each element of the array. Returns /// the array comprised of the results x for each element where @@ -525,7 +525,7 @@ module Array = /// Evaluates to [| 2 |] /// [] - val choose: chooser: ('T -> 'U option) -> array: 'T[] -> 'U[] + val choose: chooser: ('T -> 'U option) -> array: 'T array -> 'U array /// Divides the input array into chunks of size at most chunkSize. /// @@ -555,7 +555,7 @@ module Array = /// Throws ArgumentException /// [] - val chunkBySize: chunkSize: int -> array: 'T[] -> 'T[][] + val chunkBySize: chunkSize: int -> array: 'T array -> 'T array array /// Returns an array that contains no duplicate entries according to generic hash and /// equality comparisons on the entries. @@ -576,7 +576,7 @@ module Array = /// Evaluates to [| 1; 2; 3 |] /// [] - val distinct: array: 'T[] -> 'T[] when 'T: equality + val distinct: array: 'T array -> 'T array when 'T: equality /// Returns an array that contains no duplicate entries according to the /// generic hash and equality comparisons on the keys returned by the given key-generating function. @@ -598,7 +598,7 @@ module Array = /// Evaluates to [| { Bar = 1 }; { Bar = 2 }; { Bar = 3 } |] /// [] - val distinctBy: projection: ('T -> 'Key) -> array: 'T[] -> 'T[] when 'Key: equality + val distinctBy: projection: ('T -> 'Key) -> array: 'T array -> 'T array when 'Key: equality /// Splits the input array into at most count chunks. /// @@ -628,7 +628,7 @@ module Array = /// Throws ArgumentException /// [] - val splitInto: count: int -> array: 'T[] -> 'T[][] + val splitInto: count: int -> array: 'T array -> 'T array array /// Returns an empty array of the given type. /// The empty array. @@ -640,7 +640,7 @@ module Array = /// [] [] - val empty<'T> : 'T[] + val empty<'T> : 'T array /// Returns the only element of the array. /// @@ -671,14 +671,14 @@ module Array = /// /// /// - /// let inputs: int[] = [| |] + /// let inputs: int array = [| |] /// /// inputs |> Array.exactlyOne /// /// Throws ArgumentException /// [] - val exactlyOne: array: 'T[] -> 'T + val exactlyOne: array: 'T array -> 'T /// Returns the only element of the array or None if array is empty or contains more than one element. /// @@ -708,14 +708,14 @@ module Array = /// /// /// - /// let inputs: int[] = [| |] + /// let inputs: int array = [| |] /// /// inputs |> Array.tryExactlyOne /// /// Evaluates to None /// [] - val tryExactlyOne: array: 'T[] -> 'T option + val tryExactlyOne: array: 'T array -> 'T option /// Returns a new list with the distinct elements of the input array which do not appear in the itemsToExclude sequence, /// using generic hash and equality comparisons to compare values. @@ -738,7 +738,7 @@ module Array = /// Evaluates to [| 2; 4 |] /// [] - val except: itemsToExclude: seq<'T> -> array: 'T[] -> 'T[] when 'T: equality + val except: itemsToExclude: seq<'T> -> array: 'T array -> 'T array when 'T: equality /// Tests if any element of the array satisfies the given predicate. /// @@ -771,7 +771,7 @@ module Array = /// Evaluates to false /// [] - val inline exists: predicate: ('T -> bool) -> array: 'T[] -> bool + val inline exists: predicate: ('T -> bool) -> array: 'T array -> bool /// Tests if any pair of corresponding elements of the arrays satisfies the given predicate. /// @@ -810,7 +810,7 @@ module Array = /// Evaluates to true /// [] - val exists2: predicate: ('T1 -> 'T2 -> bool) -> array1: 'T1[] -> array2: 'T2[] -> bool + val exists2: predicate: ('T1 -> 'T2 -> bool) -> array1: 'T1 array -> array2: 'T2 array -> bool /// Returns a new collection containing only the elements of the collection /// for which the given predicate returns "true". @@ -831,7 +831,7 @@ module Array = /// Evaluates to [| 2; 4 |] /// [] - val filter: predicate: ('T -> bool) -> array: 'T[] -> 'T[] + val filter: predicate: ('T -> bool) -> array: 'T array -> 'T array /// Returns the first element for which the given function returns 'true'. /// Raise if no such element exists. @@ -863,7 +863,7 @@ module Array = /// Throws KeyNotFoundException /// [] - val find: predicate: ('T -> bool) -> array: 'T[] -> 'T + val find: predicate: ('T -> bool) -> array: 'T array -> 'T /// Returns the last element for which the given function returns 'true'. /// Raise if no such element exists. @@ -895,7 +895,7 @@ module Array = /// Throws KeyNotFoundException /// [] - val findBack: predicate: ('T -> bool) -> array: 'T[] -> 'T + val findBack: predicate: ('T -> bool) -> array: 'T array -> 'T /// Returns the index of the first element in the array /// that satisfies the given predicate. Raise if @@ -927,7 +927,7 @@ module Array = /// Throws KeyNotFoundException /// [] - val findIndex: predicate: ('T -> bool) -> array: 'T[] -> int + val findIndex: predicate: ('T -> bool) -> array: 'T array -> int /// Returns the index of the last element in the array /// that satisfies the given predicate. Raise if @@ -960,7 +960,7 @@ module Array = /// Throws KeyNotFoundException /// [] - val findIndexBack: predicate: ('T -> bool) -> array: 'T[] -> int + val findIndexBack: predicate: ('T -> bool) -> array: 'T array -> int /// Tests if all elements of the array satisfy the given predicate. /// @@ -985,7 +985,7 @@ module Array = /// /// [] - val forall: predicate: ('T -> bool) -> array: 'T[] -> bool + val forall: predicate: ('T -> bool) -> array: 'T array -> bool /// Tests if all corresponding elements of the array satisfy the given predicate pairwise. /// @@ -1034,7 +1034,7 @@ module Array = /// Throws ArgumentException. /// [] - val forall2: predicate: ('T1 -> 'T2 -> bool) -> array1: 'T1[] -> array2: 'T2[] -> bool + val forall2: predicate: ('T1 -> 'T2 -> bool) -> array1: 'T1 array -> array2: 'T2 array -> bool /// Applies a function to each element of the collection, threading an accumulator argument /// through the computation. If the input function is f and the elements are i0...iN then computes @@ -1064,7 +1064,7 @@ module Array = /// Evaluates to 2 /// [] - val fold<'T, 'State> : folder: ('State -> 'T -> 'State) -> state: 'State -> array: 'T[] -> 'State + val fold<'T, 'State> : folder: ('State -> 'T -> 'State) -> state: 'State -> array: 'T array -> 'State /// Applies a function to each element of the array, starting from the end, threading an accumulator argument /// through the computation. If the input function is f and the elements are i0...iN then computes @@ -1107,7 +1107,7 @@ module Array = /// /// [] - val foldBack<'T, 'State> : folder: ('T -> 'State -> 'State) -> array: 'T[] -> state: 'State -> 'State + val foldBack<'T, 'State> : folder: ('T -> 'State -> 'State) -> array: 'T array -> state: 'State -> 'State /// Applies a function to pairs of elements drawn from the two collections, /// left-to-right, threading an accumulator argument @@ -1142,7 +1142,7 @@ module Array = /// [] val fold2<'T1, 'T2, 'State> : - folder: ('State -> 'T1 -> 'T2 -> 'State) -> state: 'State -> array1: 'T1[] -> array2: 'T2[] -> 'State + folder: ('State -> 'T1 -> 'T2 -> 'State) -> state: 'State -> array1: 'T1 array -> array2: 'T2 array -> 'State /// Apply a function to pairs of elements drawn from the two collections, right-to-left, /// threading an accumulator argument through the computation. The two input @@ -1191,7 +1191,7 @@ module Array = /// [] val foldBack2<'T1, 'T2, 'State> : - folder: ('T1 -> 'T2 -> 'State -> 'State) -> array1: 'T1[] -> array2: 'T2[] -> state: 'State -> 'State + folder: ('T1 -> 'T2 -> 'State -> 'State) -> array1: 'T1 array -> array2: 'T2 array -> state: 'State -> 'State /// Gets an element from an array. /// @@ -1223,7 +1223,7 @@ module Array = /// Throws IndexOutOfRangeException /// [] - val get: array: 'T[] -> index: int -> 'T + val get: array: 'T array -> index: int -> 'T /// Returns the first element of the array. /// @@ -1250,7 +1250,7 @@ module Array = /// Throws ArgumentException /// [] - val head: array: 'T[] -> 'T + val head: array: 'T array -> 'T /// Applies a key-generating function to each element of an array and yields an array of /// unique keys. Each unique key contains an array of all elements that match @@ -1272,7 +1272,7 @@ module Array = /// Evaluates to [| (1, [| 1; 3; 5 |]); (0, [| 2; 4 |]) |] /// [] - val groupBy: projection: ('T -> 'Key) -> array: 'T[] -> ('Key * 'T[])[] when 'Key: equality + val groupBy: projection: ('T -> 'Key) -> array: 'T array -> ('Key * 'T array) array when 'Key: equality /// Builds a new array whose elements are the corresponding elements of the input array /// paired with the integer index (from 0) of each element. @@ -1292,7 +1292,7 @@ module Array = /// Evaluates to [| (0, "a"); (1, "b"); (2, "c") |] /// [] - val indexed: array: 'T[] -> (int * 'T)[] + val indexed: array: 'T array -> (int * 'T) array /// Creates an array given the dimension and a generator function to compute the elements. /// @@ -1317,7 +1317,7 @@ module Array = /// Throws ArgumentException /// [] - val inline init: count: int -> initializer: (int -> 'T) -> 'T[] + val inline init: count: int -> initializer: (int -> 'T) -> 'T array /// Creates an array where the entries are initially the default value Unchecked.defaultof<'T>. /// @@ -1329,12 +1329,12 @@ module Array = /// /// /// - /// let arr : int[] = Array.zeroCreate 4 + /// let arr : int array = Array.zeroCreate 4 /// /// Evaluates to [| 0; 0; 0; 0 |] /// [] - val zeroCreate: count: int -> 'T[] + val zeroCreate: count: int -> 'T array /// Returns true if the given array is empty, otherwise false. /// @@ -1358,7 +1358,7 @@ module Array = /// Evaluates to false /// [] - val isEmpty: array: 'T[] -> bool + val isEmpty: array: 'T array -> bool /// Applies the given function to each element of the array. /// @@ -1382,7 +1382,7 @@ module Array = /// in the console. /// [] - val inline iter: action: ('T -> unit) -> array: 'T[] -> unit + val inline iter: action: ('T -> unit) -> array: 'T array -> unit /// Applies the given function to pair of elements drawn from matching indices in two arrays. The /// two arrays must have the same lengths, otherwise an ArgumentException is @@ -1411,7 +1411,7 @@ module Array = /// in the console. /// [] - val iter2: action: ('T1 -> 'T2 -> unit) -> array1: 'T1[] -> array2: 'T2[] -> unit + val iter2: action: ('T1 -> 'T2 -> unit) -> array1: 'T1 array -> array2: 'T2 array -> unit /// Applies the given function to each element of the array. The integer passed to the /// function indicates the index of element. @@ -1436,7 +1436,7 @@ module Array = /// in the console. /// [] - val iteri: action: (int -> 'T -> unit) -> array: 'T[] -> unit + val iteri: action: (int -> 'T -> unit) -> array: 'T array -> unit /// Applies the given function to pair of elements drawn from matching indices in two arrays, /// also passing the index of the elements. The two arrays must have the same lengths, @@ -1465,7 +1465,7 @@ module Array = /// in the console. /// [] - val iteri2: action: (int -> 'T1 -> 'T2 -> unit) -> array1: 'T1[] -> array2: 'T2[] -> unit + val iteri2: action: (int -> 'T1 -> 'T2 -> unit) -> array1: 'T1 array -> array2: 'T2 array -> unit /// Returns the last element of the array. /// @@ -1490,7 +1490,7 @@ module Array = /// Throws ArgumentException /// [] - val inline last: array: 'T[] -> 'T + val inline last: array: 'T array -> 'T /// Gets an element from an array. /// @@ -1522,7 +1522,7 @@ module Array = /// Throws ArgumentException /// [] - val item: index: int -> array: 'T[] -> 'T + val item: index: int -> array: 'T array -> 'T /// Returns the length of an array. You can also use property arr.Length. /// @@ -1543,7 +1543,7 @@ module Array = /// Evaluates to 3 /// [] - val length: array: 'T[] -> int + val length: array: 'T array -> int /// Returns the last element of the array. /// Return None if no such element exists. @@ -1568,7 +1568,7 @@ module Array = /// Evaluates to None /// [] - val tryLast: array: 'T[] -> 'T option + val tryLast: array: 'T array -> 'T option /// Builds a new array whose elements are the results of applying the given function /// to each of the elements of the array. @@ -1589,7 +1589,7 @@ module Array = /// Evaluates to [| 1; 3; 2 |] /// [] - val inline map: mapping: ('T -> 'U) -> array: 'T[] -> 'U[] + val inline map: mapping: ('T -> 'U) -> array: 'T array -> 'U array /// Builds a new collection whose elements are the results of applying the given function /// to the corresponding elements of the two collections pairwise. The two input @@ -1615,7 +1615,7 @@ module Array = /// Evaluates to [| 'a'; 'd'; 'o' |] /// [] - val map2: mapping: ('T1 -> 'T2 -> 'U) -> array1: 'T1[] -> array2: 'T2[] -> 'U[] + val map2: mapping: ('T1 -> 'T2 -> 'U) -> array1: 'T1 array -> array2: 'T2 array -> 'U array /// Combines map and fold. Builds a new array whose elements are the results of applying the given function /// to each of the elements of the input array. The function is also used to accumulate a final value. @@ -1646,7 +1646,7 @@ module Array = /// [] val mapFold<'T, 'State, 'Result> : - mapping: ('State -> 'T -> 'Result * 'State) -> state: 'State -> array: 'T[] -> 'Result[] * 'State + mapping: ('State -> 'T -> 'Result * 'State) -> state: 'State -> array: 'T array -> 'Result array * 'State /// Combines map and foldBack. Builds a new array whose elements are the results of applying the given function /// to each of the elements of the input array. The function is also used to accumulate a final value. @@ -1677,7 +1677,7 @@ module Array = /// [] val mapFoldBack<'T, 'State, 'Result> : - mapping: ('T -> 'State -> 'Result * 'State) -> array: 'T[] -> state: 'State -> 'Result[] * 'State + mapping: ('T -> 'State -> 'Result * 'State) -> array: 'T array -> state: 'State -> 'Result array * 'State /// Builds a new collection whose elements are the results of applying the given function /// to the corresponding triples from the three collections. The three input @@ -1706,7 +1706,8 @@ module Array = /// /// [] - val map3: mapping: ('T1 -> 'T2 -> 'T3 -> 'U) -> array1: 'T1[] -> array2: 'T2[] -> array3: 'T3[] -> 'U[] + val map3: + mapping: ('T1 -> 'T2 -> 'T3 -> 'U) -> array1: 'T1 array -> array2: 'T2 array -> array3: 'T3 array -> 'U array /// Builds a new collection whose elements are the results of applying the given function /// to the corresponding elements of the two collections pairwise, also passing the index of @@ -1732,7 +1733,7 @@ module Array = /// Evaluates to [|(0, 'a'); (1, 'd'); (2, 'o')|] /// [] - val mapi2: mapping: (int -> 'T1 -> 'T2 -> 'U) -> array1: 'T1[] -> array2: 'T2[] -> 'U[] + val mapi2: mapping: (int -> 'T1 -> 'T2 -> 'U) -> array1: 'T1 array -> array2: 'T2 array -> 'U array /// Builds a new array whose elements are the results of applying the given function /// to each of the elements of the array. The integer index passed to the @@ -1754,7 +1755,7 @@ module Array = /// Evaluates to [| 10; 11; 12 |] /// [] - val mapi: mapping: (int -> 'T -> 'U) -> array: 'T[] -> 'U[] + val mapi: mapping: (int -> 'T -> 'U) -> array: 'T array -> 'U array /// Returns the greatest of all elements of the array, compared via Operators.max on the function result. /// @@ -1778,14 +1779,14 @@ module Array = /// /// /// - /// let inputs: int[]= [| |] + /// let inputs: int array= [| |] /// /// inputs |> Array.max /// /// Throws System.ArgumentException. /// [] - val inline max: array: 'T[] -> 'T when 'T: comparison + val inline max: array: 'T array -> 'T when 'T: comparison /// Returns the greatest of all elements of the array, compared via Operators.max on the function result. /// @@ -1810,14 +1811,14 @@ module Array = /// /// /// - /// let inputs: string[]= [| |] + /// let inputs: string array= [| |] /// /// inputs |> Array.maxBy (fun s -> s.Length) /// /// Throws System.ArgumentException. /// [] - val inline maxBy: projection: ('T -> 'U) -> array: 'T[] -> 'T when 'U: comparison + val inline maxBy: projection: ('T -> 'U) -> array: 'T array -> 'T when 'U: comparison /// Returns the lowest of all elements of the array, compared via Operators.min. /// @@ -1841,14 +1842,14 @@ module Array = /// /// /// - /// let inputs: int[]= [| |] + /// let inputs: int array= [| |] /// /// inputs |> Array.min /// /// Throws System.ArgumentException. /// [] - val inline min: array: 'T[] -> 'T when 'T: comparison + val inline min: array: 'T array -> 'T when 'T: comparison /// Returns the lowest of all elements of the array, compared via Operators.min on the function result. /// @@ -1873,14 +1874,14 @@ module Array = /// /// /// - /// let inputs: string[]= [| |] + /// let inputs: string array= [| |] /// /// inputs |> Array.minBy (fun s -> s.Length) /// /// Throws System.ArgumentException. /// [] - val inline minBy: projection: ('T -> 'U) -> array: 'T[] -> 'T when 'U: comparison + val inline minBy: projection: ('T -> 'U) -> array: 'T array -> 'T when 'U: comparison /// Builds an array from the given list. /// @@ -1897,7 +1898,7 @@ module Array = /// Evaluates to [| 1; 2; 5 |]. /// [] - val ofList: list: 'T list -> 'T[] + val ofList: list: 'T list -> 'T array /// Builds a new array from the given enumerable object. /// @@ -1916,7 +1917,7 @@ module Array = /// Evaluates to [| 1; 2; 5 |]. /// [] - val ofSeq: source: seq<'T> -> 'T[] + val ofSeq: source: seq<'T> -> 'T array /// Returns an array of each element in the input array and its predecessor, with the /// exception of the first element which is only returned as the predecessor of the second element. @@ -1936,7 +1937,7 @@ module Array = /// Evaluates to [|(1, 2); (2, 3); (3, 4)|]. /// [] - val pairwise: array: 'T[] -> ('T * 'T)[] + val pairwise: array: 'T array -> ('T * 'T) array /// Splits the collection into two collections, containing the /// elements for which the given predicate returns "true" and "false" @@ -1959,7 +1960,7 @@ module Array = /// Evaluates to ([|2; 4|], [|1; 3|]). /// [] - val partition: predicate: ('T -> bool) -> array: 'T[] -> 'T[] * 'T[] + val partition: predicate: ('T -> bool) -> array: 'T array -> 'T array * 'T array /// Returns an array with all elements permuted according to the /// specified permutation. @@ -1981,7 +1982,7 @@ module Array = /// Evaluates to [|4; 1; 2; 3|]. /// [] - val permute: indexMap: (int -> int) -> array: 'T[] -> 'T[] + val permute: indexMap: (int -> int) -> array: 'T array -> 'T array /// Applies a function to each element of the array, threading an accumulator argument /// through the computation. If the input function is f and the elements are i0...iN @@ -2005,7 +2006,7 @@ module Array = /// Evaluates to 1342, by computing ((1 * 10 + 3) * 10 + 4) * 10 + 2 /// [] - val reduce: reduction: ('T -> 'T -> 'T) -> array: 'T[] -> 'T + val reduce: reduction: ('T -> 'T -> 'T) -> array: 'T array -> 'T /// Applies a function to each element of the array, starting from the end, threading an accumulator argument /// through the computation. If the input function is f and the elements are i0...iN @@ -2029,7 +2030,7 @@ module Array = /// Evaluates to 2431, by computing 1 + (3 + (4 + 2 * 10) * 10) * 10 /// [] - val reduceBack: reduction: ('T -> 'T -> 'T) -> array: 'T[] -> 'T + val reduceBack: reduction: ('T -> 'T -> 'T) -> array: 'T array -> 'T /// Creates an array by replicating the given initial value. /// @@ -2047,7 +2048,7 @@ module Array = /// Evaluates to [| "a"; "a"; "a" |]. /// [] - val replicate: count: int -> initial: 'T -> 'T[] + val replicate: count: int -> initial: 'T -> 'T array /// Returns a new array with the elements in reverse order. /// @@ -2064,7 +2065,7 @@ module Array = /// Evaluates to [| 2; 1; 0 |]. /// [] - val rev: array: 'T[] -> 'T[] + val rev: array: 'T array -> 'T array /// Like fold, but return the intermediary and final results. /// @@ -2093,7 +2094,7 @@ module Array = /// state, 1 the next state, -1 the next state, and 2 the final state. /// [] - val scan<'T, 'State> : folder: ('State -> 'T -> 'State) -> state: 'State -> array: 'T[] -> 'State[] + val scan<'T, 'State> : folder: ('State -> 'T -> 'State) -> state: 'State -> array: 'T array -> 'State array /// Like foldBack, but return both the intermediary and final results. /// @@ -2122,7 +2123,7 @@ module Array = /// state, 3 the next state, 1 the next state, and 2 the final state. /// [] - val scanBack<'T, 'State> : folder: ('T -> 'State -> 'State) -> array: 'T[] -> state: 'State -> 'State[] + val scanBack<'T, 'State> : folder: ('T -> 'State -> 'State) -> array: 'T array -> state: 'State -> 'State array /// Returns an array that contains one item only. /// @@ -2137,7 +2138,7 @@ module Array = /// Evaluates to [| 7 |]. /// [] - val inline singleton: value: 'T -> 'T[] + val inline singleton: value: 'T -> 'T array /// Sets an element of an array. /// @@ -2166,7 +2167,7 @@ module Array = /// Throws IndexOutOfRangeException /// [] - val set: array: 'T[] -> index: int -> value: 'T -> unit + val set: array: 'T array -> index: int -> value: 'T -> unit /// Builds a new array that contains the elements of the given array, excluding the first N elements. /// @@ -2206,7 +2207,7 @@ module Array = /// Evaluates to [| "a"; "b"; "c"; "d" |]. /// [] - val skip: count: int -> array: 'T[] -> 'T[] + val skip: count: int -> array: 'T array -> 'T array /// Bypasses elements in an array while the given predicate returns True, and then returns /// the remaining elements in a new array. @@ -2228,7 +2229,7 @@ module Array = /// /// [] - val skipWhile: predicate: ('T -> bool) -> array: 'T[] -> 'T[] + val skipWhile: predicate: ('T -> bool) -> array: 'T array -> 'T array /// Builds a new array that contains the given subrange specified by /// starting index and length. @@ -2261,7 +2262,7 @@ module Array = /// Evaluates to [| 2; 3; 4 |]. /// [] - val sub: array: 'T[] -> startIndex: int -> count: int -> 'T[] + val sub: array: 'T array -> startIndex: int -> count: int -> 'T array /// Sorts the elements of an array, returning a new array. Elements are compared using . /// @@ -2283,7 +2284,7 @@ module Array = /// Evaluates to [| 1; 1; 3; 4; 6; 8 |]. /// [] - val sort: array: 'T[] -> 'T[] when 'T: comparison + val sort: array: 'T array -> 'T array when 'T: comparison /// Sorts the elements of an array, using the given projection for the keys and returning a new array. /// Elements are compared using . @@ -2307,7 +2308,7 @@ module Array = /// Evaluates to [|"a"; "dd"; "bbb"; "cccc"|]. /// [] - val sortBy: projection: ('T -> 'Key) -> array: 'T[] -> 'T[] when 'Key: comparison + val sortBy: projection: ('T -> 'Key) -> array: 'T array -> 'T array when 'Key: comparison /// Sorts the elements of an array, using the given comparison function as the order, returning a new array. /// @@ -2335,7 +2336,7 @@ module Array = /// Evaluates to [|(0, "aa"); (2, "cc"); (3, "dd"); (1, "bbb")|]. /// [] - val sortWith: comparer: ('T -> 'T -> int) -> array: 'T[] -> 'T[] + val sortWith: comparer: ('T -> 'T -> int) -> array: 'T array -> 'T array /// Sorts the elements of an array by mutating the array in-place, using the given projection for the keys. /// Elements are compared using . @@ -2357,7 +2358,7 @@ module Array = /// After evaluation array contains [|"a"; "dd"; "bbb"; "cccc"|]. /// [] - val sortInPlaceBy: projection: ('T -> 'Key) -> array: 'T[] -> unit when 'Key: comparison + val sortInPlaceBy: projection: ('T -> 'Key) -> array: 'T array -> unit when 'Key: comparison /// Sorts the elements of an array by mutating the array in-place, using the given comparison function as the order. /// @@ -2380,7 +2381,7 @@ module Array = /// After evaluation array contains [|(0, "aa"); (2, "cc"); (3, "dd"); (1, "bbb")|]. /// [] - val sortInPlaceWith: comparer: ('T -> 'T -> int) -> array: 'T[] -> unit + val sortInPlaceWith: comparer: ('T -> 'T -> int) -> array: 'T array -> unit /// Sorts the elements of an array by mutating the array in-place, using the given comparison function. /// Elements are compared using . @@ -2398,7 +2399,7 @@ module Array = /// After evaluation array contains [| 1; 1; 3; 4; 6; 8 |]. /// [] - val sortInPlace: array: 'T[] -> unit when 'T: comparison + val sortInPlace: array: 'T array -> unit when 'T: comparison /// Splits an array into two arrays, at the given index. /// @@ -2420,7 +2421,7 @@ module Array = /// Evaluates front to [|8; 4; 3|] and back to [|1; 6; 1|]. /// [] - val splitAt: index: int -> array: 'T[] -> ('T[] * 'T[]) + val splitAt: index: int -> array: 'T array -> ('T array * 'T array) /// Sorts the elements of an array, in descending order, returning a new array. Elements are compared using . /// @@ -2440,7 +2441,7 @@ module Array = /// Evaluates to [| 8; 6; 4; 3; 1; 1 |]. /// [] - val inline sortDescending: array: 'T[] -> 'T[] when 'T: comparison + val inline sortDescending: array: 'T array -> 'T array when 'T: comparison /// Sorts the elements of an array, in descending order, using the given projection for the keys and returning a new array. /// Elements are compared using . @@ -2462,7 +2463,7 @@ module Array = /// Evaluates to [|"cccc"; "bbb"; "dd"; "a"|]. /// [] - val inline sortByDescending: projection: ('T -> 'Key) -> array: 'T[] -> 'T[] when 'Key: comparison + val inline sortByDescending: projection: ('T -> 'Key) -> array: 'T array -> 'T array when 'Key: comparison /// Returns the sum of the elements in the array. /// @@ -2481,7 +2482,7 @@ module Array = /// Evaluates to 11. /// [] - val inline sum: array: ^T[] -> ^T when ^T: (static member (+): ^T * ^T -> ^T) and ^T: (static member Zero: ^T) + val inline sum: array: ^T array -> ^T when ^T: (static member (+): ^T * ^T -> ^T) and ^T: (static member Zero: ^T) /// Returns the sum of the results generated by applying the function to each element of the array. /// @@ -2502,7 +2503,7 @@ module Array = /// [] val inline sumBy: - projection: ('T -> ^U) -> array: 'T[] -> ^U + projection: ('T -> ^U) -> array: 'T array -> ^U when ^U: (static member (+): ^U * ^U -> ^U) and ^U: (static member Zero: ^U) /// Returns the first N elements of the array. @@ -2547,7 +2548,7 @@ module Array = /// Evaluates to [| |]. /// [] - val take: count: int -> array: 'T[] -> 'T[] + val take: count: int -> array: 'T array -> 'T array /// Returns an array that contains all elements of the original array while the /// given predicate returns True, and then returns no further elements. @@ -2569,7 +2570,7 @@ module Array = /// /// [] - val takeWhile: predicate: ('T -> bool) -> array: 'T[] -> 'T[] + val takeWhile: predicate: ('T -> bool) -> array: 'T array -> 'T array /// Returns a new array containing the elements of the original except the first element. /// @@ -2590,7 +2591,7 @@ module Array = /// /// [] - val tail: array: 'T[] -> 'T[] + val tail: array: 'T array -> 'T array /// Builds a list from the given array. /// @@ -2609,7 +2610,7 @@ module Array = /// Evaluates to [ 1; 2; 5 ]. /// [] - val toList: array: 'T[] -> 'T list + val toList: array: 'T array -> 'T list /// Views the given array as a sequence. /// @@ -2628,7 +2629,7 @@ module Array = /// Evaluates to seq { 1; 2; 5 }. /// [] - val toSeq: array: 'T[] -> seq<'T> + val toSeq: array: 'T array -> seq<'T> /// Returns the transpose of the given sequence of arrays. /// @@ -2650,7 +2651,7 @@ module Array = /// Evaluates to [|[|10; 11|]; [|20; 21|]; [|30; 31|]|]. /// [] - val transpose: arrays: seq<'T[]> -> 'T[][] + val transpose: arrays: seq<'T array> -> 'T array array /// Returns at most N elements in a new array. /// @@ -2688,7 +2689,7 @@ module Array = /// Evaluates to [| |]. /// [] - val truncate: count: int -> array: 'T[] -> 'T[] + val truncate: count: int -> array: 'T array -> 'T array /// Returns the first element for which the given function returns True. /// Return None if no such element exists. @@ -2718,7 +2719,7 @@ module Array = /// Evaluates to None /// [] - val tryFind: predicate: ('T -> bool) -> array: 'T[] -> 'T option + val tryFind: predicate: ('T -> bool) -> array: 'T array -> 'T option /// Returns the last element for which the given function returns True. /// Return None if no such element exists. @@ -2748,7 +2749,7 @@ module Array = /// Evaluates to None /// [] - val tryFindBack: predicate: ('T -> bool) -> array: 'T[] -> 'T option + val tryFindBack: predicate: ('T -> bool) -> array: 'T array -> 'T option /// Returns the index of the first element in the array /// that satisfies the given predicate. @@ -2778,7 +2779,7 @@ module Array = /// Evaluates to None /// [] - val tryFindIndex: predicate: ('T -> bool) -> array: 'T[] -> int option + val tryFindIndex: predicate: ('T -> bool) -> array: 'T array -> int option /// Tries to find the nth element in the array. /// Returns None if index is negative or the input array does not contain enough elements. @@ -2808,7 +2809,7 @@ module Array = /// Evaluates to None. /// [] - val tryItem: index: int -> array: 'T[] -> 'T option + val tryItem: index: int -> array: 'T array -> 'T option /// Returns the index of the last element in the array /// that satisfies the given predicate. @@ -2838,7 +2839,7 @@ module Array = /// Evaluates to None /// [] - val tryFindIndexBack: predicate: ('T -> bool) -> array: 'T[] -> int option + val tryFindIndexBack: predicate: ('T -> bool) -> array: 'T array -> int option /// Returns an array that contains the elements generated by the given computation. /// The generator is repeatedly called to build the list until it returns `None`. @@ -2857,7 +2858,7 @@ module Array = /// Evaluates to [| 1; 2; 4; 8; 16; 32; 64 |] /// [] - val unfold<'T, 'State> : generator: ('State -> ('T * 'State) option) -> state: 'State -> 'T[] + val unfold<'T, 'State> : generator: ('State -> ('T * 'State) option) -> state: 'State -> 'T array /// Splits an array of pairs into two arrays. /// @@ -2876,7 +2877,7 @@ module Array = /// Evaluates numbers to [|1; 2|] and names to [|"one"; "two"|]. /// [] - val unzip: array: ('T1 * 'T2)[] -> ('T1[] * 'T2[]) + val unzip: array: ('T1 * 'T2) array -> ('T1 array * 'T2 array) /// Splits an array of triples into three arrays. /// @@ -2895,7 +2896,7 @@ module Array = /// Evaluates numbers to [|1; 2|], names to [|"one"; "two"|] and roman to [|"I"; "II"|]. /// [] - val unzip3: array: ('T1 * 'T2 * 'T3)[] -> ('T1[] * 'T2[] * 'T3[]) + val unzip3: array: ('T1 * 'T2 * 'T3) array -> ('T1 array * 'T2 array * 'T3 array) /// Returns a new array containing only the elements of the array /// for which the given predicate returns "true". @@ -2918,7 +2919,7 @@ module Array = /// Evaluates to [| 2; 4 |] /// [] - val where: predicate: ('T -> bool) -> array: 'T[] -> 'T[] + val where: predicate: ('T -> bool) -> array: 'T array -> 'T array /// Returns an array of sliding windows containing elements drawn from the input /// array. Each window is returned as a fresh array. @@ -2940,7 +2941,7 @@ module Array = /// Evaluates to [|[|1; 2; 3|]; [|2; 3; 4|]; [|3; 4; 5|]|] /// [] - val windowed: windowSize: int -> array: 'T[] -> 'T[][] + val windowed: windowSize: int -> array: 'T array -> 'T array array /// Combines the two arrays into an array of pairs. The two arrays must have equal lengths, otherwise an ArgumentException is /// raised. @@ -2963,7 +2964,7 @@ module Array = /// Evaluates to [| (1, "one"); (2, "two") |]. /// [] - val zip: array1: 'T1[] -> array2: 'T2[] -> ('T1 * 'T2)[] + val zip: array1: 'T1 array -> array2: 'T2 array -> ('T1 * 'T2) array /// Combines three arrays into an array of pairs. The three arrays must have equal lengths, otherwise an ArgumentException is /// raised. @@ -2988,7 +2989,7 @@ module Array = /// Evaluates to [|(1, "one", "I"); (2, "two", "II")|]. /// [] - val zip3: array1: 'T1[] -> array2: 'T2[] -> array3: 'T3[] -> ('T1 * 'T2 * 'T3)[] + val zip3: array1: 'T1 array -> array2: 'T2 array -> array3: 'T3 array -> ('T1 * 'T2 * 'T3) array /// Return a new array with the item at a given index removed. /// @@ -3008,7 +3009,7 @@ module Array = /// Evaluates to [| 0; 2 |]. /// [] - val removeAt: index: int -> source: 'T[] -> 'T[] + val removeAt: index: int -> source: 'T array -> 'T array /// Return a new array with the number of items starting at a given index removed. /// @@ -3029,7 +3030,7 @@ module Array = /// Evaluates to [| 0; 3 |]. /// [] - val removeManyAt: index: int -> count: int -> source: 'T[] -> 'T[] + val removeManyAt: index: int -> count: int -> source: 'T array -> 'T array /// Return a new array with the item at a given index set to the new value. /// @@ -3050,7 +3051,7 @@ module Array = /// Evaluates to [| 0; 9; 2 |]. /// [] - val updateAt: index: int -> value: 'T -> source: 'T[] -> 'T[] + val updateAt: index: int -> value: 'T -> source: 'T array -> 'T array /// Return a new array with a new item inserted before the given index. /// @@ -3071,7 +3072,7 @@ module Array = /// Evaluates to [| 0; 9; 1; 2 |]. /// [] - val insertAt: index: int -> value: 'T -> source: 'T[] -> 'T[] + val insertAt: index: int -> value: 'T -> source: 'T array -> 'T array /// Return a new array with new items inserted before the given index. /// @@ -3092,7 +3093,7 @@ module Array = /// Evaluates to [| 0; 8; 9; 1; 2 |]. /// [] - val insertManyAt: index: int -> values: seq<'T> -> source: 'T[] -> 'T[] + val insertManyAt: index: int -> values: seq<'T> -> source: 'T array -> 'T array /// Provides parallel operations on arrays module Parallel = @@ -3121,7 +3122,7 @@ module Array = /// [] [] - val forall: predicate: ('T -> bool) -> array: 'T[] -> bool + val forall: predicate: ('T -> bool) -> array: 'T array -> bool /// Tests if any element of the array satisfies the given predicate. /// @@ -3155,7 +3156,7 @@ module Array = /// [] [] - val exists: predicate: ('T -> bool) -> array: 'T[] -> bool + val exists: predicate: ('T -> bool) -> array: 'T array -> bool /// Returns the first element for which the given function returns True. /// Returns None if no such element exists. @@ -3186,7 +3187,7 @@ module Array = /// [] [] - val tryFind: predicate: ('T -> bool) -> array: 'T[] -> 'T option + val tryFind: predicate: ('T -> bool) -> array: 'T array -> 'T option /// Returns the index of the first element in the array /// that satisfies the given predicate. @@ -3217,7 +3218,7 @@ module Array = /// [] [] - val tryFindIndex: predicate: ('T -> bool) -> array: 'T[] -> int option + val tryFindIndex: predicate: ('T -> bool) -> array: 'T array -> int option /// Applies the given function to successive elements, returning the first /// result where the function returns Some(x) for some x. If the function @@ -3250,7 +3251,7 @@ module Array = /// [] [] - val tryPick: chooser: ('T -> 'U option) -> array: 'T[] -> 'U option + val tryPick: chooser: ('T -> 'U option) -> array: 'T array -> 'U option /// Applies a function to each element of the array in parallel, threading an accumulator argument /// through the computation for each thread involved in the computation. After processing entire input, results from all threads are reduced together. @@ -3278,7 +3279,7 @@ module Array = [] [] - val inline reduce: reduction: ('T -> 'T -> 'T) -> array: 'T[] -> 'T + val inline reduce: reduction: ('T -> 'T -> 'T) -> array: 'T array -> 'T /// Applies a projection function to each element of the array in parallel, reducing elements in each thread with a dedicated 'reduction' function. /// After processing entire input, results from all threads are reduced together. @@ -3306,7 +3307,7 @@ module Array = [] [] - val reduceBy: projection: ('T -> 'U) -> reduction: ('U -> 'U -> 'U) -> array: 'T[] -> 'U + val reduceBy: projection: ('T -> 'U) -> reduction: ('U -> 'U -> 'U) -> array: 'T array -> 'U /// Returns the greatest of all elements of the array, compared via Operators.max. /// @@ -3330,7 +3331,7 @@ module Array = /// /// /// - /// let inputs: int[]= [| |] + /// let inputs: int array= [| |] /// /// inputs |> Array.Parallel.max /// @@ -3338,7 +3339,7 @@ module Array = /// [] [] - val inline max: array: 'T[] -> 'T when 'T: comparison + val inline max: array: 'T array -> 'T when 'T: comparison /// Returns the greatest of all elements of the array, compared via Operators.max on the function result. /// @@ -3363,7 +3364,7 @@ module Array = /// /// /// - /// let inputs: string[]= [| |] + /// let inputs: string array= [| |] /// /// inputs |> Array.Parallel.maxBy (fun s -> s.Length) /// @@ -3371,7 +3372,7 @@ module Array = /// [] [] - val inline maxBy: projection: ('T -> 'U) -> array: 'T[] -> 'T when 'U: comparison + val inline maxBy: projection: ('T -> 'U) -> array: 'T array -> 'T when 'U: comparison /// Returns the smallest of all elements of the array, compared via Operators.min. /// @@ -3395,7 +3396,7 @@ module Array = /// /// /// - /// let inputs: int[]= [| |] + /// let inputs: int array= [| |] /// /// inputs |> Array.Parallel.min /// @@ -3403,7 +3404,7 @@ module Array = /// [] [] - val inline min: array: 'T[] -> 'T when 'T: comparison + val inline min: array: 'T array -> 'T when 'T: comparison /// Returns the lowest of all elements of the array, compared via Operators.min on the function result. /// @@ -3428,7 +3429,7 @@ module Array = /// /// /// - /// let inputs: string[]= [| |] + /// let inputs: string array= [| |] /// /// inputs |> Array.Parallel.minBy (fun s -> s.Length) /// @@ -3436,7 +3437,7 @@ module Array = /// [] [] - val inline minBy: projection: ('T -> 'U) -> array: 'T[] -> 'T when 'U: comparison + val inline minBy: projection: ('T -> 'U) -> array: 'T array -> 'T when 'U: comparison /// Returns the sum of the elements in the array. /// @@ -3456,7 +3457,8 @@ module Array = /// [] [] - val inline sum: array: ^T[] -> ^T when ^T: (static member (+): ^T * ^T -> ^T) and ^T: (static member Zero: ^T) + val inline sum: + array: ^T array -> ^T when ^T: (static member (+): ^T * ^T -> ^T) and ^T: (static member Zero: ^T) /// Returns the sum of the results generated by applying the function to each element of the array. /// @@ -3478,7 +3480,7 @@ module Array = [] [] val inline sumBy: - projection: ('T -> ^U) -> array: 'T[] -> ^U + projection: ('T -> ^U) -> array: 'T array -> ^U when ^U: (static member (+): ^U * ^U -> ^U) and ^U: (static member Zero: ^U) /// Returns the average of the elements in the array. @@ -3506,7 +3508,7 @@ module Array = [] [] val inline average: - array: ^T[] -> ^T + array: ^T array -> ^T when ^T: (static member (+): ^T * ^T -> ^T) and ^T: (static member DivideByInt: ^T * int -> ^T) /// Returns the average of the elements generated by applying the function to each element of the array. @@ -3535,7 +3537,7 @@ module Array = /// /// type Foo = { Bar: float } /// - /// let input : Foo[] = [| |] + /// let input : Foo array = [| |] /// /// input |> Array.Parallel.averageBy (fun foo -> foo.Bar) /// @@ -3544,7 +3546,7 @@ module Array = [] [] val inline averageBy: - projection: ('T -> ^U) -> array: 'T[] -> ^U + projection: ('T -> ^U) -> array: 'T array -> ^U when ^U: (static member (+): ^U * ^U -> ^U) and ^U: (static member DivideByInt: ^U * int -> ^U) /// Apply the given function to each element of the array. Return @@ -3579,7 +3581,7 @@ module Array = /// Evaluates to [| 2 |] /// [] - val choose: chooser: ('T -> 'U option) -> array: 'T[] -> 'U[] + val choose: chooser: ('T -> 'U option) -> array: 'T array -> 'U array /// For each element of the array, apply the given function. Concatenate all the results and return the combined array. /// @@ -3589,13 +3591,13 @@ module Array = /// /// The input array. /// - /// 'U[] + /// 'U array /// /// Thrown when the input array is null. /// /// /// - /// type Foo = { Bar: int[] } + /// type Foo = { Bar: int array } /// /// let input = [| {Bar = [| 1; 2 |]}; {Bar = [| 3; 4 |]} |] /// @@ -3613,7 +3615,7 @@ module Array = /// Evaluates to [| 1; 2; 3; 4 |] /// [] - val collect: mapping: ('T -> 'U[]) -> array: 'T[] -> 'U[] + val collect: mapping: ('T -> 'U array) -> array: 'T array -> 'U array /// Build a new array whose elements are the results of applying the given function /// to each of the elements of the array. @@ -3637,7 +3639,7 @@ module Array = /// Evaluates to [| 1; 3; 2 |] /// [] - val map: mapping: ('T -> 'U) -> array: 'T[] -> 'U[] + val map: mapping: ('T -> 'U) -> array: 'T array -> 'U array /// Build a new array whose elements are the results of applying the given function /// to each of the elements of the array. The integer index passed to the @@ -3662,7 +3664,7 @@ module Array = /// Evaluates to [| 10; 11; 12 |] /// [] - val mapi: mapping: (int -> 'T -> 'U) -> array: 'T[] -> 'U[] + val mapi: mapping: (int -> 'T -> 'U) -> array: 'T array -> 'U array /// Applies a key-generating function to each element of an array in parallel and yields an array of /// unique keys. Each unique key contains an array of all elements that match @@ -3689,7 +3691,7 @@ module Array = [] [] - val groupBy: projection: ('T -> 'Key) -> array: 'T[] -> ('Key * 'T[])[] when 'Key: equality + val groupBy: projection: ('T -> 'Key) -> array: 'T array -> ('Key * 'T array) array when 'Key: equality /// Apply the given function to each element of the array. /// @@ -3715,7 +3717,7 @@ module Array = /// /// [] - val iter: action: ('T -> unit) -> array: 'T[] -> unit + val iter: action: ('T -> unit) -> array: 'T array -> unit /// Apply the given function to each element of the array. The integer passed to the /// function indicates the index of element. @@ -3742,7 +3744,7 @@ module Array = /// /// [] - val iteri: action: (int -> 'T -> unit) -> array: 'T[] -> unit + val iteri: action: (int -> 'T -> unit) -> array: 'T array -> unit /// Create an array given the dimension and a generator function to compute the elements. /// @@ -3761,7 +3763,7 @@ module Array = /// Evaluates to [| 5; 6; 7; 8 |] /// [] - val init: count: int -> initializer: (int -> 'T) -> 'T[] + val init: count: int -> initializer: (int -> 'T) -> 'T array /// Split the collection into two collections, containing the /// elements for which the given predicate returns "true" and "false" @@ -3786,7 +3788,7 @@ module Array = /// Evaluates to ([|2; 4|], [|1; 3|]). /// [] - val partition: predicate: ('T -> bool) -> array: 'T[] -> 'T[] * 'T[] + val partition: predicate: ('T -> bool) -> array: 'T array -> 'T array * 'T array /// Sorts the elements of an array in parallel, returning a new array. Elements are compared using . /// @@ -3809,7 +3811,7 @@ module Array = /// [] [] - val sort: array: 'T[] -> 'T[] when 'T: comparison + val sort: array: 'T array -> 'T array when 'T: comparison /// Sorts the elements of an array in parallel, using the given projection for the keys and returning a new array. /// Elements are compared using . @@ -3835,7 +3837,7 @@ module Array = [] [] - val sortBy: projection: ('T -> 'Key) -> array: 'T[] -> 'T[] when 'Key: comparison + val sortBy: projection: ('T -> 'Key) -> array: 'T array -> 'T array when 'Key: comparison /// Sorts the elements of an array in parallel, using the given comparison function as the order, returning a new array. /// @@ -3864,7 +3866,7 @@ module Array = /// [] [] - val sortWith: comparer: ('T -> 'T -> int) -> array: 'T[] -> 'T[] + val sortWith: comparer: ('T -> 'T -> int) -> array: 'T array -> 'T array /// Sorts the elements of an array by mutating the array in-place in parallel, using the given projection for the keys. /// Elements are compared using . @@ -3887,7 +3889,7 @@ module Array = /// [] [] - val sortInPlaceBy: projection: ('T -> 'Key) -> array: 'T[] -> unit when 'Key: comparison + val sortInPlaceBy: projection: ('T -> 'Key) -> array: 'T array -> unit when 'Key: comparison /// Sorts the elements of an array by mutating the array in-place in parallel, using the given comparison function as the order. /// @@ -3911,7 +3913,7 @@ module Array = /// [] [] - val sortInPlaceWith: comparer: ('T -> 'T -> int) -> array: 'T[] -> unit + val sortInPlaceWith: comparer: ('T -> 'T -> int) -> array: 'T array -> unit /// Sorts the elements of an array by mutating the array in-place in parallel, using the given comparison function. /// Elements are compared using . @@ -3930,7 +3932,7 @@ module Array = /// [] [] - val sortInPlace: array: 'T[] -> unit when 'T: comparison + val sortInPlace: array: 'T array -> unit when 'T: comparison /// Sorts the elements of an array in parallel, in descending order, returning a new array. Elements are compared using . /// @@ -3951,7 +3953,7 @@ module Array = /// [] [] - val sortDescending: array: 'T[] -> 'T[] when 'T: comparison + val sortDescending: array: 'T array -> 'T array when 'T: comparison /// Sorts the elements of an array in parallel, in descending order, using the given projection for the keys and returning a new array. /// Elements are compared using . @@ -3974,7 +3976,7 @@ module Array = /// [] [] - val sortByDescending: projection: ('T -> 'Key) -> array: 'T[] -> 'T[] when 'Key: comparison + val sortByDescending: projection: ('T -> 'Key) -> array: 'T array -> 'T array when 'Key: comparison /// Combines the two arrays into an array of pairs. The two arrays must have equal lengths, otherwise an ArgumentException is /// raised. @@ -3998,7 +4000,7 @@ module Array = /// [] [] - val zip: array1: 'T1[] -> array2: 'T2[] -> ('T1 * 'T2)[] + val zip: array1: 'T1 array -> array2: 'T2 array -> ('T1 * 'T2) array /// Returns a new collection containing only the elements of the collection /// for which the given predicate returns true. @@ -4020,4 +4022,4 @@ module Array = /// [] [] - val filter: predicate: ('T -> bool) -> array: 'T[] -> 'T[] + val filter: predicate: ('T -> bool) -> array: 'T array -> 'T array diff --git a/src/FSharp.Core/async.fs b/src/FSharp.Core/async.fs index bc9d676663b..9f9a2d1ef48 100644 --- a/src/FSharp.Core/async.fs +++ b/src/FSharp.Core/async.fs @@ -2249,7 +2249,7 @@ module CommonExtensions = type System.IO.Stream with [] // give the extension member a 'nice', unmangled compiled name, unique within this module - member stream.AsyncRead(buffer: byte[], ?offset, ?count) = + member stream.AsyncRead(buffer: byte array, ?offset, ?count) = let offset = defaultArg offset 0 let count = defaultArg count buffer.Length Async.FromBeginEnd(buffer, offset, count, stream.BeginRead, stream.EndRead) @@ -2271,7 +2271,7 @@ module CommonExtensions = } [] // give the extension member a 'nice', unmangled compiled name, unique within this module - member stream.AsyncWrite(buffer: byte[], ?offset: int, ?count: int) = + member stream.AsyncWrite(buffer: byte array, ?offset: int, ?count: int) = let offset = defaultArg offset 0 let count = defaultArg count buffer.Length Async.FromBeginEnd(buffer, offset, count, stream.BeginWrite, stream.EndWrite) @@ -2361,7 +2361,7 @@ module WebExtensions = ) [] // give the extension member a 'nice', unmangled compiled name, unique within this module - member this.AsyncDownloadData(address: Uri) : Async = + member this.AsyncDownloadData(address: Uri) : Async = this.Download( event = this.DownloadDataCompleted, handler = (fun action -> Net.DownloadDataCompletedEventHandler action), diff --git a/src/FSharp.Core/async.fsi b/src/FSharp.Core/async.fsi index 35b3e227a5d..6b621176c9d 100644 --- a/src/FSharp.Core/async.fsi +++ b/src/FSharp.Core/async.fsi @@ -428,7 +428,7 @@ namespace Microsoft.FSharp.Control /// /// This will print "3", "5", "7", "11" (in any order) in 1-2 seconds and then [| false; true; true; true; false; true |]. /// - static member Parallel : computations:seq> -> Async<'T[]> + static member Parallel : computations:seq> -> Async<'T array> /// Creates an asynchronous computation that executes all the given asynchronous computations, /// initially queueing each as work items and using a fork/join pattern. @@ -475,7 +475,7 @@ namespace Microsoft.FSharp.Control /// This will print "3", "5" (in any order) in 1-2 seconds, and then "7", "11" (in any order) in 1-2 more seconds and then /// [| false; true; true; true; false; true |]. /// - static member Parallel : computations:seq> * ?maxDegreeOfParallelism : int -> Async<'T[]> + static member Parallel : computations:seq> * ?maxDegreeOfParallelism : int -> Async<'T array> /// Creates an asynchronous computation that executes all the given asynchronous computations sequentially. /// @@ -520,7 +520,7 @@ namespace Microsoft.FSharp.Control /// This will print "3", "5", "7", "11" with ~1-2 seconds between them except for pauses where even numbers would be and then /// prints [| false; true; true; true; false; true |]. /// - static member Sequential : computations:seq> -> Async<'T[]> + static member Sequential : computations:seq> -> Async<'T array> /// /// Creates an asynchronous computation that executes all given asynchronous computations in parallel, @@ -1409,17 +1409,17 @@ namespace Microsoft.FSharp.Control /// /// [] // give the extension member a nice, unmangled compiled name, unique within this module - member AsyncRead : buffer:byte[] * ?offset:int * ?count:int -> Async + member AsyncRead : buffer:byte array * ?offset:int * ?count:int -> Async /// Returns an asynchronous computation that will read the given number of bytes from the stream. /// /// The number of bytes to read. /// - /// An asynchronous computation that returns the read byte[] when run. + /// An asynchronous computation that returns the read byte array when run. /// /// [] // give the extension member a nice, unmangled compiled name, unique within this module - member AsyncRead : count:int -> Async + member AsyncRead : count:int -> Async /// Returns an asynchronous computation that will write the given bytes to the stream. /// @@ -1435,7 +1435,7 @@ namespace Microsoft.FSharp.Control /// /// [] // give the extension member a nice, unmangled compiled name, unique within this module - member AsyncWrite : buffer:byte[] * ?offset:int * ?count:int -> Async + member AsyncWrite : buffer:byte array * ?offset:int * ?count:int -> Async ///The family of first class event values for delegate types that satisfy the F# delegate constraint. @@ -1522,7 +1522,7 @@ namespace Microsoft.FSharp.Control /// /// Downloads the data in bytes and decodes it to a string. [] // give the extension member a nice, unmangled compiled name, unique within this module - member AsyncDownloadData : address:System.Uri -> Async + member AsyncDownloadData : address:System.Uri -> Async /// Returns an asynchronous computation that, when run, will wait for the download of the given URI to specified file. /// diff --git a/src/FSharp.Core/collections.fsi b/src/FSharp.Core/collections.fsi index 1e8494aa9e3..9baf6097394 100644 --- a/src/FSharp.Core/collections.fsi +++ b/src/FSharp.Core/collections.fsi @@ -70,7 +70,7 @@ module HashIdentity = /// /// open System.Collections.Generic /// - /// let dict = new Dictionary<int[],int>(HashIdentity.Structural) + /// let dict = new Dictionary<int array,int>(HashIdentity.Structural) /// /// let arr1 = [| 1;2;3 |] /// let arr2 = [| 1;2;3 |] @@ -134,7 +134,7 @@ module HashIdentity = /// /// open System.Collections.Generic /// - /// let dict = new Dictionary<int[],int>(HashIdentity.Structural) + /// let dict = new Dictionary<int array,int>(HashIdentity.Structural) /// /// let arr1 = [| 1;2;3 |] /// let arr2 = [| 1;2;3 |] diff --git a/src/FSharp.Core/event.fs b/src/FSharp.Core/event.fs index da356eefb05..338d7275fc3 100644 --- a/src/FSharp.Core/event.fs +++ b/src/FSharp.Core/event.fs @@ -31,7 +31,7 @@ module private Atomic = type DelegateEvent<'Delegate when 'Delegate :> System.Delegate>() = let mutable multicast: System.Delegate = null - member x.Trigger(args: obj[]) = + member x.Trigger(args: obj array) = match multicast with | null -> () | d -> d.DynamicInvoke(args) |> ignore diff --git a/src/FSharp.Core/event.fsi b/src/FSharp.Core/event.fsi index 12557f0dc22..09225b9b0fa 100644 --- a/src/FSharp.Core/event.fsi +++ b/src/FSharp.Core/event.fsi @@ -23,7 +23,7 @@ type DelegateEvent<'Delegate when 'Delegate :> System.Delegate> = /// The parameters for the event. /// /// - member Trigger: args: obj[] -> unit + member Trigger: args: obj array -> unit /// Publishes the event as a first class event value. /// diff --git a/src/FSharp.Core/fslib-extra-pervasives.fs b/src/FSharp.Core/fslib-extra-pervasives.fs index d12a20211b8..def5d42c2e5 100644 --- a/src/FSharp.Core/fslib-extra-pervasives.fs +++ b/src/FSharp.Core/fslib-extra-pervasives.fs @@ -215,7 +215,7 @@ module ExtraTopLevelOperators = let getArray (vals: seq<'T>) = match vals with - | :? ('T[]) as arr -> arr + | :? ('T array) as arr -> arr | _ -> Seq.toArray vals [] @@ -415,7 +415,7 @@ type TypeProviderConfig (systemRuntimeContainsType: string -> bool, getReferencedAssembliesOption: (unit -> string array) option) = let mutable resolutionFolder: string = null let mutable runtimeAssembly: string = null - let mutable referencedAssemblies: string[] = null + let mutable referencedAssemblies: string array = null let mutable temporaryFolder: string = null let mutable isInvalidationSupported: bool = false let mutable useResolutionFolderAtRuntime: bool = false @@ -468,31 +468,31 @@ type IProvidedNamespace = abstract NamespaceName: string - abstract GetNestedNamespaces: unit -> IProvidedNamespace[] + abstract GetNestedNamespaces: unit -> IProvidedNamespace array - abstract GetTypes: unit -> Type[] + abstract GetTypes: unit -> Type array abstract ResolveTypeName: typeName: string -> Type type ITypeProvider = inherit System.IDisposable - abstract GetNamespaces: unit -> IProvidedNamespace[] + abstract GetNamespaces: unit -> IProvidedNamespace array - abstract GetStaticParameters: typeWithoutArguments: Type -> ParameterInfo[] + abstract GetStaticParameters: typeWithoutArguments: Type -> ParameterInfo array abstract ApplyStaticArguments: - typeWithoutArguments: Type * typePathWithArguments: string[] * staticArguments: obj[] -> Type + typeWithoutArguments: Type * typePathWithArguments: string array * staticArguments: obj array -> Type - abstract GetInvokerExpression: syntheticMethodBase: MethodBase * parameters: Expr[] -> Expr + abstract GetInvokerExpression: syntheticMethodBase: MethodBase * parameters: Expr array -> Expr [] abstract Invalidate: IEvent - abstract GetGeneratedAssemblyContents: assembly: System.Reflection.Assembly -> byte[] + abstract GetGeneratedAssemblyContents: assembly: System.Reflection.Assembly -> byte array type ITypeProvider2 = - abstract GetStaticParametersForMethod: methodWithoutArguments: MethodBase -> ParameterInfo[] + abstract GetStaticParametersForMethod: methodWithoutArguments: MethodBase -> ParameterInfo array abstract ApplyStaticArgumentsForMethod: - methodWithoutArguments: MethodBase * methodNameWithArguments: string * staticArguments: obj[] -> MethodBase + methodWithoutArguments: MethodBase * methodNameWithArguments: string * staticArguments: obj array -> MethodBase diff --git a/src/FSharp.Core/fslib-extra-pervasives.fsi b/src/FSharp.Core/fslib-extra-pervasives.fsi index 619f5ff05d4..bd4bf0ee8b5 100644 --- a/src/FSharp.Core/fslib-extra-pervasives.fsi +++ b/src/FSharp.Core/fslib-extra-pervasives.fsi @@ -459,7 +459,7 @@ namespace Microsoft.FSharp.Core.CompilerServices new : systemRuntimeContainsType : (string -> bool) -> TypeProviderConfig /// Create a configuration which calls the given functions for the corresponding operation. - new : systemRuntimeContainsType : (string -> bool) * getReferencedAssemblies : (unit -> string[]) -> TypeProviderConfig + new : systemRuntimeContainsType : (string -> bool) * getReferencedAssemblies : (unit -> string array) -> TypeProviderConfig /// Get the full path to use to resolve relative paths in any file name arguments given to the type provider instance. member ResolutionFolder : string with get,set @@ -468,7 +468,7 @@ namespace Microsoft.FSharp.Core.CompilerServices member RuntimeAssembly : string with get,set /// Get the referenced assemblies for the type provider instance. - member ReferencedAssemblies : string[] with get,set + member ReferencedAssemblies : string array with get,set /// Get the full path to use for temporary files for the type provider instance. member TemporaryFolder : string with get,set @@ -493,13 +493,13 @@ namespace Microsoft.FSharp.Core.CompilerServices abstract NamespaceName : string /// The sub-namespaces in this namespace. An optional member to prevent generation of namespaces until an outer namespace is explored. - abstract GetNestedNamespaces : unit -> IProvidedNamespace[] + abstract GetNestedNamespaces : unit -> IProvidedNamespace array /// /// The top-level types /// /// - abstract GetTypes : unit -> Type[] + abstract GetTypes : unit -> Type array /// /// Compilers call this method to query a type provider for a type name. @@ -518,7 +518,7 @@ namespace Microsoft.FSharp.Core.CompilerServices /// /// Gets the namespaces provided by the type provider. /// - abstract GetNamespaces : unit -> IProvidedNamespace[] + abstract GetNamespaces : unit -> IProvidedNamespace array /// /// Get the static parameters for a provided type. @@ -527,7 +527,7 @@ namespace Microsoft.FSharp.Core.CompilerServices /// A type returned by GetTypes or ResolveTypeName /// /// - abstract GetStaticParameters : typeWithoutArguments:Type -> ParameterInfo[] + abstract GetStaticParameters : typeWithoutArguments:Type -> ParameterInfo array /// /// Apply static arguments to a provided type that accepts static arguments. @@ -540,7 +540,7 @@ namespace Microsoft.FSharp.Core.CompilerServices /// the static parameters, indexed by name /// /// - abstract ApplyStaticArguments : typeWithoutArguments:Type * typePathWithArguments:string[] * staticArguments:obj[] -> Type + abstract ApplyStaticArguments : typeWithoutArguments:Type * typePathWithArguments:string array * staticArguments:obj array -> Type /// /// Called by the compiler to ask for an Expression tree to replace the given MethodBase with. @@ -550,7 +550,7 @@ namespace Microsoft.FSharp.Core.CompilerServices /// Expressions that represent the parameters to this call. /// /// An expression that the compiler will use in place of the given method base. - abstract GetInvokerExpression : syntheticMethodBase:MethodBase * parameters:Expr[] -> Expr + abstract GetInvokerExpression : syntheticMethodBase:MethodBase * parameters:Expr array -> Expr /// /// Triggered when an assumption changes that invalidates the resolutions so far reported by the provider @@ -561,7 +561,7 @@ namespace Microsoft.FSharp.Core.CompilerServices /// /// Get the physical contents of the given logical provided assembly. /// - abstract GetGeneratedAssemblyContents : assembly:Assembly -> byte[] + abstract GetGeneratedAssemblyContents : assembly:Assembly -> byte array /// Represents additional, optional information for a type provider component type ITypeProvider2 = @@ -573,7 +573,7 @@ namespace Microsoft.FSharp.Core.CompilerServices /// A method returned by GetMethod on a provided type /// /// The static parameters of the provided method, if any - abstract GetStaticParametersForMethod : methodWithoutArguments:MethodBase -> ParameterInfo[] + abstract GetStaticParametersForMethod : methodWithoutArguments:MethodBase -> ParameterInfo array /// /// Apply static arguments to a provided method that accepts static arguments. @@ -584,4 +584,4 @@ namespace Microsoft.FSharp.Core.CompilerServices /// the values of the static parameters, indexed by name /// /// The provided method definition corresponding to the given static parameter values - abstract ApplyStaticArgumentsForMethod : methodWithoutArguments:MethodBase * methodNameWithArguments:string * staticArguments:obj[] -> MethodBase + abstract ApplyStaticArgumentsForMethod : methodWithoutArguments:MethodBase * methodNameWithArguments:string * staticArguments:obj array -> MethodBase diff --git a/src/FSharp.Core/list.fs b/src/FSharp.Core/list.fs index e3559a13958..dc81209a8c7 100644 --- a/src/FSharp.Core/list.fs +++ b/src/FSharp.Core/list.fs @@ -327,7 +327,7 @@ module List = loop state list1 list2 - let foldArraySubRight (f: OptimizedClosures.FSharpFunc<'T, _, _>) (arr: 'T[]) start fin acc = + let foldArraySubRight (f: OptimizedClosures.FSharpFunc<'T, _, _>) (arr: 'T array) start fin acc = let mutable state = acc for i = fin downto start do @@ -365,7 +365,7 @@ module List = let scanArraySubRight<'T, 'State> (f: OptimizedClosures.FSharpFunc<'T, 'State, 'State>) - (arr: _[]) + (arr: _ array) start fin initState diff --git a/src/FSharp.Core/list.fsi b/src/FSharp.Core/list.fsi index d746198f7fb..05ff605b247 100644 --- a/src/FSharp.Core/list.fsi +++ b/src/FSharp.Core/list.fsi @@ -1594,7 +1594,7 @@ module List = /// Evaluates to [ 1; 2; 5 ]. /// [] - val ofArray : array:'T[] -> 'T list + val ofArray : array:'T array -> 'T list /// Builds a new list from the given enumerable object. /// @@ -2176,7 +2176,7 @@ module List = /// Evaluates to [| 1; 2; 5 |]. /// [] - val toArray: list:'T list -> 'T[] + val toArray: list:'T list -> 'T array /// Views the given list as a sequence. /// diff --git a/src/FSharp.Core/local.fs b/src/FSharp.Core/local.fs index 6a3b44f5475..a57621c651f 100644 --- a/src/FSharp.Core/local.fs +++ b/src/FSharp.Core/local.fs @@ -193,7 +193,7 @@ module internal List = cons let groupBy (comparer:IEqualityComparer<'SafeKey>) (keyf:'T->'SafeKey) (getKey:'SafeKey->'Key) (list: 'T list) = - let dict = Dictionary<_, _ list []> comparer + let dict = Dictionary<_, _ list array> comparer // Build the groupings let rec loop list = diff --git a/src/FSharp.Core/local.fsi b/src/FSharp.Core/local.fsi index 9c2ebc0bbfd..1b210474062 100644 --- a/src/FSharp.Core/local.fsi +++ b/src/FSharp.Core/local.fsi @@ -72,10 +72,10 @@ module internal List = val splitInto: int -> 'T list -> 'T list list val zip: 'T1 list -> 'T2 list -> ('T1 * 'T2) list val zip3: 'T1 list -> 'T2 list -> 'T3 list -> ('T1 * 'T2 * 'T3) list - val ofArray: 'T[] -> 'T list + val ofArray: 'T array -> 'T list val take: int -> 'T list -> 'T list val takeWhile: ('T -> bool) -> 'T list -> 'T list - val toArray: 'T list -> 'T[] + val toArray: 'T list -> 'T array val inline ofSeq: seq<'T> -> 'T List val splitAt: int -> 'T list -> ('T list * 'T list) val transpose: 'T list list -> 'T list list @@ -84,40 +84,40 @@ module internal List = module internal Array = // The input parameter should be checked by callers if necessary - val inline zeroCreateUnchecked: int -> 'T[] + val inline zeroCreateUnchecked: int -> 'T array - val inline init: int -> (int -> 'T) -> 'T[] + val inline init: int -> (int -> 'T) -> 'T array - val splitInto: int -> 'T[] -> 'T[][] + val splitInto: int -> 'T array -> 'T array array - val findBack: predicate: ('T -> bool) -> array: 'T[] -> 'T + val findBack: predicate: ('T -> bool) -> array: 'T array -> 'T - val tryFindBack: predicate: ('T -> bool) -> array: 'T[] -> 'T option + val tryFindBack: predicate: ('T -> bool) -> array: 'T array -> 'T option - val findIndexBack: predicate: ('T -> bool) -> array: 'T[] -> int + val findIndexBack: predicate: ('T -> bool) -> array: 'T array -> int - val tryFindIndexBack: predicate: ('T -> bool) -> array: 'T[] -> int option + val tryFindIndexBack: predicate: ('T -> bool) -> array: 'T array -> int option - val mapFold: ('State -> 'T -> 'U * 'State) -> 'State -> 'T[] -> 'U[] * 'State + val mapFold: ('State -> 'T -> 'U * 'State) -> 'State -> 'T array -> 'U array * 'State - val mapFoldBack: ('T -> 'State -> 'U * 'State) -> 'T[] -> 'State -> 'U[] * 'State + val mapFoldBack: ('T -> 'State -> 'U * 'State) -> 'T array -> 'State -> 'U array * 'State - val permute: indexMap: (int -> int) -> 'T[] -> 'T[] + val permute: indexMap: (int -> int) -> 'T array -> 'T array val scanSubRight: - f: ('T -> 'State -> 'State) -> array: 'T[] -> start: int -> fin: int -> initState: 'State -> 'State[] + f: ('T -> 'State -> 'State) -> array: 'T array -> start: int -> fin: int -> initState: 'State -> 'State array - val inline subUnchecked: int -> int -> 'T[] -> 'T[] + val inline subUnchecked: int -> int -> 'T array -> 'T array - val unstableSortInPlaceBy: projection: ('T -> 'Key) -> array: 'T[] -> unit when 'Key: comparison + val unstableSortInPlaceBy: projection: ('T -> 'Key) -> array: 'T array -> unit when 'Key: comparison - val unstableSortInPlace: array: 'T[] -> unit when 'T: comparison + val unstableSortInPlace: array: 'T array -> unit when 'T: comparison - val stableSortInPlaceBy: projection: ('T -> 'Key) -> array: 'T[] -> unit when 'Key: comparison + val stableSortInPlaceBy: projection: ('T -> 'Key) -> array: 'T array -> unit when 'Key: comparison - val stableSortInPlaceWith: comparer: ('T -> 'T -> int) -> array: 'T[] -> unit + val stableSortInPlaceWith: comparer: ('T -> 'T -> int) -> array: 'T array -> unit - val stableSortInPlace: array: 'T[] -> unit when 'T: comparison + val stableSortInPlace: array: 'T array -> unit when 'T: comparison module internal Seq = val tryLastV: 'T seq -> 'T ValueOption diff --git a/src/FSharp.Core/map.fs b/src/FSharp.Core/map.fs index 35145ad8ec7..43459ecdf33 100644 --- a/src/FSharp.Core/map.fs +++ b/src/FSharp.Core/map.fs @@ -546,13 +546,13 @@ module MapTree = let ofSeq comparer (c: seq<'Key * 'T>) = match c with - | :? (('Key * 'T)[]) as xs -> ofArray comparer xs + | :? (('Key * 'T) array) as xs -> ofArray comparer xs | :? (('Key * 'T) list) as xs -> ofList comparer xs | _ -> use ie = c.GetEnumerator() mkFromEnumerator comparer empty ie - let copyToArray m (arr: _[]) i = + let copyToArray m (arr: _ array) i = let mutable j = i m diff --git a/src/FSharp.Core/map.fsi b/src/FSharp.Core/map.fsi index 49913cadbda..e6b0b1afb30 100644 --- a/src/FSharp.Core/map.fsi +++ b/src/FSharp.Core/map.fsi @@ -294,7 +294,7 @@ module Map = /// /// [] - val ofArray: elements: ('Key * 'T)[] -> Map<'Key, 'T> + val ofArray: elements: ('Key * 'T) array -> Map<'Key, 'T> /// Returns a new map made from the given bindings. /// @@ -361,7 +361,7 @@ module Map = /// /// [] - val toArray: table: Map<'Key, 'T> -> ('Key * 'T)[] + val toArray: table: Map<'Key, 'T> -> ('Key * 'T) array /// Is the map empty? /// diff --git a/src/FSharp.Core/option.fsi b/src/FSharp.Core/option.fsi index d22e80be420..d6375c6d39e 100644 --- a/src/FSharp.Core/option.fsi +++ b/src/FSharp.Core/option.fsi @@ -380,7 +380,7 @@ module Option = /// /// [] - val inline toArray: option: 'T option -> 'T[] + val inline toArray: option: 'T option -> 'T array /// Convert the option to a list of length 0 or 1. /// @@ -828,7 +828,7 @@ module ValueOption = /// /// [] - val inline toArray: voption: 'T voption -> 'T[] + val inline toArray: voption: 'T voption -> 'T array /// Convert the value option to a list of length 0 or 1. /// diff --git a/src/FSharp.Core/prim-types-prelude.fsi b/src/FSharp.Core/prim-types-prelude.fsi index f9f8715c1ff..b9bbc5c125f 100644 --- a/src/FSharp.Core/prim-types-prelude.fsi +++ b/src/FSharp.Core/prim-types-prelude.fsi @@ -133,7 +133,7 @@ namespace Microsoft.FSharp.Core /// Basic Types type uint = uint32 - /// Single dimensional, zero-based arrays, written int[], string[] etc. + /// Single dimensional, zero-based arrays, written int array, string array etc. /// /// Use the values in the Array module to manipulate values /// of this type, or the notation arr.[x] to get/set array @@ -396,7 +396,7 @@ namespace Microsoft.FSharp.Core type 'T ``[,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]`` = (# "!0[0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...,0 ...]" #) - /// Single dimensional, zero-based arrays, written int[], string[] etc. + /// Single dimensional, zero-based arrays, written int array, string array etc. /// /// Use the values in the module to manipulate values /// of this type, or the notation arr.[x] to get/set array diff --git a/src/FSharp.Core/prim-types.fs b/src/FSharp.Core/prim-types.fs index 6d6eaf10a5c..a6d5bcc6ac0 100644 --- a/src/FSharp.Core/prim-types.fs +++ b/src/FSharp.Core/prim-types.fs @@ -223,7 +223,7 @@ namespace Microsoft.FSharp.Core variantNumber:int, sequenceNumber:int, resourceName:string, - typeDefinitions:System.Type[]) = + typeDefinitions:System.Type array) = inherit Attribute() member _.SourceConstructFlags = sourceConstructFlags member _.SequenceNumber = sequenceNumber @@ -270,7 +270,7 @@ namespace Microsoft.FSharp.Core [] [] - type CompilationArgumentCountsAttribute(counts:int[]) = + type CompilationArgumentCountsAttribute(counts:int array) = inherit Attribute() member _.Counts = let unboxPrim(x:obj) = (# "unbox.any !0" type ('T) x : 'T #) @@ -524,10 +524,10 @@ namespace Microsoft.FSharp.Core let inline ignore _ = () let inline intOfByte (b:byte) = (# "" b : int #) let inline raise (e: System.Exception) = (# "throw" e : 'U #) - let inline length (x: 'T[]) = (# "ldlen conv.i4" x : int #) - let inline zeroCreate (n:int) = (# "newarr !0" type ('T) n : 'T[] #) - let inline get (arr: 'T[]) (n:int) = (# "ldelem.any !0" type ('T) arr n : 'T #) - let set (arr: 'T[]) (n:int) (x:'T) = (# "stelem.any !0" type ('T) arr n x #) + let inline length (x: 'T array) = (# "ldlen conv.i4" x : int #) + let inline zeroCreate (n:int) = (# "newarr !0" type ('T) n : 'T array #) + let inline get (arr: 'T array) (n:int) = (# "ldelem.any !0" type ('T) arr n : 'T #) + let set (arr: 'T array) (n:int) (x:'T) = (# "stelem.any !0" type ('T) arr n x #) let inline objEq (xobj:obj) (yobj:obj) = (# "ceq" xobj yobj : bool #) @@ -573,7 +573,7 @@ namespace Microsoft.FSharp.Core let inline opxor (x:int) (y:int) : int = (# "xor" x y : int32 #) let inline combineTupleHashes (h1 : int) (h2 : int) = (opxor ((opshl h1 5) + h1) h2) - let combineTupleHashCodes (codes : int []) = + let combineTupleHashCodes (codes : int array) = let mutable (num : int32) = codes.Length - 1 while (num > 1) do @@ -758,7 +758,7 @@ namespace Microsoft.FSharp.Core SetArray dst i (GetArray arr (start + i)) dst - let inline SetArraySub arr (start:int) (len:int) (src:_[]) = + let inline SetArraySub arr (start:int) (len:int) (src:_ array) = for i = 0 to len - 1 do SetArray arr (start+i) (GetArray src i) @@ -886,6 +886,123 @@ namespace Microsoft.FSharp.Core module HashCompare = + //------------------------------------------------------------------------- + // LanguagePrimitives.HashCompare: HASHING. + //------------------------------------------------------------------------- + + let defaultHashNodes = 18 + + /// The implementation of IEqualityComparer, using depth-limited for hashing and PER semantics for NaN equality. + type CountLimitedHasherPER(sz:int) = + [] + val mutable nodeCount : int + + member x.Fresh() = + if (System.Threading.Interlocked.CompareExchange(&(x.nodeCount), sz, 0) = 0) then + x + else + new CountLimitedHasherPER(sz) + + interface IEqualityComparer + + /// The implementation of IEqualityComparer, using unlimited depth for hashing and ER semantics for NaN equality. + type UnlimitedHasherER() = + interface IEqualityComparer + + /// The implementation of IEqualityComparer, using unlimited depth for hashing and PER semantics for NaN equality. + type UnlimitedHasherPER() = + interface IEqualityComparer + + + /// The unique object for unlimited depth for hashing and ER semantics for equality. + let fsEqualityComparerUnlimitedHashingER = UnlimitedHasherER() + + /// The unique object for unlimited depth for hashing and PER semantics for equality. + let fsEqualityComparerUnlimitedHashingPER = UnlimitedHasherPER() + + let inline HashCombine nr x y = (x <<< 1) + y + 631 * nr + + let GenericHashObjArray (iec : IEqualityComparer) (x: obj array) : int = + let len = x.Length + let mutable i = len - 1 + if i > defaultHashNodes then i <- defaultHashNodes // limit the hash + let mutable acc = 0 + while (i >= 0) do + // NOTE: GenericHash* call decreases nr + acc <- HashCombine i acc (iec.GetHashCode(x.GetValue(i))); + i <- i - 1 + acc + + // optimized case - byte arrays + let GenericHashByteArray (x: byte array) : int = + let len = length x + let mutable i = len - 1 + if i > defaultHashNodes then i <- defaultHashNodes // limit the hash + let mutable acc = 0 + while (i >= 0) do + acc <- HashCombine i acc (intOfByte (get x i)); + i <- i - 1 + acc + + // optimized case - int arrays + let GenericHashInt32Array (x: int array) : int = + let len = length x + let mutable i = len - 1 + if i > defaultHashNodes then i <- defaultHashNodes // limit the hash + let mutable acc = 0 + while (i >= 0) do + acc <- HashCombine i acc (get x i); + i <- i - 1 + acc + + // optimized case - int arrays + let GenericHashInt64Array (x: int64 array) : int = + let len = length x + let mutable i = len - 1 + if i > defaultHashNodes then i <- defaultHashNodes // limit the hash + let mutable acc = 0 + while (i >= 0) do + acc <- HashCombine i acc (int32 (get x i)); + i <- i - 1 + acc + + // special case - arrays do not by default have a decent structural hashing function + let GenericHashArbArray (iec : IEqualityComparer) (x: System.Array) : int = + match x.Rank with + | 1 -> + let b = x.GetLowerBound(0) + let len = x.Length + let mutable i = b + len - 1 + if i > b + defaultHashNodes then i <- b + defaultHashNodes // limit the hash + let mutable acc = 0 + while (i >= b) do + // NOTE: GenericHash* call decreases nr + acc <- HashCombine i acc (iec.GetHashCode(x.GetValue(i))); + i <- i - 1 + acc + | _ -> + HashCombine 10 (x.GetLength(0)) (x.GetLength(1)) + + // Core implementation of structural hashing, corresponds to pseudo-code in the + // F# Language spec. Searches for the IStructuralHash interface, otherwise uses GetHashCode(). + // Arrays are structurally hashed through a separate technique. + // + // "iec" is either fsEqualityComparerUnlimitedHashingER, fsEqualityComparerUnlimitedHashingPER or a CountLimitedHasherPER. + let rec GenericHashParamObj (iec : IEqualityComparer) (x: obj) : int = + match x with + | null -> 0 + | (:? System.Array as a) -> + match a with + | :? (obj array) as oa -> GenericHashObjArray iec oa + | :? (byte array) as ba -> GenericHashByteArray ba + | :? (int array) as ba -> GenericHashInt32Array ba + | :? (int64 array) as ba -> GenericHashInt64Array ba + | _ -> GenericHashArbArray iec a + | :? IStructuralEquatable as a -> + a.GetHashCode(iec) + | _ -> + x.GetHashCode() + //------------------------------------------------------------------------- // LanguagePrimitives.HashCompare: Physical Equality //------------------------------------------------------------------------- @@ -949,10 +1066,10 @@ namespace Microsoft.FSharp.Core | (:? System.Array as arr1),_ -> match arr1,yobj with // Fast path - | (:? (obj[]) as arr1), (:? (obj[]) as arr2) -> + | (:? (obj array) as arr1), (:? (obj array) as arr2) -> GenericComparisonObjArrayWithComparer comp arr1 arr2 // Fast path - | (:? (byte[]) as arr1), (:? (byte[]) as arr2) -> + | (:? (byte array) as arr1), (:? (byte array) as arr2) -> GenericComparisonByteArray arr1 arr2 | _, (:? System.Array as arr2) -> GenericComparisonArbArrayWithComparer comp arr1 arr2 @@ -1050,7 +1167,7 @@ namespace Microsoft.FSharp.Core precheck (k+1) let c = precheck 0 if c <> 0 then c else - let idxs : int64[] = zeroCreate ndims + let idxs : int64 array = zeroCreate ndims let rec checkN k baseIdx i lim = if i >=. lim then 0 else set idxs k (baseIdx +. i) @@ -1067,7 +1184,7 @@ namespace Microsoft.FSharp.Core check 0 /// optimized case: Core implementation of structural comparison on object arrays. - and GenericComparisonObjArrayWithComparer (comp:GenericComparer) (x:obj[]) (y:obj[]) : int = + and GenericComparisonObjArrayWithComparer (comp:GenericComparer) (x:obj array) (y:obj array) : int = let lenx = x.Length let leny = y.Length let c = intOrder lenx leny @@ -1082,7 +1199,7 @@ namespace Microsoft.FSharp.Core res /// optimized case: Core implementation of structural comparison on arrays. - and GenericComparisonByteArray (x:byte[]) (y:byte[]) : int = + and GenericComparisonByteArray (x:byte array) (y:byte array) : int = let lenx = x.Length let leny = y.Length let c = intOrder lenx leny @@ -1333,7 +1450,7 @@ namespace Microsoft.FSharp.Core /// optimized case: Core implementation of structural equality on arrays. - let GenericEqualityByteArray (x:byte[]) (y:byte[]) : bool= + let GenericEqualityByteArray (x:byte array) (y:byte array) : bool= let lenx = x.Length let leny = y.Length let c = (lenx = leny) @@ -1348,7 +1465,7 @@ namespace Microsoft.FSharp.Core res /// optimized case: Core implementation of structural equality on arrays. - let GenericEqualityInt32Array (x:int[]) (y:int[]) : bool= + let GenericEqualityInt32Array (x:int array) (y:int array) : bool= let lenx = x.Length let leny = y.Length let c = (lenx = leny) @@ -1363,7 +1480,7 @@ namespace Microsoft.FSharp.Core res /// optimized case: Core implementation of structural equality on arrays - let GenericEqualitySingleArray er (x:float32[]) (y:float32[]) : bool= + let GenericEqualitySingleArray er (x:float32 array) (y:float32 array) : bool= let lenx = x.Length let leny = y.Length let f32eq x y = if er && not(float32Eq x x) && not(float32Eq y y) then true else (float32Eq x y) @@ -1379,7 +1496,7 @@ namespace Microsoft.FSharp.Core res /// optimized case: Core implementation of structural equality on arrays. - let GenericEqualityDoubleArray er (x:float[]) (y:float[]) : bool= + let GenericEqualityDoubleArray er (x:float array) (y:float array) : bool= let lenx = x.Length let leny = y.Length let c = (lenx = leny) @@ -1395,7 +1512,7 @@ namespace Microsoft.FSharp.Core res /// optimized case: Core implementation of structural equality on arrays. - let GenericEqualityCharArray (x:char[]) (y:char[]) : bool= + let GenericEqualityCharArray (x:char array) (y:char array) : bool= let lenx = x.Length let leny = y.Length let c = (lenx = leny) @@ -1410,7 +1527,7 @@ namespace Microsoft.FSharp.Core res /// optimized case: Core implementation of structural equality on arrays. - let GenericEqualityInt64Array (x:int64[]) (y:int64[]) : bool= + let GenericEqualityInt64Array (x:int64 array) (y:int64 array) : bool= let lenx = x.Length let leny = y.Length let c = (lenx = leny) @@ -1445,14 +1562,14 @@ namespace Microsoft.FSharp.Core | (:? System.Array as arr1),_ -> match arr1,yobj with // Fast path - | (:? (obj[]) as arr1), (:? (obj[]) as arr2) -> GenericEqualityObjArray er iec arr1 arr2 + | (:? (obj array) as arr1), (:? (obj array) as arr2) -> GenericEqualityObjArray er iec arr1 arr2 // Fast path - | (:? (byte[]) as arr1), (:? (byte[]) as arr2) -> GenericEqualityByteArray arr1 arr2 - | (:? (int32[]) as arr1), (:? (int32[]) as arr2) -> GenericEqualityInt32Array arr1 arr2 - | (:? (int64[]) as arr1), (:? (int64[]) as arr2) -> GenericEqualityInt64Array arr1 arr2 - | (:? (char[]) as arr1), (:? (char[]) as arr2) -> GenericEqualityCharArray arr1 arr2 - | (:? (float32[]) as arr1), (:? (float32[]) as arr2) -> GenericEqualitySingleArray er arr1 arr2 - | (:? (float[]) as arr1), (:? (float[]) as arr2) -> GenericEqualityDoubleArray er arr1 arr2 + | (:? (byte array) as arr1), (:? (byte array) as arr2) -> GenericEqualityByteArray arr1 arr2 + | (:? (int32 array) as arr1), (:? (int32 array) as arr2) -> GenericEqualityInt32Array arr1 arr2 + | (:? (int64 array) as arr1), (:? (int64 array) as arr2) -> GenericEqualityInt64Array arr1 arr2 + | (:? (char array) as arr1), (:? (char array) as arr2) -> GenericEqualityCharArray arr1 arr2 + | (:? (float32 array) as arr1), (:? (float32 array) as arr2) -> GenericEqualitySingleArray er arr1 arr2 + | (:? (float array) as arr1), (:? (float array) as arr2) -> GenericEqualityDoubleArray er arr1 arr2 | _, (:? System.Array as arr2) -> GenericEqualityArbArray er iec arr1 arr2 | _ -> xobj.Equals(yobj) | (:? IStructuralEquatable as x1),_ -> x1.Equals(yobj,iec) @@ -1507,7 +1624,7 @@ namespace Microsoft.FSharp.Core int32Eq (x.GetLowerBound(k)) (y.GetLowerBound(k)) && precheck (k+1)) precheck 0 && - let idxs : int64[] = zeroCreate ndims + let idxs : int64 array = zeroCreate ndims // check contents let rec checkN k baseIdx i lim = (i >=. lim) || @@ -1523,7 +1640,7 @@ namespace Microsoft.FSharp.Core check 0 /// optimized case: Core implementation of structural equality on object arrays. - and GenericEqualityObjArray er iec (x:obj[]) (y:obj[]) : bool = + and GenericEqualityObjArray er iec (x:obj array) (y:obj array) : bool = let lenx = x.Length let leny = y.Length let c = (lenx = leny ) @@ -1663,126 +1780,6 @@ namespace Microsoft.FSharp.Core when 'T : decimal = System.Decimal.op_Equality((# "" x:decimal #), (# "" y:decimal #)) when 'T : DateTime = DateTime.Equals((# "" x : DateTime #), (# "" y : DateTime #)) - //------------------------------------------------------------------------- - // LanguagePrimitives.HashCompare: HASHING. - //------------------------------------------------------------------------- - - - - let defaultHashNodes = 18 - - /// The implementation of IEqualityComparer, using depth-limited for hashing and PER semantics for NaN equality. - type CountLimitedHasherPER(sz:int) = - [] - val mutable nodeCount : int - - member x.Fresh() = - if (System.Threading.Interlocked.CompareExchange(&(x.nodeCount), sz, 0) = 0) then - x - else - new CountLimitedHasherPER(sz) - - interface IEqualityComparer - - /// The implementation of IEqualityComparer, using unlimited depth for hashing and ER semantics for NaN equality. - type UnlimitedHasherER() = - interface IEqualityComparer - - /// The implementation of IEqualityComparer, using unlimited depth for hashing and PER semantics for NaN equality. - type UnlimitedHasherPER() = - interface IEqualityComparer - - - /// The unique object for unlimited depth for hashing and ER semantics for equality. - let fsEqualityComparerUnlimitedHashingER = UnlimitedHasherER() - - /// The unique object for unlimited depth for hashing and PER semantics for equality. - let fsEqualityComparerUnlimitedHashingPER = UnlimitedHasherPER() - - let inline HashCombine nr x y = (x <<< 1) + y + 631 * nr - - let GenericHashObjArray (iec : IEqualityComparer) (x: obj[]) : int = - let len = x.Length - let mutable i = len - 1 - if i > defaultHashNodes then i <- defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= 0) do - // NOTE: GenericHash* call decreases nr - acc <- HashCombine i acc (iec.GetHashCode(x.GetValue(i))); - i <- i - 1 - acc - - // optimized case - byte arrays - let GenericHashByteArray (x: byte[]) : int = - let len = length x - let mutable i = len - 1 - if i > defaultHashNodes then i <- defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= 0) do - acc <- HashCombine i acc (intOfByte (get x i)); - i <- i - 1 - acc - - // optimized case - int arrays - let GenericHashInt32Array (x: int[]) : int = - let len = length x - let mutable i = len - 1 - if i > defaultHashNodes then i <- defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= 0) do - acc <- HashCombine i acc (get x i); - i <- i - 1 - acc - - // optimized case - int arrays - let GenericHashInt64Array (x: int64[]) : int = - let len = length x - let mutable i = len - 1 - if i > defaultHashNodes then i <- defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= 0) do - acc <- HashCombine i acc (int32 (get x i)); - i <- i - 1 - acc - - // special case - arrays do not by default have a decent structural hashing function - let GenericHashArbArray (iec : IEqualityComparer) (x: System.Array) : int = - match x.Rank with - | 1 -> - let b = x.GetLowerBound(0) - let len = x.Length - let mutable i = b + len - 1 - if i > b + defaultHashNodes then i <- b + defaultHashNodes // limit the hash - let mutable acc = 0 - while (i >= b) do - // NOTE: GenericHash* call decreases nr - acc <- HashCombine i acc (iec.GetHashCode(x.GetValue(i))); - i <- i - 1 - acc - | _ -> - HashCombine 10 (x.GetLength(0)) (x.GetLength(1)) - - // Core implementation of structural hashing, corresponds to pseudo-code in the - // F# Language spec. Searches for the IStructuralHash interface, otherwise uses GetHashCode(). - // Arrays are structurally hashed through a separate technique. - // - // "iec" is either fsEqualityComparerUnlimitedHashingER, fsEqualityComparerUnlimitedHashingPER or a CountLimitedHasherPER. - let rec GenericHashParamObj (iec : IEqualityComparer) (x: obj) : int = - match x with - | null -> 0 - | (:? System.Array as a) -> - match a with - | :? (obj[]) as oa -> GenericHashObjArray iec oa - | :? (byte[]) as ba -> GenericHashByteArray ba - | :? (int[]) as ba -> GenericHashInt32Array ba - | :? (int64[]) as ba -> GenericHashInt64Array ba - | _ -> GenericHashArbArray iec a - | :? IStructuralEquatable as a -> - a.GetHashCode(iec) - | _ -> - x.GetHashCode() - - /// Fill in the implementation of CountLimitedHasherPER type CountLimitedHasherPER with @@ -2601,7 +2598,7 @@ namespace Microsoft.FSharp.Core type Type with /// Gets a single static non-conversion operator or method by types - member inline this.GetSingleStaticMethodByTypes(name: string, parameterTypes: Type[]) = + member inline this.GetSingleStaticMethodByTypes(name: string, parameterTypes: Type array) = let staticBindingFlags = (# "" 0b111000 : BindingFlags #) // BindingFlags.Static ||| BindingFlags.Public ||| BindingFlags.NonPublic this.GetMethod(name, staticBindingFlags, null, parameterTypes, null ) @@ -4001,7 +3998,7 @@ namespace Microsoft.FSharp.Collections let items length = let items = zeroCreate length - let rec copy (items: 'T[]) l i = + let rec copy (items: 'T array) l i = match l with | [] -> () | h :: t -> @@ -5966,11 +5963,11 @@ namespace Microsoft.FSharp.Core low, high - let inline GetArraySlice (source: _[]) start finish = + let inline GetArraySlice (source: _ array) start finish = let start, finish = ComputeSlice 0 start finish source.Length GetArraySub source start (finish - start + 1) - let inline SetArraySlice (target: _[]) start finish (source: _[]) = + let inline SetArraySlice (target: _ array) start finish (source: _ array) = let start = (match start with None -> 0 | Some n -> n) let finish = (match finish with None -> target.Length - 1 | Some n -> n) SetArraySub target start (finish - start + 1) source @@ -6002,7 +5999,7 @@ namespace Microsoft.FSharp.Core let inline GetArraySlice2DFixed2 (source: _[,]) start1 finish1 index2 = GetArraySlice2DFixed source start1 finish1 index2 0 - let inline SetArraySlice2DFixed (target: _[,]) (source: _[]) index start finish nonFixedDim = + let inline SetArraySlice2DFixed (target: _[,]) (source: _ array) index start finish nonFixedDim = let bound = target.GetLowerBound(nonFixedDim) let start, finish = ComputeSlice bound start finish (GetArray2DLength target nonFixedDim) let len = (finish - start + 1) @@ -6014,9 +6011,9 @@ namespace Microsoft.FSharp.Core for j = 0 to len - 1 do setArrayElem j - let inline SetArraySlice2DFixed1 (target: _[,]) index1 start2 finish2 (source: _[]) = SetArraySlice2DFixed target source index1 start2 finish2 1 + let inline SetArraySlice2DFixed1 (target: _[,]) index1 start2 finish2 (source: _ array) = SetArraySlice2DFixed target source index1 start2 finish2 1 - let inline SetArraySlice2DFixed2 (target: _[,]) start1 finish1 index2 (source:_[]) = SetArraySlice2DFixed target source index2 start1 finish1 0 + let inline SetArraySlice2DFixed2 (target: _[,]) start1 finish1 index2 (source:_ array) = SetArraySlice2DFixed target source index2 start1 finish1 0 let inline SetArraySlice2D (target: _[,]) start1 finish1 start2 finish2 (source: _[,]) = let bound1 = target.GetLowerBound(0) @@ -6130,7 +6127,7 @@ namespace Microsoft.FSharp.Core let inline SetArraySlice3DFixedSingle3 (target: _[,,]) start1 finish1 start2 finish2 index (source: _[,]) = SetArraySlice3DFixedSingle target source index start1 finish1 start2 finish2 0 1 - let inline SetArraySlice3DFixedDouble (target: _[,,]) (source: _[]) index1 index2 start finish nonFixedDim = + let inline SetArraySlice3DFixedDouble (target: _[,,]) (source: _ array) index1 index2 start finish nonFixedDim = let bound = target.GetLowerBound(nonFixedDim) let start, finish = ComputeSlice bound start finish (GetArray3DLength target nonFixedDim) let len = (finish - start + 1) @@ -6143,13 +6140,13 @@ namespace Microsoft.FSharp.Core for j = 0 to len - 1 do setArrayElem j - let inline SetArraySlice3DFixedDouble1 (target: _[,,]) index1 index2 start3 finish3 (source: _[]) = + let inline SetArraySlice3DFixedDouble1 (target: _[,,]) index1 index2 start3 finish3 (source: _ array) = SetArraySlice3DFixedDouble target source index1 index2 start3 finish3 2 - let inline SetArraySlice3DFixedDouble2 (target: _[,,]) index1 start2 finish2 index3 (source: _[]) = + let inline SetArraySlice3DFixedDouble2 (target: _[,,]) index1 start2 finish2 index3 (source: _ array) = SetArraySlice3DFixedDouble target source index1 index3 start2 finish2 1 - let inline SetArraySlice3DFixedDouble3 (target: _[,,]) start1 finish1 index2 index3 (source: _[]) = + let inline SetArraySlice3DFixedDouble3 (target: _[,,]) start1 finish1 index2 index3 (source: _ array) = SetArraySlice3DFixedDouble target source index2 index3 start1 finish1 0 let inline GetArraySlice4D (source: _[,,,]) start1 finish1 start2 finish2 start3 finish3 start4 finish4 = @@ -6367,7 +6364,7 @@ namespace Microsoft.FSharp.Core let inline SetArraySlice4DFixedDouble6 (target: _[,,,]) start1 finish1 start2 finish2 index3 index4 (source: _[,]) = SetArraySlice4DFixedDouble target source index3 index4 start1 finish1 start2 finish2 0 1 - let inline SetArraySlice4DFixedTriple (target: _[,,,]) (source: _[]) index1 index2 index3 start1 finish1 nonFixedDim1 = + let inline SetArraySlice4DFixedTriple (target: _[,,,]) (source: _ array) index1 index2 index3 start1 finish1 nonFixedDim1 = let bound1 = target.GetLowerBound(nonFixedDim1) let start1, finish1 = ComputeSlice bound1 start1 finish1 (GetArray4DLength target nonFixedDim1) let len1 = (finish1 - start1 + 1) @@ -6381,16 +6378,16 @@ namespace Microsoft.FSharp.Core for i = 0 to len1 - 1 do setArrayElem i - let inline SetArraySlice4DFixedTriple1 (target: _[,,,]) start1 finish1 index2 index3 index4 (source: _[]) = + let inline SetArraySlice4DFixedTriple1 (target: _[,,,]) start1 finish1 index2 index3 index4 (source: _ array) = SetArraySlice4DFixedTriple target source index2 index3 index4 start1 finish1 0 - let inline SetArraySlice4DFixedTriple2 (target: _[,,,]) index1 start2 finish2 index3 index4 (source: _[]) = + let inline SetArraySlice4DFixedTriple2 (target: _[,,,]) index1 start2 finish2 index3 index4 (source: _ array) = SetArraySlice4DFixedTriple target source index1 index3 index4 start2 finish2 1 - let inline SetArraySlice4DFixedTriple3 (target: _[,,,]) index1 index2 start3 finish3 index4 (source: _[]) = + let inline SetArraySlice4DFixedTriple3 (target: _[,,,]) index1 index2 start3 finish3 index4 (source: _ array) = SetArraySlice4DFixedTriple target source index1 index2 index4 start3 finish3 2 - let inline SetArraySlice4DFixedTriple4 (target: _[,,,]) index1 index2 index3 start4 finish4 (source: _[]) = + let inline SetArraySlice4DFixedTriple4 (target: _[,,,]) index1 index2 index3 start4 finish4 (source: _ array) = SetArraySlice4DFixedTriple target source index1 index2 index3 start4 finish4 3 let inline GetStringSlice (source: string) start finish = diff --git a/src/FSharp.Core/prim-types.fsi b/src/FSharp.Core/prim-types.fsi index 655a31a8c87..dc4b9cc22dd 100644 --- a/src/FSharp.Core/prim-types.fsi +++ b/src/FSharp.Core/prim-types.fsi @@ -691,7 +691,7 @@ namespace Microsoft.FSharp.Core /// The name of the resource needed to resolve the source construct. /// /// CompilationMappingAttribute - new: resourceName:string * typeDefinitions:System.Type[] -> CompilationMappingAttribute + new: resourceName:string * typeDefinitions:System.Type array -> CompilationMappingAttribute /// Indicates the relationship between the compiled entity and F# source code member SourceConstructFlags: SourceConstructFlags @@ -706,7 +706,7 @@ namespace Microsoft.FSharp.Core member ResourceName: string /// Indicates the type definitions needed to resolve the source construct - member TypeDefinitions: System.Type[] + member TypeDefinitions: System.Type array /// This attribute is inserted automatically by the F# compiler to tag /// methods which are given the 'CompiledName' attribute. @@ -809,7 +809,7 @@ namespace Microsoft.FSharp.Core /// Indicates the number of arguments in each argument group. /// /// CompilationArgumentCountsAttribute - new: counts:int[] -> CompilationArgumentCountsAttribute + new: counts:int array -> CompilationArgumentCountsAttribute /// Indicates the number of arguments in each argument group member Counts: System.Collections.Generic.IEnumerable @@ -1785,7 +1785,7 @@ namespace Microsoft.FSharp.Core /// The standard overloaded associative (indexed) lookup operator //[] - val inline GetArray: source: 'T[] -> index: int -> 'T + val inline GetArray: source: 'T array -> index: int -> 'T /// The standard overloaded associative (2-indexed) lookup operator //[] @@ -1801,7 +1801,7 @@ namespace Microsoft.FSharp.Core /// The standard overloaded associative (indexed) mutation operator //[] - val inline SetArray: target: 'T[] -> index: int -> value: 'T -> unit + val inline SetArray: target: 'T array -> index: int -> value: 'T -> unit /// The standard overloaded associative (2-indexed) mutation operator //[] @@ -4744,7 +4744,7 @@ namespace Microsoft.FSharp.Core /// The end index. /// /// The sub array from the input indices. - val inline GetArraySlice: source:'T[] -> start:int option -> finish:int option -> 'T[] + val inline GetArraySlice: source:'T array -> start:int option -> finish:int option -> 'T array /// Sets a slice of an array /// @@ -4752,7 +4752,7 @@ namespace Microsoft.FSharp.Core /// The start index. /// The end index. /// The source array. - val inline SetArraySlice: target:'T[] -> start:int option -> finish:int option -> source:'T[] -> unit + val inline SetArraySlice: target:'T array -> start:int option -> finish:int option -> source:'T array -> unit /// Gets a region slice of an array /// @@ -4773,7 +4773,7 @@ namespace Microsoft.FSharp.Core /// The end index of the second dimension. /// /// The sub array from the input indices. - val inline GetArraySlice2DFixed1: source:'T[,] -> index1:int -> start2:int option -> finish2:int option -> 'T[] + val inline GetArraySlice2DFixed1: source:'T[,] -> index1:int -> start2:int option -> finish2:int option -> 'T array /// Gets a vector slice of a 2D array. The index of the second dimension is fixed. /// @@ -4783,7 +4783,7 @@ namespace Microsoft.FSharp.Core /// The fixed index of the second dimension. /// /// The sub array from the input indices. - val inline GetArraySlice2DFixed2: source:'T[,] -> start1:int option -> finish1:int option -> index2: int -> 'T[] + val inline GetArraySlice2DFixed2: source:'T[,] -> start1:int option -> finish1:int option -> index2: int -> 'T array /// Sets a region slice of an array /// @@ -4802,7 +4802,7 @@ namespace Microsoft.FSharp.Core /// The start index of the second dimension. /// The end index of the second dimension. /// The source array. - val inline SetArraySlice2DFixed1: target:'T[,] -> index1:int -> start2:int option -> finish2:int option -> source:'T[] -> unit + val inline SetArraySlice2DFixed1: target:'T[,] -> index1:int -> start2:int option -> finish2:int option -> source:'T array -> unit /// Sets a vector slice of a 2D array. The index of the second dimension is fixed. /// @@ -4811,7 +4811,7 @@ namespace Microsoft.FSharp.Core /// The end index of the first dimension. /// The index of the second dimension. /// The source array. - val inline SetArraySlice2DFixed2: target:'T[,] -> start1:int option -> finish1:int option -> index2:int -> source:'T[] -> unit + val inline SetArraySlice2DFixed2: target:'T[,] -> start1:int option -> finish1:int option -> index2:int -> source:'T array -> unit /// Gets a slice of an array /// @@ -4871,7 +4871,7 @@ namespace Microsoft.FSharp.Core /// The end index of the third dimension. /// /// The one dimensional sub array from the given indices. - val inline GetArraySlice3DFixedDouble1: source:'T[,,] -> index1:int -> index2:int -> start3:int option -> finish3:int option -> 'T[] + val inline GetArraySlice3DFixedDouble1: source:'T[,,] -> index1:int -> index2:int -> start3:int option -> finish3:int option -> 'T array /// Gets a 1D slice of a 3D array. /// @@ -4882,7 +4882,7 @@ namespace Microsoft.FSharp.Core /// The fixed index of the third dimension. /// /// The one dimensional sub array from the given indices. - val inline GetArraySlice3DFixedDouble2: source:'T[,,] -> index1:int -> start2:int option -> finish2:int option -> index3:int -> 'T[] + val inline GetArraySlice3DFixedDouble2: source:'T[,,] -> index1:int -> start2:int option -> finish2:int option -> index3:int -> 'T array /// Gets a 1D slice of a 3D array. /// @@ -4893,7 +4893,7 @@ namespace Microsoft.FSharp.Core /// The fixed index of the third dimension. /// /// The one dimensional sub array from the given indices. - val inline GetArraySlice3DFixedDouble3: source:'T[,,] -> start1:int option -> finish1:int option -> index2:int -> index3:int -> 'T[] + val inline GetArraySlice3DFixedDouble3: source:'T[,,] -> start1:int option -> finish1:int option -> index2:int -> index3:int -> 'T array /// Sets a slice of an array /// @@ -4956,7 +4956,7 @@ namespace Microsoft.FSharp.Core /// The source array. /// /// The one dimensional sub array from the given indices. - val inline SetArraySlice3DFixedDouble1: target: 'T[,,] -> index1: int -> index2: int -> start3: int option -> finish3: int option -> source: 'T[] -> unit + val inline SetArraySlice3DFixedDouble1: target: 'T[,,] -> index1: int -> index2: int -> start3: int option -> finish3: int option -> source: 'T array -> unit /// Sets a 1D slice of a 3D array. /// @@ -4968,7 +4968,7 @@ namespace Microsoft.FSharp.Core /// The source array. /// /// The one dimensional sub array from the given indices. - val inline SetArraySlice3DFixedDouble2: target: 'T[,,] -> index1: int -> start2: int option -> finish2: int option -> index3: int -> source: 'T[] -> unit + val inline SetArraySlice3DFixedDouble2: target: 'T[,,] -> index1: int -> start2: int option -> finish2: int option -> index3: int -> source: 'T array -> unit /// Sets a 1D slice of a 3D array. /// @@ -4980,7 +4980,7 @@ namespace Microsoft.FSharp.Core /// The source array. /// /// The one dimensional sub array from the given indices. - val inline SetArraySlice3DFixedDouble3: target: 'T[,,] -> start1: int option -> finish1: int option -> index2: int -> index3: int -> source: 'T[] -> unit + val inline SetArraySlice3DFixedDouble3: target: 'T[,,] -> start1: int option -> finish1: int option -> index2: int -> index3: int -> source: 'T array -> unit /// Gets a slice of an array /// @@ -5141,7 +5141,7 @@ namespace Microsoft.FSharp.Core /// The end index of the fourth dimension. /// /// The one dimensional sub array from the given indices. - val inline GetArraySlice4DFixedTriple4: source: 'T[,,,] -> index1: int -> index2: int -> index3: int -> start4: int option -> finish4: int option -> 'T[] + val inline GetArraySlice4DFixedTriple4: source: 'T[,,,] -> index1: int -> index2: int -> index3: int -> start4: int option -> finish4: int option -> 'T array /// Gets a 1D slice of a 4D array /// @@ -5153,7 +5153,7 @@ namespace Microsoft.FSharp.Core /// The fixed index of the fourth dimension. /// /// The one dimensional sub array from the given indices. - val inline GetArraySlice4DFixedTriple3: source: 'T[,,,] -> index1: int -> index2: int -> start3: int option -> finish3: int option -> index4: int -> 'T[] + val inline GetArraySlice4DFixedTriple3: source: 'T[,,,] -> index1: int -> index2: int -> start3: int option -> finish3: int option -> index4: int -> 'T array /// Gets a 1D slice of a 4D array /// @@ -5165,7 +5165,7 @@ namespace Microsoft.FSharp.Core /// The fixed index of the fourth dimension. /// /// The one dimensional sub array from the given indices. - val inline GetArraySlice4DFixedTriple2: source:'T[,,,] -> index1: int -> start2: int option -> finish2: int option -> index3: int -> index4: int -> 'T[] + val inline GetArraySlice4DFixedTriple2: source:'T[,,,] -> index1: int -> start2: int option -> finish2: int option -> index3: int -> index4: int -> 'T array /// Gets a 1D slice of a 4D array /// @@ -5177,7 +5177,7 @@ namespace Microsoft.FSharp.Core /// The fixed index of the fourth dimension. /// /// The one dimensional sub array from the given indices. - val inline GetArraySlice4DFixedTriple1: source: 'T[,,,] -> start1: int option -> finish1: int option -> index2: int -> index3: int -> index4: int -> 'T[] + val inline GetArraySlice4DFixedTriple1: source: 'T[,,,] -> start1: int option -> finish1: int option -> index2: int -> index3: int -> index4: int -> 'T array /// Sets a 3D slice of a 4D array /// @@ -5312,7 +5312,7 @@ namespace Microsoft.FSharp.Core /// The start index of the fourth dimension. /// The end index of the fourth dimension. /// The source array. - val inline SetArraySlice4DFixedTriple4: target:'T[,,,] -> index1:int -> index2:int -> index3:int -> start4:int option -> finish4:int option -> source: 'T[] -> unit + val inline SetArraySlice4DFixedTriple4: target:'T[,,,] -> index1:int -> index2:int -> index3:int -> start4:int option -> finish4:int option -> source: 'T array -> unit /// Sets a 1D slice of a 4D array /// @@ -5323,7 +5323,7 @@ namespace Microsoft.FSharp.Core /// The end index of the third dimension. /// The fixed index of the fourth dimension. /// The source array. - val inline SetArraySlice4DFixedTriple3: target:'T[,,,] -> index1:int -> index2:int -> start3:int option -> finish3:int option -> index4:int -> source: 'T[] -> unit + val inline SetArraySlice4DFixedTriple3: target:'T[,,,] -> index1:int -> index2:int -> start3:int option -> finish3:int option -> index4:int -> source: 'T array -> unit /// Sets a 1D slice of a 4D array /// @@ -5334,7 +5334,7 @@ namespace Microsoft.FSharp.Core /// The fixed index of the third dimension. /// The fixed index of the fourth dimension. /// The source array. - val inline SetArraySlice4DFixedTriple2: target:'T[,,,] -> index1:int -> start2: int option -> finish2:int option -> index3:int -> index4:int -> source: 'T[] -> unit + val inline SetArraySlice4DFixedTriple2: target:'T[,,,] -> index1:int -> start2: int option -> finish2:int option -> index3:int -> index4:int -> source: 'T array -> unit /// Sets a 1D slice of a 4D array /// @@ -5345,7 +5345,7 @@ namespace Microsoft.FSharp.Core /// The fixed index of the third dimension. /// The fixed index of the fourth dimension. /// The source array. - val inline SetArraySlice4DFixedTriple1: target:'T[,,,] -> start1:int option -> finish1:int option -> index2:int -> index3:int -> index4:int -> source: 'T[] -> unit + val inline SetArraySlice4DFixedTriple1: target:'T[,,,] -> start1:int option -> finish1:int option -> index2:int -> index3:int -> index4:int -> source: 'T array -> unit /// Sets a slice of an array /// diff --git a/src/FSharp.Core/printf.fs b/src/FSharp.Core/printf.fs index 9a7d461d698..7afcce1e66b 100644 --- a/src/FSharp.Core/printf.fs +++ b/src/FSharp.Core/printf.fs @@ -19,7 +19,7 @@ open LanguagePrimitives.IntrinsicOperators type PrintfFormat<'Printer, 'State, 'Residue, 'Result> [] - (value:string, captures: obj[], captureTys: Type[]) = + (value:string, captures: obj array, captureTys: Type array) = [] new (value) = new PrintfFormat<'Printer, 'State, 'Residue, 'Result>(value, null, null) @@ -34,7 +34,7 @@ type PrintfFormat<'Printer, 'State, 'Residue, 'Result> type PrintfFormat<'Printer, 'State, 'Residue, 'Result, 'Tuple> [] - (value:string, captures, captureTys: Type[]) = + (value:string, captures, captureTys: Type array) = inherit PrintfFormat<'Printer, 'State, 'Residue, 'Result>(value, captures, captureTys) @@ -274,7 +274,7 @@ module internal PrintfImpl = | StepPercentStar2 of prefix: string // Count the number of string fragments in a sequence of steps - static member BlockCount(steps: Step[]) = + static member BlockCount(steps: Step array) = let mutable count = 0 for step in steps do match step with @@ -323,7 +323,7 @@ module internal PrintfImpl = if not (String.IsNullOrEmpty s) then env.Write s - member env.RunSteps (args: obj[], argTys: Type[], steps: Step[]) = + member env.RunSteps (args: obj array, argTys: Type array, steps: Step array) = let mutable argIndex = 0 let mutable tyIndex = 0 @@ -1029,7 +1029,7 @@ module internal PrintfImpl = type LargeStringPrintfEnv<'Result>(continuation, blockSize) = inherit PrintfEnv(()) - let buf: string[] = Array.zeroCreate blockSize + let buf: string array = Array.zeroCreate blockSize let mutable ptr = 0 override _.Finish() : 'Result = continuation (String.Concat buf) @@ -1089,8 +1089,8 @@ module internal PrintfImpl = [] type FormatParser<'Printer, 'State, 'Residue, 'Result>(fmt: string) = - let buildCaptureFunc (spec: FormatSpecifier, allSteps, argTys: Type[], retTy, nextInfo) = - let (next:obj, nextCanCombine: bool, nextArgTys: Type[], nextRetTy, nextNextOpt) = nextInfo + let buildCaptureFunc (spec: FormatSpecifier, allSteps, argTys: Type array, retTy, nextInfo) = + let (next:obj, nextCanCombine: bool, nextArgTys: Type array, nextRetTy, nextNextOpt) = nextInfo assert (argTys.Length > 0) // See if we can compress a capture to a multi-capture @@ -1133,7 +1133,7 @@ module internal PrintfImpl = let factoryObj = mi.Invoke(null, [| next |]) factoryObj, true, argTys, retTy, Some next - let buildStep (spec: FormatSpecifier) (argTys: Type[]) prefix = + let buildStep (spec: FormatSpecifier) (argTys: Type array) prefix = if spec.TypeChar = 'a' then StepLittleA prefix elif spec.TypeChar = 't' then diff --git a/src/FSharp.Core/printf.fsi b/src/FSharp.Core/printf.fsi index a188e570183..2e4d009c537 100644 --- a/src/FSharp.Core/printf.fsi +++ b/src/FSharp.Core/printf.fsi @@ -30,16 +30,18 @@ type PrintfFormat<'Printer, 'State, 'Residue, 'Result> = /// The captured expressions in an interpolated string. /// The types of expressions for %A expression gaps in interpolated string. /// The PrintfFormat containing the formatted result. - new: value: string * captures: obj[] * captureTys: Type[] -> PrintfFormat<'Printer, 'State, 'Residue, 'Result> + new: + value: string * captures: obj array * captureTys: Type array -> + PrintfFormat<'Printer, 'State, 'Residue, 'Result> /// The raw text of the format string. member Value: string /// The captures associated with an interpolated string. - member Captures: obj[] + member Captures: obj array /// The capture types associated with an interpolated string. - member CaptureTypes: System.Type[] + member CaptureTypes: System.Type array /// Type of a formatting expression. /// @@ -69,7 +71,7 @@ type PrintfFormat<'Printer, 'State, 'Residue, 'Result, 'Tuple> = /// /// The created format string. new: - value: string * captures: obj[] * captureTys: Type[] -> + value: string * captures: obj array * captureTys: Type array -> PrintfFormat<'Printer, 'State, 'Residue, 'Result, 'Tuple> /// Type of a formatting expression. diff --git a/src/FSharp.Core/quotations.fs b/src/FSharp.Core/quotations.fs index 18a3f4cae12..4c71bf0d928 100644 --- a/src/FSharp.Core/quotations.fs +++ b/src/FSharp.Core/quotations.fs @@ -92,7 +92,7 @@ module Helpers = | null -> nullArg argName | _ -> () - let getTypesFromParamInfos (infos: ParameterInfo[]) = + let getTypesFromParamInfos (infos: ParameterInfo array) = infos |> Array.map (fun pi -> pi.ParameterType) open Helpers @@ -456,7 +456,7 @@ module Patterns = /// as a computation. type Instantiable<'T> = (int -> Type) -> 'T - type ByteStream(bytes: byte[], initial: int, len: int) = + type ByteStream(bytes: byte array, initial: int, len: int) = let mutable pos = initial let lim = initial + len @@ -970,7 +970,7 @@ module Patterns = if (not (assignableFrom expectedType receivedType)) then invalidArg "receivedType" (String.Format(threeHoleSR, name, expectedType, receivedType)) - let checkArgs (paramInfos: ParameterInfo[]) (args: Expr list) = + let checkArgs (paramInfos: ParameterInfo array) (args: Expr list) = if (paramInfos.Length <> args.Length) then invalidArg "args" (SR.GetString(SR.QincorrectNumArgs)) @@ -1381,7 +1381,7 @@ module Patterns = let typesEqual (tys1: Type list) (tys2: Type list) = (tys1.Length = tys2.Length) && List.forall2 typeEquals tys1 tys2 - let instFormal (typarEnv: Type[]) (ty: Instantiable<'T>) = + let instFormal (typarEnv: Type array) (ty: Instantiable<'T>) = ty (fun i -> typarEnv.[i]) let getGenericArguments (genericType: Type) = @@ -1672,9 +1672,9 @@ module Patterns = type InputState = { is: ByteStream - istrings: string[] + istrings: string array localAssembly: System.Reflection.Assembly - referencedTypeDefs: Type[] + referencedTypeDefs: Type array } let u_byte_as_int st = @@ -1935,7 +1935,7 @@ module Patterns = varn = env.varn + 1 } - let mkTyparSubst (tyargs: Type[]) = + let mkTyparSubst (tyargs: Type array) = let n = tyargs.Length fun idx -> @@ -1944,7 +1944,7 @@ module Patterns = else invalidOp (SR.GetString(SR.QtypeArgumentOutOfRange)) - let envClosed (spliceTypes: Type[]) = + let envClosed (spliceTypes: Type array) = { vars = Map.empty varn = 0 @@ -2256,7 +2256,7 @@ module Patterns = //-------------------------------------------------------------------------- /// Fill the holes in an Expr - let rec fillHolesInRawExpr (l: Expr[]) (E t as e) = + let rec fillHolesInRawExpr (l: Expr array) (E t as e) = match t with | VarTerm _ -> e | LambdaTerm(v, b) -> EA(LambdaTerm(v, fillHolesInRawExpr l b), e.CustomAttributes) @@ -2363,7 +2363,7 @@ module Patterns = resourceName.StartsWith(ReflectedDefinitionsResourceNameBase, StringComparison.Ordinal) /// Get the reflected definition at the given (always generic) instantiation - let tryGetReflectedDefinition (methodBase: MethodBase, tyargs: Type[]) = + let tryGetReflectedDefinition (methodBase: MethodBase, tyargs: Type array) = checkNonNull "methodBase" methodBase let data = @@ -2692,12 +2692,12 @@ type Expr with static member Cast(source: Expr) = cast source - static member Deserialize(qualifyingType: Type, spliceTypes, spliceExprs, bytes: byte[]) = + static member Deserialize(qualifyingType: Type, spliceTypes, spliceExprs, bytes: byte array) = checkNonNull "qualifyingType" qualifyingType checkNonNull "bytes" bytes deserialize (qualifyingType, [||], Array.ofList spliceTypes, Array.ofList spliceExprs, bytes) - static member Deserialize40(qualifyingType: Type, referencedTypes, spliceTypes, spliceExprs, bytes: byte[]) = + static member Deserialize40(qualifyingType: Type, referencedTypes, spliceTypes, spliceExprs, bytes: byte array) = checkNonNull "spliceExprs" spliceExprs checkNonNull "spliceTypes" spliceTypes checkNonNull "referencedTypeDefs" referencedTypes diff --git a/src/FSharp.Core/quotations.fsi b/src/FSharp.Core/quotations.fsi index f3aec29009a..ad69f801996 100644 --- a/src/FSharp.Core/quotations.fsi +++ b/src/FSharp.Core/quotations.fsi @@ -1269,7 +1269,7 @@ type Expr = /// /// The resulting expression. static member Deserialize: - qualifyingType: Type * spliceTypes: Type list * spliceExprs: Expr list * bytes: byte[] -> Expr + qualifyingType: Type * spliceTypes: Type list * spliceExprs: Expr list * bytes: byte array -> Expr /// This function is called automatically when quotation syntax (<@ @>) and other sources of /// quotations are used. @@ -1282,7 +1282,11 @@ type Expr = /// /// The resulting expression. static member Deserialize40: - qualifyingType: Type * referencedTypes: Type[] * spliceTypes: Type[] * spliceExprs: Expr[] * bytes: byte[] -> + qualifyingType: Type * + referencedTypes: Type array * + spliceTypes: Type array * + spliceExprs: Expr array * + bytes: byte array -> Expr /// Permits interactive environments such as F# Interactive @@ -1293,7 +1297,8 @@ type Expr = /// The unique name for the resources being added. /// The serialized resource to register with the environment. /// - static member RegisterReflectedDefinitions: assembly: Assembly * resource: string * serializedValue: byte[] -> unit + static member RegisterReflectedDefinitions: + assembly: Assembly * resource: string * serializedValue: byte array -> unit /// Permits interactive environments such as F# Interactive /// to explicitly register new pickled resources that represent persisted @@ -1305,7 +1310,7 @@ type Expr = /// The serialized resource to register with the environment. /// static member RegisterReflectedDefinitions: - assembly: Assembly * resource: string * serializedValue: byte[] * referencedTypes: Type[] -> unit + assembly: Assembly * resource: string * serializedValue: byte array * referencedTypes: Type array -> unit /// Fetches or creates a new variable with the given name and type from a global pool of shared variables /// indexed by name and type. The type is given by the explicit or inferred type parameter diff --git a/src/FSharp.Core/reflect.fs b/src/FSharp.Core/reflect.fs index df487893df9..500d0b2effc 100644 --- a/src/FSharp.Core/reflect.fs +++ b/src/FSharp.Core/reflect.fs @@ -91,12 +91,12 @@ module internal Impl = expr.Compile() - let compileRecordOrUnionCaseReaderFunc (typ, props: PropertyInfo[]) = + let compileRecordOrUnionCaseReaderFunc (typ, props: PropertyInfo array) = let param = Expression.Parameter(typeof, "param") let typedParam = Expression.Variable typ let expr = - Expression.Lambda>( + Expression.Lambda>( Expression.Block( [ typedParam ], Expression.Assign(typedParam, Expression.Convert(param, typ)), @@ -115,10 +115,10 @@ module internal Impl = let compileRecordConstructorFunc (ctorInfo: ConstructorInfo) = let ctorParams = ctorInfo.GetParameters() - let paramArray = Expression.Parameter(typeof, "paramArray") + let paramArray = Expression.Parameter(typeof, "paramArray") let expr = - Expression.Lambda>( + Expression.Lambda>( Expression.Convert( Expression.New( ctorInfo, @@ -139,10 +139,10 @@ module internal Impl = let compileUnionCaseConstructorFunc (methodInfo: MethodInfo) = let methodParams = methodInfo.GetParameters() - let paramArray = Expression.Parameter(typeof, "param") + let paramArray = Expression.Parameter(typeof, "param") let expr = - Expression.Lambda>( + Expression.Lambda>( Expression.Convert( Expression.Call( methodInfo, @@ -192,10 +192,10 @@ module internal Impl = ] ) - let elements = Expression.Parameter(typeof, "elements") + let elements = Expression.Parameter(typeof, "elements") let expr = - Expression.Lambda>( + Expression.Lambda>( Expression.Convert(constituentTuple typ elements 0, typeof), elements ) @@ -208,10 +208,10 @@ module internal Impl = let elements = match getTupleElementAccessors typ with // typ is a struct tuple and its elements are accessed via fields - | Choice1Of2(fi: FieldInfo[]) -> + | Choice1Of2(fi: FieldInfo array) -> fi |> Array.map (fun fi -> Expression.Field(tuple, fi), fi.FieldType) // typ is a class tuple and its elements are accessed via properties - | Choice2Of2(pi: PropertyInfo[]) -> + | Choice2Of2(pi: PropertyInfo array) -> pi |> Array.map (fun pi -> Expression.Property(tuple, pi), pi.PropertyType) for index, (element, elementType) in elements |> Array.indexed do @@ -235,7 +235,7 @@ module internal Impl = } let param = Expression.Parameter(typeof, "outerTuple") - let outputArray = Expression.Variable(typeof, "output") + let outputArray = Expression.Variable(typeof, "output") let rec outputLength tupleEncField (typ: Type) = let genericArgs = typ.GetGenericArguments() @@ -246,7 +246,7 @@ module internal Impl = genericArgs.Length let expr = - Expression.Lambda>( + Expression.Lambda>( Expression.Block( [ outputArray ], [ @@ -266,7 +266,7 @@ module internal Impl = //----------------------------------------------------------------- // ATTRIBUTE DECOMPILATION - let tryFindCompilationMappingAttribute (attrs: obj[]) = + let tryFindCompilationMappingAttribute (attrs: obj array) = match attrs with | null | [||] -> None @@ -275,7 +275,7 @@ module internal Impl = Some(a.SourceConstructFlags, a.SequenceNumber, a.VariantNumber) | _ -> invalidOp (SR.GetString(SR.multipleCompilationMappings)) - let findCompilationMappingAttribute (attrs: obj[]) = + let findCompilationMappingAttribute (attrs: obj array) = match tryFindCompilationMappingAttribute attrs with | None -> failwith "no compilation mapping attribute" | Some a -> a @@ -688,10 +688,10 @@ module internal Impl = |] let private dictionaryLock = obj () - let private refTupleTypes = Dictionary() - let private valueTupleTypes = Dictionary() + let private refTupleTypes = Dictionary() + let private valueTupleTypes = Dictionary() - let rec mkTupleType isStruct (asm: Assembly) (tys: Type[]) = + let rec mkTupleType isStruct (asm: Assembly) (tys: Type array) = let table = let makeIt n = let tupleFullName n = @@ -777,7 +777,7 @@ module internal Impl = [] let lastRegularTupIndex = 6 //nestedTupIndex - 1 (wait for arithmetic in constants) - let rec mkTupleTypeNetStandard (tupTyTbl: Type[]) (tys: Type[]) = + let rec mkTupleTypeNetStandard (tupTyTbl: Type array) (tys: Type array) = let tblIdx = tys.Length - 1 assert (tblIdx >= 0) assert (nestedTupIndex = tupTyTbl.Length - 1) @@ -804,12 +804,12 @@ module internal Impl = else tyargs - let orderTupleProperties (props: PropertyInfo[]) = - // The PropertyInfo[] may not come back in order, so ensure ordering here. + let orderTupleProperties (props: PropertyInfo array) = + // The PropertyInfo array may not come back in order, so ensure ordering here. props |> Array.sortBy (fun p -> p.Name) // alphabetic works because there is max. 8 of them - let orderTupleFields (fields: FieldInfo[]) = - // The FieldInfo[] may not come back in order, so ensure ordering here. + let orderTupleFields (fields: FieldInfo array) = + // The FieldInfo array may not come back in order, so ensure ordering here. fields |> Array.sortBy (fun fi -> fi.Name) // alphabetic works because there is max. 8 of them let getTupleConstructorMethod (typ: Type) = @@ -845,7 +845,7 @@ module internal Impl = let getTupleCtor (typ: Type) = let ctor = getTupleConstructorMethod typ - (fun (args: obj[]) -> + (fun (args: obj array) -> ctor.Invoke(BindingFlags.InvokeMethod ||| BindingFlags.Instance ||| BindingFlags.Public, null, args, null)) let getTupleElementAccessors (typ: Type) = @@ -886,7 +886,7 @@ module internal Impl = let tyBenc = etys.[tupleEncField] let maker2 = getTupleConstructor tyBenc - (fun (args: obj[]) -> + (fun (args: obj array) -> let encVal = maker2 args.[tupleEncField..] maker1 (Array.append args.[0 .. tupleEncField - 1] [| encVal |])) @@ -1005,7 +1005,7 @@ module internal Impl = let getRecordConstructor (typ: Type, bindingFlags) = let ctor = getRecordConstructorMethod (typ, bindingFlags) - (fun (args: obj[]) -> + (fun (args: obj array) -> ctor.Invoke(BindingFlags.InvokeMethod ||| BindingFlags.Instance ||| bindingFlags, null, args, null)) let getRecordConstructorCompiled (typ: Type, bindingFlags) = @@ -1155,7 +1155,7 @@ type FSharpType = checkNonNull "range" range func.MakeGenericType [| domain; range |] - static member MakeTupleType(types: Type[]) = + static member MakeTupleType(types: Type array) = checkNonNull "types" types if types.Length = 0 then @@ -1166,7 +1166,7 @@ type FSharpType = mkTupleTypeNetStandard refTupleTypesNetStandard types - static member MakeTupleType(asm: Assembly, types: Type[]) = + static member MakeTupleType(asm: Assembly, types: Type array) = checkNonNull "types" types if @@ -1179,7 +1179,7 @@ type FSharpType = TupleFromSpecifiedAssembly.mkTupleType false asm types - static member MakeStructTupleType(asm: Assembly, types: Type[]) = + static member MakeStructTupleType(asm: Assembly, types: Type array) = checkNonNull "types" types if @@ -1192,7 +1192,7 @@ type FSharpType = TupleFromSpecifiedAssembly.mkTupleType true asm types - static member MakeStructTupleType(types: Type[]) = + static member MakeStructTupleType(types: Type array) = checkNonNull "types" types if types.Length = 0 then @@ -1278,7 +1278,7 @@ type FSharpValue = checkNonNull "info" info compilePropGetterFunc(info).Invoke - static member PreComputeRecordReader(recordType: Type, ?bindingFlags) : (obj -> obj[]) = + static member PreComputeRecordReader(recordType: Type, ?bindingFlags) : (obj -> obj array) = let bindingFlags = defaultArg bindingFlags BindingFlags.Public checkRecordType ("recordType", recordType, bindingFlags) getRecordReaderCompiled (recordType, bindingFlags) @@ -1308,7 +1308,7 @@ type FSharpValue = let (f: (obj -> obj) -> obj) = downcast o f implementation - static member MakeTuple(tupleElements: obj[], tupleType: Type) = + static member MakeTuple(tupleElements: obj array, tupleType: Type) = checkNonNull "tupleElements" tupleElements checkTupleType ("tupleType", tupleType) getTupleConstructor tupleType tupleElements @@ -1341,7 +1341,7 @@ type FSharpValue = fields.[index] - static member PreComputeTupleReader(tupleType: Type) : (obj -> obj[]) = + static member PreComputeTupleReader(tupleType: Type) : (obj -> obj array) = checkTupleType ("tupleType", tupleType) (compileTupleReader tupleEncField getTupleElementAccessors tupleType).Invoke @@ -1359,7 +1359,7 @@ type FSharpValue = checkTupleType ("tupleType", tupleType) getTupleConstructorInfo tupleType - static member MakeUnion(unionCase: UnionCaseInfo, args: obj[], ?bindingFlags) = + static member MakeUnion(unionCase: UnionCaseInfo, args: obj array, ?bindingFlags) = let bindingFlags = defaultArg bindingFlags BindingFlags.Public checkNonNull "unionCase" unionCase getUnionCaseConstructor (unionCase.DeclaringType, unionCase.Tag, bindingFlags) args @@ -1409,7 +1409,7 @@ type FSharpValue = checkUnionType (unionType, bindingFlags) getUnionTagMemberInfo (unionType, bindingFlags) - static member PreComputeUnionReader(unionCase: UnionCaseInfo, ?bindingFlags) : (obj -> obj[]) = + static member PreComputeUnionReader(unionCase: UnionCaseInfo, ?bindingFlags) : (obj -> obj array) = let bindingFlags = defaultArg bindingFlags BindingFlags.Public checkNonNull "unionCase" unionCase let typ = unionCase.DeclaringType @@ -1460,7 +1460,11 @@ module FSharpReflectionExtensions = let bindingFlags = getBindingFlags allowAccessToPrivateRepresentation FSharpValue.GetRecordFields(record, bindingFlags) - static member PreComputeRecordReader(recordType: Type, ?allowAccessToPrivateRepresentation) : (obj -> obj[]) = + static member PreComputeRecordReader + ( + recordType: Type, + ?allowAccessToPrivateRepresentation + ) : (obj -> obj array) = let bindingFlags = getBindingFlags allowAccessToPrivateRepresentation FSharpValue.PreComputeRecordReader(recordType, bindingFlags) @@ -1472,7 +1476,7 @@ module FSharpReflectionExtensions = let bindingFlags = getBindingFlags allowAccessToPrivateRepresentation FSharpValue.PreComputeRecordConstructorInfo(recordType, bindingFlags) - static member MakeUnion(unionCase: UnionCaseInfo, args: obj[], ?allowAccessToPrivateRepresentation) = + static member MakeUnion(unionCase: UnionCaseInfo, args: obj array, ?allowAccessToPrivateRepresentation) = let bindingFlags = getBindingFlags allowAccessToPrivateRepresentation FSharpValue.MakeUnion(unionCase, args, bindingFlags) @@ -1500,7 +1504,7 @@ module FSharpReflectionExtensions = ( unionCase: UnionCaseInfo, ?allowAccessToPrivateRepresentation - ) : (obj -> obj[]) = + ) : (obj -> obj array) = let bindingFlags = getBindingFlags allowAccessToPrivateRepresentation FSharpValue.PreComputeUnionReader(unionCase, bindingFlags) diff --git a/src/FSharp.Core/reflect.fsi b/src/FSharp.Core/reflect.fsi index 5d69af7f9a3..9aa76f62a69 100644 --- a/src/FSharp.Core/reflect.fsi +++ b/src/FSharp.Core/reflect.fsi @@ -82,7 +82,7 @@ type UnionCaseInfo = /// VariantNumber = 0;}|]|] /// /// - member GetCustomAttributes: unit -> obj[] + member GetCustomAttributes: unit -> obj array /// Returns the custom attributes associated with the case matching the given attribute type. /// The type of attributes to return. @@ -111,7 +111,7 @@ type UnionCaseInfo = /// TypeId = FSI_0147+Signal;}|]|] /// /// - member GetCustomAttributes: attributeType: Type -> obj[] + member GetCustomAttributes: attributeType: Type -> obj array /// Returns the custom attributes data associated with the case. /// An list of custom attribute data items. @@ -176,7 +176,7 @@ type UnionCaseInfo = /// [|("width", "Double"); ("Item2", "Double"); ("height", "Double")|]|] /// /// - member GetFields: unit -> PropertyInfo[] + member GetFields: unit -> PropertyInfo array /// The integer tag for the case. /// @@ -243,7 +243,7 @@ type FSharpValue = /// static member MakeRecord: [] recordType: Type * - values: obj[] * + values: obj array * ?bindingFlags: BindingFlags -> obj @@ -258,7 +258,7 @@ type FSharpValue = /// The array of fields from the record. /// /// - static member GetRecordFields: record: obj * ?bindingFlags: BindingFlags -> obj[] + static member GetRecordFields: record: obj * ?bindingFlags: BindingFlags -> obj array /// Precompute a function for reading all the fields from a record. The fields are returned in the /// same order as the fields reported by a call to Microsoft.FSharp.Reflection.Type.GetInfo for @@ -282,7 +282,7 @@ type FSharpValue = static member PreComputeRecordReader: [] recordType: Type * ?bindingFlags: BindingFlags -> - (obj -> obj[]) + (obj -> obj array) /// Precompute a function for constructing a record value. /// @@ -300,7 +300,7 @@ type FSharpValue = static member PreComputeRecordConstructor: [] recordType: Type * ?bindingFlags: BindingFlags -> - (obj[] -> obj) + (obj array -> obj) /// Get a ConstructorInfo for a record type /// @@ -324,7 +324,7 @@ type FSharpValue = /// The constructed union case. /// /// - static member MakeUnion: unionCase: UnionCaseInfo * args: obj[] * ?bindingFlags: BindingFlags -> obj + static member MakeUnion: unionCase: UnionCaseInfo * args: obj array * ?bindingFlags: BindingFlags -> obj /// Identify the union case and its fields for an object /// @@ -346,7 +346,7 @@ type FSharpValue = value: obj * [] unionType: Type * ?bindingFlags: BindingFlags -> - UnionCaseInfo * obj[] + UnionCaseInfo * obj array /// Assumes the given type is a union type. /// If not, is raised during pre-computation. @@ -387,7 +387,7 @@ type FSharpValue = /// A function to for reading the fields of the given union case. /// /// - static member PreComputeUnionReader: unionCase: UnionCaseInfo * ?bindingFlags: BindingFlags -> (obj -> obj[]) + static member PreComputeUnionReader: unionCase: UnionCaseInfo * ?bindingFlags: BindingFlags -> (obj -> obj array) /// Precompute a function for constructing a discriminated union value for a particular union case. /// @@ -397,7 +397,8 @@ type FSharpValue = /// A function for constructing values of the given union case. /// /// - static member PreComputeUnionConstructor: unionCase: UnionCaseInfo * ?bindingFlags: BindingFlags -> (obj[] -> obj) + static member PreComputeUnionConstructor: + unionCase: UnionCaseInfo * ?bindingFlags: BindingFlags -> (obj array -> obj) /// A method that constructs objects of the given case /// @@ -421,7 +422,7 @@ type FSharpValue = /// The fields from the given exception. /// /// - static member GetExceptionFields: exn: obj * ?bindingFlags: BindingFlags -> obj[] + static member GetExceptionFields: exn: obj * ?bindingFlags: BindingFlags -> obj array /// Creates an instance of a tuple type /// @@ -435,7 +436,7 @@ type FSharpValue = /// An instance of the tuple type with the given elements. /// /// - static member MakeTuple: tupleElements: obj[] * tupleType: Type -> obj + static member MakeTuple: tupleElements: obj array * tupleType: Type -> obj /// Reads a field from a tuple value. /// @@ -460,7 +461,7 @@ type FSharpValue = /// An array of the fields from the given tuple. /// /// - static member GetTupleFields: tuple: obj -> obj[] + static member GetTupleFields: tuple: obj -> obj array /// Precompute a function for reading the values of a particular tuple type /// @@ -475,7 +476,7 @@ type FSharpValue = /// /// static member PreComputeTupleReader: - [] tupleType: Type -> (obj -> obj[]) + [] tupleType: Type -> (obj -> obj array) /// Gets information that indicates how to read a field of a tuple /// @@ -502,7 +503,7 @@ type FSharpValue = /// /// static member PreComputeTupleConstructor: - [] tupleType: Type -> (obj[] -> obj) + [] tupleType: Type -> (obj array -> obj) /// Gets a method that constructs objects of the given tuple type. /// For small tuples, no additional type will be returned. @@ -554,7 +555,7 @@ type FSharpType = static member GetRecordFields: [] recordType: Type * ?bindingFlags: BindingFlags -> - PropertyInfo[] + PropertyInfo array /// Gets the cases of a union type. /// @@ -570,7 +571,7 @@ type FSharpType = /// static member GetUnionCases: [] unionType: Type * ?bindingFlags: BindingFlags -> - UnionCaseInfo[] + UnionCaseInfo array /// Return true if the typ is a representation of an F# record type /// @@ -611,7 +612,7 @@ type FSharpType = static member GetExceptionFields: [] exceptionType: Type * ?bindingFlags: BindingFlags -> - PropertyInfo[] + PropertyInfo array /// Returns true if the typ is a representation of an F# exception declaration /// @@ -646,7 +647,7 @@ type FSharpType = /// The type representing the tuple containing the input elements. /// /// - static member MakeTupleType: types: Type[] -> Type + static member MakeTupleType: types: Type array -> Type /// Returns a representing an F# tuple type with the given element types /// @@ -656,7 +657,7 @@ type FSharpType = /// The type representing the tuple containing the input elements. /// /// - static member MakeTupleType: asm: Assembly * types: Type[] -> Type + static member MakeTupleType: asm: Assembly * types: Type array -> Type /// Returns a representing an F# struct tuple type with the given element types /// @@ -666,7 +667,7 @@ type FSharpType = /// The type representing the struct tuple containing the input elements. /// /// - static member MakeStructTupleType: asm: Assembly * types: Type[] -> Type + static member MakeStructTupleType: asm: Assembly * types: Type array -> Type /// Returns a representing an F# struct tuple type with the given element types /// @@ -675,7 +676,7 @@ type FSharpType = /// The type representing the struct tuple containing the input elements. /// /// - static member MakeStructTupleType: types: Type[] -> Type + static member MakeStructTupleType: types: Type array -> Type /// Return true if the typ is a representation of an F# tuple type /// @@ -712,7 +713,7 @@ type FSharpType = /// /// static member GetTupleElements: - [] tupleType: Type -> Type[] + [] tupleType: Type -> Type array /// Gets the domain and range types from an F# function type or from the runtime type of a closure implementing an F# type /// @@ -744,7 +745,7 @@ module FSharpReflectionExtensions = /// static member MakeRecord: [] recordType: Type * - values: obj[] * + values: obj array * ?allowAccessToPrivateRepresentation: bool -> obj @@ -763,7 +764,7 @@ module FSharpReflectionExtensions = static member GetRecordFields: [] record: obj * ?allowAccessToPrivateRepresentation: bool -> - obj[] + obj array /// Precompute a function for reading all the fields from a record. The fields are returned in the /// same order as the fields reported by a call to Microsoft.FSharp.Reflection.Type.GetInfo for @@ -787,7 +788,7 @@ module FSharpReflectionExtensions = static member PreComputeRecordReader: [] recordType: Type * ?allowAccessToPrivateRepresentation: bool -> - (obj -> obj[]) + (obj -> obj array) /// Precompute a function for constructing a record value. /// @@ -805,7 +806,7 @@ module FSharpReflectionExtensions = static member PreComputeRecordConstructor: [] recordType: Type * ?allowAccessToPrivateRepresentation: bool -> - (obj[] -> obj) + (obj array -> obj) /// Get a ConstructorInfo for a record type /// @@ -830,7 +831,7 @@ module FSharpReflectionExtensions = /// /// static member MakeUnion: - unionCase: UnionCaseInfo * args: obj[] * ?allowAccessToPrivateRepresentation: bool -> obj + unionCase: UnionCaseInfo * args: obj array * ?allowAccessToPrivateRepresentation: bool -> obj /// Identify the union case and its fields for an object /// @@ -853,7 +854,7 @@ module FSharpReflectionExtensions = value: obj * [] unionType: Type * ?allowAccessToPrivateRepresentation: bool -> - UnionCaseInfo * obj[] + UnionCaseInfo * obj array /// Assumes the given type is a union type. /// If not, is raised during pre-computation. @@ -897,7 +898,7 @@ module FSharpReflectionExtensions = /// /// static member PreComputeUnionReader: - unionCase: UnionCaseInfo * ?allowAccessToPrivateRepresentation: bool -> (obj -> obj[]) + unionCase: UnionCaseInfo * ?allowAccessToPrivateRepresentation: bool -> (obj -> obj array) /// Precompute a function for constructing a discriminated union value for a particular union case. /// @@ -908,7 +909,7 @@ module FSharpReflectionExtensions = /// /// static member PreComputeUnionConstructor: - unionCase: UnionCaseInfo * ?allowAccessToPrivateRepresentation: bool -> (obj[] -> obj) + unionCase: UnionCaseInfo * ?allowAccessToPrivateRepresentation: bool -> (obj array -> obj) /// A method that constructs objects of the given case /// @@ -933,7 +934,7 @@ module FSharpReflectionExtensions = /// The fields from the given exception. /// /// - static member GetExceptionFields: exn: obj * ?allowAccessToPrivateRepresentation: bool -> obj[] + static member GetExceptionFields: exn: obj * ?allowAccessToPrivateRepresentation: bool -> obj array type FSharpType with @@ -950,7 +951,7 @@ module FSharpReflectionExtensions = static member GetRecordFields: [] recordType: Type * ?allowAccessToPrivateRepresentation: bool -> - PropertyInfo[] + PropertyInfo array /// Gets the cases of a union type. /// @@ -967,7 +968,7 @@ module FSharpReflectionExtensions = static member GetUnionCases: [] unionType: Type * ?allowAccessToPrivateRepresentation: bool -> - UnionCaseInfo[] + UnionCaseInfo array /// Return true if the typ is a representation of an F# record type /// @@ -1010,7 +1011,7 @@ module FSharpReflectionExtensions = static member GetExceptionFields: [] exceptionType: Type * ?allowAccessToPrivateRepresentation: bool -> - PropertyInfo[] + PropertyInfo array /// Returns true if the exceptionType is a representation of an F# exception declaration /// diff --git a/src/FSharp.Core/result.fsi b/src/FSharp.Core/result.fsi index c10226ab6ba..db9a2aa2a42 100644 --- a/src/FSharp.Core/result.fsi +++ b/src/FSharp.Core/result.fsi @@ -266,7 +266,7 @@ module Result = /// /// [] - val inline toArray: result: Result<'T, 'Error> -> 'T[] + val inline toArray: result: Result<'T, 'Error> -> 'T array /// Convert the result to a list of length 0 or 1. /// diff --git a/src/FSharp.Core/resumable.fs b/src/FSharp.Core/resumable.fs index e63e79f92dd..24131ea155f 100644 --- a/src/FSharp.Core/resumable.fs +++ b/src/FSharp.Core/resumable.fs @@ -115,6 +115,7 @@ module StateMachineHelpers = "__stateMachine should always be guarded by __useResumableCode and only used in valid state machine implementations" module ResumableCode = + open System.Runtime.ExceptionServices let inline GetResumptionFunc (sm: byref>) = sm.ResumptionDynamicInfo.ResumptionFunc @@ -294,7 +295,10 @@ module ResumableCode = // reraise at the end of the finally block match savedExn with | None -> true - | Some exn -> raise exn + | Some exn -> + // This should preserve initial location for the failure (file + line, given they're available). + ExceptionDispatchInfo.Capture(exn).Throw() + true else let rf = GetResumptionFunc &sm @@ -384,7 +388,7 @@ module ResumableCode = if __stack_fin then match savedExn with | None -> () - | Some exn -> raise exn + | Some exn -> ExceptionDispatchInfo.Capture(exn).Throw() __stack_fin //-- RESUMABLE CODE END diff --git a/src/FSharp.Core/seq.fs b/src/FSharp.Core/seq.fs index 13770d726ca..bfe050f9f7c 100644 --- a/src/FSharp.Core/seq.fs +++ b/src/FSharp.Core/seq.fs @@ -873,7 +873,7 @@ module Seq = checkNonNull "source" source match source with - | :? ('T[]) as a -> a.Length = 0 + | :? ('T array) as a -> a.Length = 0 | :? ('T list) as a -> a.IsEmpty | :? ICollection<'T> as a -> a.Count = 0 | _ -> @@ -890,7 +890,7 @@ module Seq = checkNonNull "source" source match source with - | :? ('T[]) as a -> a.Length + | :? ('T array) as a -> a.Length | :? ('T list) as a -> a.Length | :? ICollection<'T> as a -> a.Count | _ -> @@ -1014,7 +1014,7 @@ module Seq = checkNonNull "source" source match source with - | :? ('T[]) as res -> (res.Clone() :?> 'T[]) + | :? ('T array) as res -> (res.Clone() :?> 'T array) | :? ('T list) as res -> List.toArray res | :? ICollection<'T> as res -> // Directly create an array and copy ourselves. @@ -1037,7 +1037,7 @@ module Seq = else [||] - let foldArraySubRight (f: OptimizedClosures.FSharpFunc<'T, _, _>) (arr: 'T[]) start fin acc = + let foldArraySubRight (f: OptimizedClosures.FSharpFunc<'T, _, _>) (arr: 'T array) start fin acc = let mutable state = acc for i = fin downto start do @@ -1181,7 +1181,7 @@ module Seq = checkNonNull "source" source source |> toArray |> Array.findIndexBack predicate - // windowed : int -> seq<'T> -> seq<'T[]> + // windowed : int -> seq<'T> -> seq<'T array> [] let windowed windowSize (source: seq<_>) = checkNonNull "source" source diff --git a/src/FSharp.Core/seq.fsi b/src/FSharp.Core/seq.fsi index 9ce69ed5ce4..4bdf2a54d6d 100644 --- a/src/FSharp.Core/seq.fsi +++ b/src/FSharp.Core/seq.fsi @@ -249,7 +249,7 @@ module Seq = /// Throws ArgumentException /// [] - val chunkBySize: chunkSize: int -> source: seq<'T> -> seq<'T[]> + val chunkBySize: chunkSize: int -> source: seq<'T> -> seq<'T array> /// Applies the given function to each element of the sequence and concatenates all the /// results. @@ -530,7 +530,7 @@ module Seq = /// Throws ArgumentException /// [] - val splitInto: count: int -> source: seq<'T> -> seq<'T[]> + val splitInto: count: int -> source: seq<'T> -> seq<'T array> /// Creates an empty sequence. /// @@ -1833,7 +1833,7 @@ module Seq = /// Evaluates to a sequence yielding the same results as seq { 1; 2; 5 }. /// [] - val ofArray: source: 'T[] -> seq<'T> + val ofArray: source: 'T array -> seq<'T> /// Views the given list as a sequence. /// @@ -1955,7 +1955,7 @@ module Seq = /// /// let readonlyView = input |> Seq.readonly /// - /// (readonlyView :?> int[]).[1] <- 4 + /// (readonlyView :?> int array).[1] <- 4 /// /// Throws an InvalidCastException. /// @@ -2474,7 +2474,7 @@ module Seq = /// Evaluates to [| 1; 2; 5 |]. /// [] - val toArray: source: seq<'T> -> 'T[] + val toArray: source: seq<'T> -> 'T array /// Builds a list from the given collection. /// @@ -2792,7 +2792,7 @@ module Seq = /// Evaluates to a sequence of arrays yielding the results seq { [| 1; 2; 3 |]; [| 2; 3; 4 |]; [| 3; 4; 5 |] } /// [] - val windowed: windowSize: int -> source: seq<'T> -> seq<'T[]> + val windowed: windowSize: int -> source: seq<'T> -> seq<'T array> /// Combines the two sequences into a sequence of pairs. The two sequences need not have equal lengths: /// when one sequence is exhausted any remaining elements in the other diff --git a/src/FSharp.Core/seqcore.fs b/src/FSharp.Core/seqcore.fs index b95e96ff95a..68b8c35c48a 100644 --- a/src/FSharp.Core/seqcore.fs +++ b/src/FSharp.Core/seqcore.fs @@ -553,7 +553,7 @@ type ListCollector<'T> = member this.AddMany (values: seq<'T>) = // cook a faster iterator for lists and arrays match values with - | :? ('T[]) as valuesAsArray -> + | :? ('T array) as valuesAsArray -> for v in valuesAsArray do this.Add v | :? ('T list) as valuesAsList -> @@ -631,7 +631,7 @@ type ArrayCollector<'T> = else // cook a faster iterator for lists and arrays match values with - | :? ('T[]) as valuesAsArray -> + | :? ('T array) as valuesAsArray -> for v in valuesAsArray do this.Add v | :? ('T list) as valuesAsList -> diff --git a/src/FSharp.Core/seqcore.fsi b/src/FSharp.Core/seqcore.fsi index 1522522000b..e58e1f6ce68 100644 --- a/src/FSharp.Core/seqcore.fsi +++ b/src/FSharp.Core/seqcore.fsi @@ -205,7 +205,7 @@ type ArrayCollector<'T> = member AddMany: values: seq<'T> -> unit /// Add multiple elements to the collector and return the resulting array - member AddManyAndClose: values: seq<'T> -> 'T[] + member AddManyAndClose: values: seq<'T> -> 'T array /// Return the resulting list - member Close: unit -> 'T[] + member Close: unit -> 'T array diff --git a/src/FSharp.Core/set.fs b/src/FSharp.Core/set.fs index aaed3e37a41..b47bef257c1 100644 --- a/src/FSharp.Core/set.fs +++ b/src/FSharp.Core/set.fs @@ -669,7 +669,7 @@ module internal SetTree = loop t [] - let copyToArray s (arr: _[]) i = + let copyToArray s (arr: _ array) i = let mutable j = i iter diff --git a/src/FSharp.Core/set.fsi b/src/FSharp.Core/set.fsi index f1c2fd291c2..58615cedaa9 100644 --- a/src/FSharp.Core/set.fsi +++ b/src/FSharp.Core/set.fsi @@ -752,7 +752,7 @@ module Set = /// The sample evaluates to the following output: The set is set [(1, 2, 3)] and type is "FSharpSet`1" /// [] - val ofArray: array: 'T[] -> Set<'T> + val ofArray: array: 'T array -> Set<'T> /// Builds an array that contains the elements of the set in order. /// @@ -766,10 +766,10 @@ module Set = /// let array = Set.toArray set /// printfn$ "The set is {set} and type is {array.GetType().Name}" /// - /// The sample evaluates to the following output: The set is [|1; 2; 3|] and type is System.Int32[] + /// The sample evaluates to the following output: The set is [|1; 2; 3|] and type is System.Int32 array /// [] - val toArray: set: Set<'T> -> 'T[] + val toArray: set: Set<'T> -> 'T array /// Returns an ordered view of the collection as an enumerable object. /// diff --git a/src/FSharp.Core/string.fs b/src/FSharp.Core/string.fs index 42f86f75079..d716b793db1 100644 --- a/src/FSharp.Core/string.fs +++ b/src/FSharp.Core/string.fs @@ -26,7 +26,7 @@ module String = [] let concat sep (strings: seq) = - let concatArray sep (strings: string[]) = + let concatArray sep (strings: string array) = match length sep with | 0 -> String.Concat strings // following line should be used when this overload becomes part of .NET Standard (it's only in .NET Core) @@ -34,7 +34,7 @@ module String = | _ -> String.Join(sep, strings, 0, strings.Length) match strings with - | :? (string[]) as arr -> concatArray sep arr + | :? (string array) as arr -> concatArray sep arr | :? (string list) as lst -> lst |> List.toArray |> concatArray sep diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncLock.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncLock.fs new file mode 100644 index 00000000000..ef4b69a3910 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncLock.fs @@ -0,0 +1,26 @@ +module CompilerService.AsyncLock + +open Internal.Utilities.Collections + +open Xunit +open System.Threading.Tasks + + +[] +let ``Async lock works`` () = + task { + use lock = new AsyncLock() + + let mutable x = 0 + + let job () = task { + let y = x + do! Task.Delay(10) + x <- y + 1 + } + + let jobs = [ for _ in 1..100 -> lock.Do job ] + let! _ = Task.WhenAll(jobs) + + Assert.Equal(100, x) + } \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs new file mode 100644 index 00000000000..e442335f940 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -0,0 +1,578 @@ +module CompilerService.AsyncMemoize + +open System +open System.Threading +open Xunit +open Internal.Utilities.Collections +open System.Threading.Tasks +open System.Diagnostics +open System.Collections.Concurrent +open FSharp.Compiler.DiagnosticsLogger +open FSharp.Compiler.Diagnostics +open FSharp.Compiler.BuildGraph + + +let timeout = TimeSpan.FromSeconds 10 + +let waitFor (mre: ManualResetEvent) = + if not <| mre.WaitOne timeout then + failwith "waitFor timed out" + +let waitUntil condition value = + task { + let sw = Stopwatch.StartNew() + while not <| condition value do + if sw.Elapsed > timeout then + failwith "waitUntil timed out" + do! Task.Delay 10 + } + +let rec internal spinFor (duration: TimeSpan) = + node { + let sw = Stopwatch.StartNew() + do! Async.Sleep 10 |> NodeCode.AwaitAsync + let remaining = duration - sw.Elapsed + if remaining > TimeSpan.Zero then + return! spinFor remaining + } + + +type internal EventRecorder<'a, 'b, 'c when 'a : equality and 'b : equality>(memoize: AsyncMemoize<'a,'b,'c>) as self = + + let events = ConcurrentQueue() + + do memoize.OnEvent self.Add + + member _.Add (e, (_label, k, _version)) = events.Enqueue (e, k) + + member _.Received value = events |> Seq.exists (fst >> (=) value) + + member _.CountOf value count = events |> Seq.filter (fst >> (=) value) |> Seq.length |> (=) count + + member _.ShouldBe (expected) = + let expected = expected |> Seq.toArray + let actual = events |> Seq.toArray + Assert.Equal<_ array>(expected, actual) + + +[] +let ``Basics``() = + + let computation key = node { + do! Async.Sleep 1 |> NodeCode.AwaitAsync + return key * 2 + } + + let eventLog = ConcurrentBag() + + let memoize = AsyncMemoize() + memoize.OnEvent(fun (e, (_label, k, _version)) -> eventLog.Add (e, k)) + + let result = + seq { + memoize.Get'(5, computation 5) + memoize.Get'(5, computation 5) + memoize.Get'(2, computation 2) + memoize.Get'(5, computation 5) + memoize.Get'(3, computation 3) + memoize.Get'(2, computation 2) + } + |> NodeCode.Parallel + |> NodeCode.RunImmediateWithoutCancellation + + let expected = [| 10; 10; 4; 10; 6; 4|] + + Assert.Equal(expected, result) + + let groups = eventLog |> Seq.groupBy snd |> Seq.toList + Assert.Equal(3, groups.Length) + for key, events in groups do + Assert.Equal>(Set [ Requested, key; Started, key; Finished, key ], Set events) + +[] +let ``We can cancel a job`` () = + task { + + let jobStarted = new ManualResetEvent(false) + + let computation action = node { + action() |> ignore + do! spinFor timeout + failwith "Should be canceled before it gets here" + } + + let memoize = AsyncMemoize<_, int, _>() + let events = EventRecorder(memoize) + + use cts1 = new CancellationTokenSource() + use cts2 = new CancellationTokenSource() + use cts3 = new CancellationTokenSource() + + let key = 1 + + let _task1 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation jobStarted.Set), ct = cts1.Token) + + waitFor jobStarted + jobStarted.Reset() |> ignore + + let _task2 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation ignore), ct = cts2.Token) + let _task3 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation ignore), ct = cts3.Token) + + do! waitUntil (events.CountOf Requested) 3 + + cts1.Cancel() + cts2.Cancel() + + waitFor jobStarted + + cts3.Cancel() + + do! waitUntil events.Received Canceled + + events.ShouldBe [ + Requested, key + Started, key + Requested, key + Requested, key + Restarted, key + Canceled, key + ] + } + +[] +let ``Job is restarted if first requestor cancels`` () = + task { + let jobStarted = new ManualResetEvent(false) + + let jobCanComplete = new ManualResetEvent(false) + + let computation key = node { + jobStarted.Set() |> ignore + waitFor jobCanComplete + return key * 2 + } + + let memoize = AsyncMemoize<_, int, _>() + let events = EventRecorder(memoize) + + + use cts1 = new CancellationTokenSource() + use cts2 = new CancellationTokenSource() + use cts3 = new CancellationTokenSource() + + let key = 1 + + let _task1 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts1.Token) + + waitFor jobStarted + jobStarted.Reset() |> ignore + + let _task2 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts2.Token) + let _task3 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts3.Token) + + do! waitUntil (events.CountOf Requested) 3 + + cts1.Cancel() + + waitFor jobStarted + + jobCanComplete.Set() |> ignore + + let! result = _task2 + Assert.Equal(2, result) + + events.ShouldBe [ + Requested, key + Started, key + Requested, key + Requested, key + Restarted, key + Finished, key ] + } + +[] +let ``Job is restarted if first requestor cancels but keeps running if second requestor cancels`` () = + task { + let jobStarted = new ManualResetEvent(false) + + let jobCanComplete = new ManualResetEvent(false) + + let computation key = node { + jobStarted.Set() |> ignore + waitFor jobCanComplete + return key * 2 + } + + let memoize = AsyncMemoize<_, int, _>() + let events = EventRecorder(memoize) + + + use cts1 = new CancellationTokenSource() + use cts2 = new CancellationTokenSource() + use cts3 = new CancellationTokenSource() + + let key = 1 + + let _task1 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts1.Token) + + waitFor jobStarted + jobStarted.Reset() |> ignore + + let _task2 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts2.Token) + let _task3 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts3.Token) + + do! waitUntil (events.CountOf Requested) 3 + + cts1.Cancel() + + waitFor jobStarted + + cts2.Cancel() + + jobCanComplete.Set() |> ignore + + let! result = _task3 + Assert.Equal(2, result) + + events.ShouldBe [ + Requested, key + Started, key + Requested, key + Requested, key + Restarted, key + Finished, key ] + } + + +type ExpectedException() = + inherit Exception() + +[] +let ``Stress test`` () = + + let seed = System.Random().Next() + + let rng = System.Random seed + let threads = 30 + let iterations = 30 + let maxDuration = 100 + let minTimeout = 0 + let maxTimeout = 500 + let exceptionProbability = 0.01 + let gcProbability = 0.1 + let stepMs = 10 + let keyCount = rng.Next(5, 200) + let keys = [| 1 .. keyCount |] + + let testTimeoutMs = threads * iterations * maxDuration * 2 + + let intenseComputation durationMs result = + async { + if rng.NextDouble() < exceptionProbability then + raise (ExpectedException()) + let s = Stopwatch.StartNew() + let mutable number = 0 + while (int s.ElapsedMilliseconds) < durationMs do + number <- number + 1 % 12345 + return [result] + } |> NodeCode.AwaitAsync + + let rec sleepyComputation durationMs result = + node { + if rng.NextDouble() < (exceptionProbability / (float durationMs / float stepMs)) then + raise (ExpectedException()) + if durationMs > 0 then + do! Async.Sleep (min stepMs durationMs) |> NodeCode.AwaitAsync + return! sleepyComputation (durationMs - stepMs) result + else + return [result] + } + + let rec mixedComputation durationMs result = + node { + if durationMs > 0 then + if rng.NextDouble() < 0.5 then + let! _ = intenseComputation (min stepMs durationMs) () + () + else + let! _ = sleepyComputation (min stepMs durationMs) () + () + return! mixedComputation (durationMs - stepMs) result + else + return [result] + } + + let computations = [| + intenseComputation + sleepyComputation + mixedComputation + |] + + let cache = AsyncMemoize(keepStrongly=5, keepWeakly=10) + + let mutable started = 0 + let mutable canceled = 0 + let mutable timeout = 0 + let mutable failed = 0 + let mutable completed = 0 + + let test = + seq { + for _ in 1..threads do + let rec loop iteration = + task { + if gcProbability > rng.NextDouble() then + GC.Collect(2, GCCollectionMode.Forced, false) + + let computation = computations[rng.Next computations.Length] + let durationMs = rng.Next maxDuration + let timeoutMs = rng.Next(minTimeout, maxTimeout) + let key = keys[rng.Next keys.Length] + let result = key * 2 + let job = cache.Get'(key, computation durationMs result) + let cts = new CancellationTokenSource() + let runningJob = NodeCode.StartAsTask_ForTesting(job, ct = cts.Token) + cts.CancelAfter timeoutMs + Interlocked.Increment &started |> ignore + try + let! actual = runningJob + Assert.Equal(result, actual.Head) + Interlocked.Increment &completed |> ignore + with + | :? TaskCanceledException as _e -> + Interlocked.Increment &canceled |> ignore + | :? OperationCanceledException as _e -> + Interlocked.Increment &canceled |> ignore + | :? TimeoutException -> Interlocked.Increment &timeout |> ignore + | :? ExpectedException -> Interlocked.Increment &failed |> ignore + | :? AggregateException as ex when + ex.Flatten().InnerExceptions |> Seq.exists (fun e -> e :? ExpectedException) -> + Interlocked.Increment &failed |> ignore + | e -> + failwith $"Seed {seed} failed on iteration {iteration}: %A{e}" + if iteration < iterations then + return! loop (iteration + 1) + return () + } + loop 1 + } + |> Task.WhenAll + + if not (test.Wait testTimeoutMs) then failwith "Test timed out - most likely deadlocked" + + Assert.Equal (threads * iterations, started) + // Assert.Equal((0,0,0,0,0),(started, completed, canceled, failed, timeout)) + Assert.Equal (started, completed + canceled + failed + timeout) + + Assert.True ((float completed) > ((float started) * 0.1), "Less than 10 % completed jobs") + + +[] +[] +[] +let ``Cancel running jobs with the same key`` cancelDuplicate expectFinished = + task { + let cache = AsyncMemoize(cancelDuplicateRunningJobs=cancelDuplicate) + + let mutable started = 0 + let mutable finished = 0 + + let job1started = new ManualResetEvent(false) + let job1finished = new ManualResetEvent(false) + + let jobCanContinue = new ManualResetEvent(false) + + let job2started = new ManualResetEvent(false) + let job2finished = new ManualResetEvent(false) + + let work onStart onFinish = node { + Interlocked.Increment &started |> ignore + onStart() |> ignore + waitFor jobCanContinue + do! spinFor (TimeSpan.FromMilliseconds 100) + Interlocked.Increment &finished |> ignore + onFinish() |> ignore + } + + let key1 = + { new ICacheKey<_, _> with + member _.GetKey() = 1 + member _.GetVersion() = 1 + member _.GetLabel() = "key1" } + + cache.Get(key1, work job1started.Set job1finished.Set) |> Async.AwaitNodeCode |> Async.Start + + waitFor job1started + + let key2 = + { new ICacheKey<_, _> with + member _.GetKey() = key1.GetKey() + member _.GetVersion() = key1.GetVersion() + 1 + member _.GetLabel() = "key2" } + + cache.Get(key2, work job2started.Set job2finished.Set ) |> Async.AwaitNodeCode |> Async.Start + + waitFor job2started + + jobCanContinue.Set() |> ignore + + waitFor job2finished + + if not cancelDuplicate then + waitFor job1finished + + Assert.Equal((2, expectFinished), (started, finished)) + } + + +type DummyException(msg) = + inherit Exception(msg) + +[] +let ``Preserve thread static diagnostics`` () = + + let seed = System.Random().Next() + + let rng = System.Random seed + + let job1Cache = AsyncMemoize() + let job2Cache = AsyncMemoize() + + let job1 (input: string) = node { + let! _ = Async.Sleep (rng.Next(1, 30)) |> NodeCode.AwaitAsync + let ex = DummyException("job1 error") + DiagnosticsThreadStatics.DiagnosticsLogger.ErrorR(ex) + return Ok input + } + + let job2 (input: int) = node { + + DiagnosticsThreadStatics.DiagnosticsLogger.Warning(DummyException("job2 error 1")) + + let! _ = Async.Sleep (rng.Next(1, 30)) |> NodeCode.AwaitAsync + + let key = { new ICacheKey<_, _> with + member _.GetKey() = "job1" + member _.GetVersion() = input + member _.GetLabel() = "job1" } + + let! result = job1Cache.Get(key, job1 "${input}" ) + + DiagnosticsThreadStatics.DiagnosticsLogger.Warning(DummyException("job2 error 2")) + + return input, result + + } + + let tasks = seq { + for i in 1 .. 100 do + + task { + let diagnosticsLogger = + CompilationDiagnosticLogger($"Testing task {i}", FSharpDiagnosticOptions.Default) + + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Optimize) + + DiagnosticsThreadStatics.DiagnosticsLogger.Warning(DummyException("task error")) + + + let key = { new ICacheKey<_, _> with + member _.GetKey() = "job2" + member _.GetVersion() = rng.Next(1, 10) + member _.GetLabel() = "job2" } + + let! result = job2Cache.Get(key, job2 (i % 10)) |> Async.AwaitNodeCode + + let diagnostics = diagnosticsLogger.GetDiagnostics() + + //Assert.Equal(3, diagnostics.Length) + + return result, diagnostics + } + } + + let results = (Task.WhenAll tasks).Result + + let _diagnosticCounts = results |> Seq.map snd |> Seq.map Array.length |> Seq.groupBy id |> Seq.map (fun (k, v) -> k, v |> Seq.length) |> Seq.sortBy fst |> Seq.toList + + //Assert.Equal<(int * int) list>([4, 100], diagnosticCounts) + + let diagnosticMessages = results |> Seq.map snd |> Seq.map (Array.map (fun (d, _) -> d.Exception.Message) >> Array.toList) |> Set + + Assert.Equal>(Set [["task error"; "job2 error 1"; "job1 error"; "job2 error 2"; ]], diagnosticMessages) + + +[] +let ``Preserve thread static diagnostics already completed job`` () = + + let cache = AsyncMemoize() + + let key = { new ICacheKey<_, _> with + member _.GetKey() = "job1" + member _.GetVersion() = 1 + member _.GetLabel() = "job1" } + + let job (input: string) = node { + let ex = DummyException($"job {input} error") + DiagnosticsThreadStatics.DiagnosticsLogger.ErrorR(ex) + return Ok input + } + + async { + + let diagnosticsLogger = CompilationDiagnosticLogger($"Testing", FSharpDiagnosticOptions.Default) + + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Optimize) + + let! _ = cache.Get(key, job "1" ) |> Async.AwaitNodeCode + let! _ = cache.Get(key, job "2" ) |> Async.AwaitNodeCode + + let diagnosticMessages = diagnosticsLogger.GetDiagnostics() |> Array.map (fun (d, _) -> d.Exception.Message) |> Array.toList + + Assert.Equal>(["job 1 error"; "job 1 error"], diagnosticMessages) + + } + |> Async.StartAsTask + + +[] +let ``We get diagnostics from the job that failed`` () = + + let cache = AsyncMemoize() + + let key = { new ICacheKey<_, _> with + member _.GetKey() = "job1" + member _.GetVersion() = 1 + member _.GetLabel() = "job1" } + + let job (input: int) = node { + let ex = DummyException($"job {input} error") + do! Async.Sleep 100 |> NodeCode.AwaitAsync + DiagnosticsThreadStatics.DiagnosticsLogger.Error(ex) + return 5 + } + + let result = + [1; 2] + |> Seq.map (fun i -> + async { + let diagnosticsLogger = CompilationDiagnosticLogger($"Testing", FSharpDiagnosticOptions.Default) + + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Optimize) + try + let! _ = cache.Get(key, job i ) |> Async.AwaitNodeCode + () + with _ -> + () + let diagnosticMessages = diagnosticsLogger.GetDiagnostics() |> Array.map (fun (d, _) -> d.Exception.Message) |> Array.toList + + return diagnosticMessages + }) + |> Async.Parallel + |> Async.StartAsTask + |> (fun t -> t.Result) + |> Array.toList + + Assert.True( + result = [["job 1 error"]; ["job 1 error"]] || + result = [["job 2 error"]; ["job 2 error"]] ) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs new file mode 100644 index 00000000000..a477f7e6f7c --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs @@ -0,0 +1,197 @@ +module CompilerService.LruCache +open Internal.Utilities.Collections + +open Xunit +open System + +[] +let ``Adding an item to the cache should make it retrievable``() = + let cache = new LruCache(keepStrongly = 2) + cache.Set(1, "one") + let result = cache.TryGet(1) + Assert.Equal("one", result.Value) + +[] +let ``Adding an item to the cache should evict the least recently used item if the cache is full``() = + let cache = new LruCache(keepStrongly = 2, keepWeakly = 0) + cache.Set(1, "one") + cache.Set(2, "two") + cache.Set(3, "three") + let result = cache.TryGet(1) + Assert.Null(result) + +[] +let ``Adding an item to the cache should not evict a required item``() = + let cache = new LruCache(keepStrongly = 2, requiredToKeep = (fun v -> v = "one")) + cache.Set(1, "one") + cache.Set(2, "two") + cache.Set(3, "three") + let result = cache.TryGet(1) + Assert.Equal("one", result.Value) + +[] +let ``Adding an item to the cache should not evict a strongly kept item``() = + let cache = new LruCache(keepStrongly = 2, keepWeakly = 0) + cache.Set(1, "one") + cache.Set(2, "two") + cache.Set(1, "one") + cache.Set(3, "three") + let result = cache.TryGet(1) + Assert.Equal("one", result.Value) + +[] +let ``Adding an item to the cache should not evict a strongly kept item, even if it is the least recently used``() = + let cache = new LruCache(keepStrongly = 2, keepWeakly = 0, requiredToKeep = (fun v -> v = "one")) + cache.Set(1, "one") + cache.Set(2, "two") + cache.Set(3, "three") + let result = cache.TryGet(1) + Assert.Equal("one", result.Value) + +[] +let ``Adding an item to the cache should not evict a weakly kept item if its reference is still valid``() = + let cache = new LruCache(keepStrongly = 2, keepWeakly = 1) + let value = "one" + cache.Set(1, value) + cache.Set(2, "two") + GC.Collect(2, GCCollectionMode.Forced, true) + let result = cache.TryGet(1) + Assert.Equal(value, result.Value) + + +// Doing this directly in the test prevents GC for some reason +let private addObjToCache (cache: LruCache<_, int,_>) key = + let o = obj () + cache.Set(key, o) + +[] +let ``Adding an item to the cache should evict a weakly kept item if its reference is no longer valid``() = + let cache = new LruCache<_, int, _>(keepStrongly = 2, keepWeakly = 1) + addObjToCache cache 1 + addObjToCache cache 2 + addObjToCache cache 3 + GC.Collect(2, GCCollectionMode.Forced, true) + + let result = cache.TryGet(1) + Assert.True(result.IsNone) + + +[] +let ``When a new version is added other versions get weakened`` () = + let eventLog = ResizeArray() + + let cache = new LruCache<_, int, _>(keepStrongly = 2, keepWeakly = 2, event = (fun e v -> eventLog.Add(e, v))) + + cache.Set(1, 1, "one1") + cache.Set(1, 2, "one2") + cache.Set(1, 3, "one3") + cache.Set(1, 4, "one4") + + let expected = [ + CacheEvent.Weakened, ("[no label]", 1, 1) + CacheEvent.Weakened, ("[no label]", 1, 2) + CacheEvent.Weakened, ("[no label]", 1, 3) + CacheEvent.Evicted, ("[no label]", 1, 1) + ] + + Assert.Equal>(expected, eventLog |> Seq.toList) + +[] +let ``When a new version is added other versions don't get weakened when they're required to keep`` () = + let eventLog = ResizeArray() + + let cache = new LruCache<_, int, _>(keepStrongly = 2, keepWeakly = 2, requiredToKeep = ((=) "one1"), event = (fun e v -> eventLog.Add(e, v))) + + cache.Set(1, 1, "one1") + cache.Set(1, 2, "one2") + cache.Set(1, 3, "one3") + cache.Set(1, 4, "one4") + + let expected = [ + CacheEvent.Weakened, ("[no label]", 1, 2) + CacheEvent.Weakened, ("[no label]", 1, 3) + ] + + Assert.Equal>(expected, eventLog |> Seq.toList) + +[] +let ``Looking up a weakened item will strengthen it`` () = + let eventLog = ResizeArray() + + let cache = new LruCache<_, int, _>(keepStrongly = 2, keepWeakly = 2, event = (fun e v -> eventLog.Add(e, v))) + + cache.Set(1, 1, "one1") + cache.Set(1, 2, "one2") + cache.Set(1, 3, "one3") + cache.Set(1, 4, "one4") + + let result = cache.TryGet(1, 2) + Assert.Equal("one2", result.Value) + + let expected = [ + CacheEvent.Weakened, ("[no label]", 1, 1) + CacheEvent.Weakened, ("[no label]", 1, 2) + CacheEvent.Weakened, ("[no label]", 1, 3) + CacheEvent.Evicted, ("[no label]", 1, 1) + CacheEvent.Strengthened, ("[no label]", 1, 2) + ] + + Assert.Equal>(expected, eventLog |> Seq.toList) + + +[] +let ``New version doesn't push other keys out of strong list``() = + + let eventLog = ResizeArray() + + let cache = new LruCache<_, int, _>(keepStrongly = 2, keepWeakly = 2, event = (fun e v -> eventLog.Add(e, v))) + + cache.Set(1, 1, "one1") + cache.Set(1, 2, "one2") + cache.Set(1, 3, "one3") + cache.Set(1, 4, "one4") + cache.Set(2, 1, "two1") + cache.Set(2, 2, "two2") + + let expected = [ + CacheEvent.Weakened, ("[no label]", 1, 1) + CacheEvent.Weakened, ("[no label]", 1, 2) + CacheEvent.Weakened, ("[no label]", 1, 3) + CacheEvent.Evicted, ("[no label]", 1, 1) + CacheEvent.Weakened, ("[no label]", 2, 1) + CacheEvent.Evicted, ("[no label]", 1, 2) + ] + + Assert.Equal>(expected, eventLog |> Seq.toList) + +[] +let ``We can clear specific keys based on a predicate``() = + + let eventLog = ResizeArray() + + let cache = new LruCache<_, int, _>(keepStrongly = 2, keepWeakly = 2, event = (fun e v -> eventLog.Add(e, v))) + + cache.Set(1, 1, "one1") + cache.Set(1, 2, "one2") + cache.Set(1, 3, "one3") + cache.Set(1, 4, "one4") + cache.Set(2, 1, "two1") + cache.Set(2, 2, "two2") + + cache.Clear((=) 1) + + let result = cache.TryGet(1, 2) + Assert.True(result.IsNone) + + let expected = [ + CacheEvent.Weakened, ("[no label]", 1, 1) + CacheEvent.Weakened, ("[no label]", 1, 2) + CacheEvent.Weakened, ("[no label]", 1, 3) + CacheEvent.Evicted, ("[no label]", 1, 1) + CacheEvent.Weakened, ("[no label]", 2, 1) + CacheEvent.Evicted, ("[no label]", 1, 2) + CacheEvent.Cleared, ("[no label]", 1, 3) + CacheEvent.Cleared, ("[no label]", 1, 4) + ] + + Assert.Equal>(expected, eventLog |> Seq.toList) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/StructTypes/StructActivePatterns.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/StructTypes/StructActivePatterns.fs index 2d9a31d2c7b..1b121b27766 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/StructTypes/StructActivePatterns.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/StructTypes/StructActivePatterns.fs @@ -46,13 +46,11 @@ let rec (|IsOne|_|) someNumber = | 1 -> ValueSome 1 | _ -> ValueNone """ + |> withLangVersion80 |> withOptions ["--warnaserror+"] |> typecheck |> shouldFail - |> withSingleDiagnostic (Error 1,Line 2, Col 9 , Line 2, Col 31, """This expression was expected to have type - ''a option' -but here has type - 'int voption' """) + |> withSingleDiagnostic (Error 3350, Line 2, Col 9, Line 2, Col 31, "Feature 'Boolean-returning and return-type-directed partial active patterns' is not available in F# 8.0. Please use language version 'PREVIEW' or greater.") [] let ``Rec struct active pattern is possible`` () = diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs index fea4dfddbb7..eec3ce03dcd 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs @@ -143,8 +143,10 @@ if true then 1 else "a" Assert.Equal("int", typeMismatch.ExpectedType.Format(displayContext)) Assert.Equal("string", typeMismatch.ActualType.Format(displayContext))) -[] -let ``ArgumentsInSigAndImplMismatchExtendedData 01`` () = +[] +[] +[] +let ``ArgumentsInSigAndImplMismatchExtendedData 01`` useTransparentCompiler = let encodeFsi = Fsi """ module Test @@ -157,7 +159,7 @@ let f (y: int) = () """ encodeFsi |> withAdditionalSourceFile encodeFs - |> typecheckProject true + |> typecheckProject true useTransparentCompiler |> checkDiagnostic (3218, "The argument names in the signature 'x' and implementation 'y' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling.") (fun (argsMismatch: ArgumentsInSigAndImplMismatchExtendedData) -> @@ -166,8 +168,10 @@ let f (y: int) = () Assert.True(argsMismatch.SignatureRange.FileName.EndsWith("fsi")) Assert.True(argsMismatch.ImplementationRange.FileName.EndsWith("fs"))) -[] -let ``FieldNotContainedDiagnosticExtendedData 01`` () = +[] +[] +[] +let ``FieldNotContainedDiagnosticExtendedData 01`` useTransparentCompiler = let encodeFsi = Fsi """ namespace rec Foo @@ -182,7 +186,7 @@ type A = """ encodeFsi |> withAdditionalSourceFile encodeFs - |> typecheckProject true + |> typecheckProject true useTransparentCompiler |> checkDiagnostic (193, "The module contains the field\n myStatic: int \nbut its signature specifies\n myStatic: int \nthe accessibility specified in the signature is more than that specified in the implementation") (fun (fieldsData: FieldNotContainedDiagnosticExtendedData) -> diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TailCallAttribute.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TailCallAttribute.fs index 9bad5b4d0af..56572879715 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TailCallAttribute.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TailCallAttribute.fs @@ -1512,3 +1512,29 @@ module Microsoft.FSharp.Core Message = "The member or function 'f' has the 'TailCallAttribute' attribute, but is not being used in a tail recursive way." } ] + + [] + let ``Warn for recursive call in list comprehension`` () = + """ +namespace N + + module M = + + [] + let rec reverse (input: list<'t>) = + match input with + | head :: tail -> [ yield! reverse tail; head ] + | [] -> [] + """ + |> FSharp + |> compile + |> shouldFail + |> withResults [ + { Error = Warning 3569 + Range = { StartLine = 9 + StartColumn = 40 + EndLine = 9 + EndColumn = 52 } + Message = + "The member or function 'reverse' has the 'TailCallAttribute' attribute, but is not being used in a tail recursive way." } + ] diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 904fbbf731c..ec4fc441f29 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -19,6 +19,7 @@ $(DefineConstants);DEBUG true + true @@ -31,75 +32,52 @@ FsUnit.fs - - - - - + + + + + - - + + - + - + - + - - + + - - - - - - + + + + + + - + - + - - + + - + @@ -242,9 +220,10 @@ - - - + + + + @@ -291,6 +270,9 @@ + + + @@ -319,16 +301,17 @@ + + - - - - %(RelativeDir)TestSource\%(Filename)%(Extension) + %(RelativeDir)TestSource\%(Filename)%(Extension) + + %(RelativeDir)\BaseLine\%(Filename)%(Extension) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs index ce9846a0ee5..735e7828b89 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs @@ -100,13 +100,14 @@ let ``Changes in a referenced project`` () = checkFile "Last" expectSignatureChanged } -[] -let ``Language service works if the same file is listed twice`` () = +[] +// TODO: This will probably require some special care in TransparentCompiler... +let ``Language service works if the same file is listed twice`` () = let file = sourceFile "First" [] - let project = SyntheticProject.Create(file) + let project = SyntheticProject.Create(file, file) project.Workflow { - checkFile "First" expectOk - addFileAbove "First" file + // checkFile "First" expectOk + // addFileAbove "First" file checkFile "First" (expectSingleWarningAndNoErrors "Please verify that it is included only once in the project file.") } @@ -150,14 +151,14 @@ let GetAllUsesOfAllSymbols() = .Build() use _ = Activity.start "GetAllUsesOfAllSymbols" [ ] - - let result = - async { + + let result = + async { let project = makeTestProject() let checker = ProjectWorkflowBuilder(project, useGetSource=true, useChangeNotifications = true).Checker - do! saveProject project false checker + do! saveProject project false checker let options = project.GetProjectOptions checker - let! checkProjectResults = checker.ParseAndCheckProject(options) + let! checkProjectResults = checker.ParseAndCheckProject(options) return checkProjectResults.GetAllUsesOfAllSymbols() } |> Async.RunSynchronously @@ -165,4 +166,4 @@ let GetAllUsesOfAllSymbols() = traceProvider.ForceFlush() |> ignore traceProvider.Dispose() - Assert.Equal(79, result.Length) + if result.Length <> 79 then failwith $"Expected 79 symbolUses, got {result.Length}:\n%A{result}" diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs index c8cff9112c2..b12edf8cb6d 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs @@ -5,6 +5,8 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Test.ProjectGeneration open FSharp.Test.ProjectGeneration.Helpers +#nowarn "57" + type Occurence = Definition | InType | Use let deriveOccurence (su:FSharpSymbolUse) = @@ -344,7 +346,7 @@ and mytype = MyType let symbolUse = getSymbolUse fileName source "MyType" options checker |> Async.RunSynchronously - checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol, fastCheck = true) + checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol) |> Async.RunSynchronously |> expectToFind [ fileName, 2, 5, 11 @@ -430,7 +432,7 @@ match 2 with let symbolUse = getSymbolUse fileName source "Even" options checker |> Async.RunSynchronously - checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol, fastCheck = true) + checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol) |> Async.RunSynchronously |> expectToFind [ fileName, 2, 6, 10 @@ -463,7 +465,7 @@ module Two = let symbolUse = getSymbolUse fileName source "Even" options checker |> Async.RunSynchronously - checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol, fastCheck = true) + checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol) |> Async.RunSynchronously |> expectToFind [ fileName, 4, 10, 14 @@ -619,7 +621,7 @@ let y = MyType.Two let symbolUse = getSymbolUse fileName source "MyType" options checker |> Async.RunSynchronously - checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol, fastCheck = true) + checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol) |> Async.RunSynchronously |> expectToFind [ fileName, 4, 5, 11 @@ -648,7 +650,7 @@ let y = MyType.Three let symbolUse = getSymbolUse fileName source "MyType" options checker |> Async.RunSynchronously - checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol, fastCheck = true) + checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol) |> Async.RunSynchronously |> expectToFind [ fileName, 4, 7, 13 diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs new file mode 100644 index 00000000000..90e28cc63ac --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs @@ -0,0 +1,104 @@ +module FSharpChecker.ProjectSnapshot + +open Xunit +open System +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot + + +// TODO: restore tests + +//[] +//let WithoutImplFilesThatHaveSignatures () = + +// let snapshot = FSharpProjectSnapshot.Create( +// projectFileName = "Dummy.fsproj", +// projectId = None, +// sourceFiles = [ +// { FileName = "A.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "C.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// ], +// referencesOnDisk = [], +// otherOptions = [], +// referencedProjects = [], +// isIncompleteTypeCheckEnvironment = true, +// useScriptResolutionRules = false, +// loadTime = DateTime(1234, 5, 6), +// unresolvedReferences = None, +// originalLoadReferences = [], +// stamp = None +// ) + +// let result = snapshot.WithoutImplFilesThatHaveSignatures + +// let expected = [| "A.fsi"; "B.fs"; "C.fsi" |] + +// Assert.Equal(expected, result.SourceFileNames |> List.toArray) + +// Assert.Equal(result.FullVersion, snapshot.SignatureVersion) + +//[] +//let WithoutImplFilesThatHaveSignaturesExceptLastOne () = + +// let snapshot = FSharpProjectSnapshot.Create( +// projectFileName = "Dummy.fsproj", +// projectId = None, +// sourceFiles = [ +// { FileName = "A.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "C.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// ], +// referencesOnDisk = [], +// otherOptions = [], +// referencedProjects = [], +// isIncompleteTypeCheckEnvironment = true, +// useScriptResolutionRules = false, +// loadTime = DateTime(1234, 5, 6), +// unresolvedReferences = None, +// originalLoadReferences = [], +// stamp = None +// ) + +// let result = snapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne + +// let expected = [| "A.fsi"; "B.fs"; "C.fsi"; "C.fs" |] + +// Assert.Equal(expected, result.SourceFileNames |> List.toArray) + +// Assert.Equal(result.FullVersion, snapshot.LastFileVersion) + + +//[] +//let WithoutImplFilesThatHaveSignaturesExceptLastOne_2 () = + +// let snapshot = FSharpProjectSnapshot.Create( +// projectFileName = "Dummy.fsproj", +// projectId = None, +// sourceFiles = [ +// { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// ], +// referencesOnDisk = [], +// otherOptions = [], +// referencedProjects = [], +// isIncompleteTypeCheckEnvironment = true, +// useScriptResolutionRules = false, +// loadTime = DateTime(1234, 5, 6), +// unresolvedReferences = None, +// originalLoadReferences = [], +// stamp = None +// ) + +// let result = snapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne + +// let expected = [| "A.fs"; "B.fs"; "C.fs" |] + +// Assert.Equal(expected, result.SourceFileNames |> List.toArray) + +// Assert.Equal(result.FullVersion, snapshot.LastFileVersion) + diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs new file mode 100644 index 00000000000..5ff288185c4 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -0,0 +1,844 @@ +module FSharpChecker.TransparentCompiler + +open System.Collections.Concurrent +open System.Diagnostics +open FSharp.Compiler.CodeAnalysis +open Internal.Utilities.Collections +open FSharp.Compiler.CodeAnalysis.TransparentCompiler +open Internal.Utilities.Library.Extras +open FSharp.Compiler.GraphChecking.GraphProcessing +open FSharp.Compiler.Diagnostics + +open Xunit + +open FSharp.Test.ProjectGeneration +open FSharp.Test.ProjectGeneration.Helpers +open System.IO +open Microsoft.CodeAnalysis +open System +open System.Threading.Tasks +open System.Threading +open TypeChecks + +open OpenTelemetry +open OpenTelemetry.Resources +open OpenTelemetry.Trace + + +#nowarn "57" + +[] +let ``Use Transparent Compiler`` () = + + let size = 20 + + let project = + { SyntheticProject.Create() with + SourceFiles = [ + sourceFile $"File%03d{0}" [] + for i in 1..size do + sourceFile $"File%03d{i}" [$"File%03d{i-1}"] + ] + } + + let first = "File001" + let middle = $"File%03d{size / 2}" + let last = $"File%03d{size}" + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + updateFile first updatePublicSurface + checkFile first expectSignatureChanged + checkFile last expectSignatureChanged + updateFile middle updatePublicSurface + checkFile last expectSignatureChanged + addFileAbove middle (sourceFile "addedFile" [first]) + updateFile middle (addDependency "addedFile") + checkFile middle expectSignatureChanged + checkFile last expectSignatureChanged + } + +[] +let ``Parallel processing`` () = + + let project = SyntheticProject.Create( + sourceFile "A" [], + sourceFile "B" ["A"], + sourceFile "C" ["A"], + sourceFile "D" ["A"], + sourceFile "E" ["B"; "C"; "D"]) + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + checkFile "E" expectOk + updateFile "A" updatePublicSurface + saveFile "A" + + checkFile "E" expectSignatureChanged + } + +[] +let ``Parallel processing with signatures`` () = + + let project = SyntheticProject.Create( + sourceFile "A" [] |> addSignatureFile, + sourceFile "B" ["A"] |> addSignatureFile, + sourceFile "C" ["A"] |> addSignatureFile, + sourceFile "D" ["A"] |> addSignatureFile, + sourceFile "E" ["B"; "C"; "D"] |> addSignatureFile) + + //let cacheEvents = ConcurrentBag<_>() + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + //withChecker (fun checker -> checker.CacheEvent.Add cacheEvents.Add) + checkFile "E" expectOk + updateFile "A" updatePublicSurface + checkFile "E" expectNoChanges + regenerateSignature "A" + regenerateSignature "B" + regenerateSignature "C" + regenerateSignature "D" + regenerateSignature "E" + checkFile "E" expectSignatureChanged + } + +let makeTestProject () = + SyntheticProject.Create( + sourceFile "First" [], + sourceFile "Second" ["First"], + sourceFile "Third" ["First"], + { sourceFile "Last" ["Second"; "Third"] with EntryPoint = true }) + +let testWorkflow () = + ProjectWorkflowBuilder(makeTestProject(), useTransparentCompiler = true) + +[] +let ``Edit file, check it, then check dependent file`` () = + testWorkflow() { + updateFile "First" breakDependentFiles + checkFile "First" expectSignatureChanged + checkFile "Second" expectErrors + } + +[] +let ``Edit file, don't check it, check dependent file`` () = + testWorkflow() { + updateFile "First" breakDependentFiles + checkFile "Second" expectErrors + } + +[] +let ``Check transitive dependency`` () = + testWorkflow() { + updateFile "First" breakDependentFiles + checkFile "Last" expectSignatureChanged + } + +[] +let ``Change multiple files at once`` () = + testWorkflow() { + updateFile "First" (setPublicVersion 2) + updateFile "Second" (setPublicVersion 2) + updateFile "Third" (setPublicVersion 2) + checkFile "Last" (expectSignatureContains "val f: x: 'a -> (ModuleFirst.TFirstV_2<'a> * ModuleSecond.TSecondV_2<'a>) * (ModuleFirst.TFirstV_2<'a> * ModuleThird.TThirdV_2<'a>) * TLastV_1<'a>") + } + +[] +let ``Files depend on signature file if present`` () = + let project = makeTestProject() |> updateFile "First" addSignatureFile + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + updateFile "First" breakDependentFiles + saveFile "First" + checkFile "Second" expectNoChanges + } + +[] +let ``Project with signatures`` () = + + let project = SyntheticProject.Create( + { sourceFile "First" [] with + Source = "let f (x: int) = x" + SignatureFile = AutoGenerated }, + { sourceFile "Second" ["First"] with + Source = "let a x = ModuleFirst.f x" + SignatureFile = AutoGenerated }) + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + checkFile "Second" expectOk + } + +[] +let ``Signature update`` () = + + let project = SyntheticProject.Create( + { sourceFile "First" [] with + Source = "let f (x: int) = x" + SignatureFile = Custom "val f: x: int -> int" }, + { sourceFile "Second" ["First"] with + Source = "let a x = ModuleFirst.f x" }) + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + checkFile "Second" expectOk + updateFile "First" (fun f -> { f with SignatureFile = Custom "val f: x: string -> string" }) + checkFile "Second" expectSignatureChanged + } + +[] +let ``Adding a file`` () = + testWorkflow() { + addFileAbove "Second" (sourceFile "New" []) + updateFile "Second" (addDependency "New") + checkFile "Last" (expectSignatureContains "val f: x: 'a -> (ModuleFirst.TFirstV_1<'a> * ModuleNew.TNewV_1<'a> * ModuleSecond.TSecondV_1<'a>) * (ModuleFirst.TFirstV_1<'a> * ModuleThird.TThirdV_1<'a>) * TLastV_1<'a>") + } + +[] +let ``Removing a file`` () = + testWorkflow() { + removeFile "Second" + checkFile "Last" expectErrors + } + +[] +let ``Changes in a referenced project`` () = + let library = SyntheticProject.Create("library", sourceFile "Library" []) + + let project = + { makeTestProject() with DependsOn = [library] } + |> updateFile "First" (addDependency "Library") + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + + updateFile "First" updatePublicSurface + checkFile "Last" expectOk + + updateFile "Library" updatePublicSurface + saveFile "Library" + checkFile "Last" expectSignatureChanged + + } + +[] +let ``File is not checked twice`` () = + + let cacheEvents = ConcurrentQueue() + + testWorkflow() { + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.Caches.TcIntermediate.OnEvent cacheEvents.Enqueue + }) + updateFile "First" updatePublicSurface + checkFile "Third" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheEvents + |> Seq.groupBy (fun (_e, (_l, (f, _p), _)) -> f |> Path.GetFileName) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Map + + Assert.Equal([Weakened; Requested; Started; Finished], intermediateTypeChecks["FileFirst.fs"]) + Assert.Equal([Weakened; Requested; Started; Finished], intermediateTypeChecks["FileThird.fs"]) + +[] +let ``If a file is checked as a dependency it's not re-checked later`` () = + let cacheEvents = ConcurrentQueue() + + testWorkflow() { + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.Caches.TcIntermediate.OnEvent cacheEvents.Enqueue + }) + updateFile "First" updatePublicSurface + checkFile "Last" expectOk + checkFile "Third" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheEvents + |> Seq.groupBy (fun (_e, (_l, (f, _p), _)) -> f |> Path.GetFileName) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Map + + Assert.Equal([Weakened; Requested; Started; Finished; Requested], intermediateTypeChecks["FileThird.fs"]) + + +// [] TODO: differentiate complete and minimal checking requests +let ``We don't check files that are not depended on`` () = + let project = SyntheticProject.Create( + sourceFile "First" [], + sourceFile "Second" ["First"], + sourceFile "Third" ["First"], + sourceFile "Last" ["Third"]) + + let cacheEvents = ConcurrentQueue() + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.Caches.TcIntermediate.OnEvent cacheEvents.Enqueue + }) + updateFile "First" updatePublicSurface + checkFile "Last" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheEvents + |> Seq.groupBy (fun (_e, (_l, (f, _p), _)) -> Path.GetFileName f) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Map + + Assert.Equal([Started; Finished], intermediateTypeChecks["FileFirst.fs"]) + Assert.Equal([Started; Finished], intermediateTypeChecks["FileThird.fs"]) + Assert.False (intermediateTypeChecks.ContainsKey "FileSecond.fs") + +// [] TODO: differentiate complete and minimal checking requests +let ``Files that are not depended on don't invalidate cache`` () = + let project = SyntheticProject.Create( + sourceFile "First" [], + sourceFile "Second" ["First"], + sourceFile "Third" ["First"], + sourceFile "Last" ["Third"]) + + let cacheTcIntermediateEvents = ConcurrentQueue() + let cacheGraphConstructionEvents = ConcurrentQueue() + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + updateFile "First" updatePublicSurface + checkFile "Last" expectOk + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.Caches.TcIntermediate.OnEvent cacheTcIntermediateEvents.Enqueue + checker.Caches.DependencyGraph.OnEvent cacheGraphConstructionEvents.Enqueue + + }) + updateFile "Second" updatePublicSurface + checkFile "Last" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheTcIntermediateEvents + |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Map + + let graphConstructions = + cacheGraphConstructionEvents + |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Map + + Assert.Equal([Started; Finished], graphConstructions["FileLast.fs"]) + + Assert.Equal([], intermediateTypeChecks |> Map.toList) + +// [] TODO: differentiate complete and minimal checking requests +let ``Files that are not depended on don't invalidate cache part 2`` () = + let project = SyntheticProject.Create( + sourceFile "A" [], + sourceFile "B" ["A"], + sourceFile "C" ["A"], + sourceFile "D" ["B"; "C"], + sourceFile "E" ["C"]) + + let cacheTcIntermediateEvents = ConcurrentQueue() + let cacheGraphConstructionEvents = ConcurrentQueue() + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + updateFile "A" updatePublicSurface + checkFile "D" expectOk + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.Caches.TcIntermediate.OnEvent cacheTcIntermediateEvents.Enqueue + checker.Caches.DependencyGraph.OnEvent cacheGraphConstructionEvents.Enqueue + }) + updateFile "B" updatePublicSurface + checkFile "E" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheTcIntermediateEvents + |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Seq.toList + + let graphConstructions = + cacheGraphConstructionEvents + |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Seq.toList + + Assert.Equal(["FileE.fs", [Started; Finished]], graphConstructions) + Assert.Equal(["FileE.fs", [Started; Finished]], intermediateTypeChecks) + +[] +let ``Changing impl files doesn't invalidate cache when they have signatures`` () = + let project = SyntheticProject.Create( + { sourceFile "A" [] with SignatureFile = AutoGenerated }, + { sourceFile "B" ["A"] with SignatureFile = AutoGenerated }, + { sourceFile "C" ["B"] with SignatureFile = AutoGenerated }) + + let cacheEvents = ConcurrentQueue() + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + updateFile "A" updatePublicSurface + checkFile "C" expectOk + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.Caches.TcIntermediate.OnEvent cacheEvents.Enqueue + }) + updateFile "A" updateInternal + checkFile "C" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheEvents + |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Seq.toList + + Assert.Equal([], intermediateTypeChecks) + +[] +let ``Changing impl file doesn't invalidate an in-memory referenced project`` () = + let library = SyntheticProject.Create("library", { sourceFile "A" [] with SignatureFile = AutoGenerated }) + + let project = { + SyntheticProject.Create("project", sourceFile "B" ["A"] ) + with DependsOn = [library] } + + let cacheEvents = ConcurrentQueue() + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + checkFile "B" expectOk + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.Caches.TcIntermediate.OnEvent cacheEvents.Enqueue + }) + updateFile "A" updateInternal + checkFile "B" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheEvents + |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Seq.toList + + Assert.Equal([], intermediateTypeChecks) + + +[] +[] +[] +let ``Multi-project`` signatureFiles = + + let sigFile = if signatureFiles then AutoGenerated else No + + let library = SyntheticProject.Create("library", + { sourceFile "LibA" [] + with + Source = "let f (x: int) = x" + SignatureFile = sigFile }, + { sourceFile "LibB" ["LibA"] with SignatureFile = sigFile }, + { sourceFile "LibC" ["LibA"] with SignatureFile = sigFile }, + { sourceFile "LibD" ["LibB"; "LibC"] with SignatureFile = sigFile } + ) + + let project = + { SyntheticProject.Create("app", + sourceFile "A" ["LibB"], + sourceFile "B" ["A"; "LibB"], + sourceFile "C" ["A"; "LibC"], + sourceFile "D" ["A"; "LibD"] + ) + with DependsOn = [library] } + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + updateFile "LibA" updatePublicSurface + checkFile "D" expectOk + } + + + +type ProjectAction = Get | Modify of (SyntheticProject -> SyntheticProject) +type ProjectModificaiton = Update of int | Add | Remove +type ProjectRequest = ProjectAction * AsyncReplyChannel + +type FuzzingEvent = StartedChecking | FinishedChecking of bool | AbortedChecking of string | ModifiedImplFile | ModifiedSigFile + +[] +type SignatureFiles = Yes = 1 | No = 2 | Some = 3 + +let fuzzingTest seed (project: SyntheticProject) = task { + let rng = System.Random seed + + let checkingThreads = 10 + let maxModificationDelayMs = 50 + let maxCheckingDelayMs = 20 + //let runTimeMs = 30000 + let signatureFileModificationProbability = 0.25 + let modificationLoopIterations = 50 + let checkingLoopIterations = 5 + + let minCheckingTimeoutMs = 0 + let maxCheckingTimeoutMs = 300 + + let builder = ProjectWorkflowBuilder(project, useTransparentCompiler = true, autoStart = false) + let checker = builder.Checker + + // Force creation and caching of options + do! SaveAndCheckProject project checker false |> Async.Ignore + + let projectAgent = MailboxProcessor.Start(fun (inbox: MailboxProcessor) -> + let rec loop project = + async { + let! action, reply = inbox.Receive() + let! project = + match action with + | Modify f -> async { + let p = f project + do! saveProject p false checker + return p } + | Get -> async.Return project + reply.Reply project + return! loop project + } + loop project) + + let getProject () = + projectAgent.PostAndAsyncReply(pair Get) + + let modifyProject f = + projectAgent.PostAndAsyncReply(pair(Modify f)) |> Async.Ignore + + let modificationProbabilities = [ + Update 1, 80 + Update 2, 5 + Update 10, 5 + //Add, 2 + //Remove, 1 + ] + + let modificationPicker = [| + for op, prob in modificationProbabilities do + for _ in 1 .. prob do + op + |] + + let addComment s = $"{s}\n\n// {rng.NextDouble()}" + let modifyImplFile f = { f with ExtraSource = f.ExtraSource |> addComment } + let modifySigFile f = { f with SignatureFile = Custom (f.SignatureFile.CustomText |> addComment) } + + let getRandomItem (xs: 'x array) = xs[rng.Next(0, xs.Length)] + + let getRandomModification () = modificationPicker |> getRandomItem + + let getRandomFile (project: SyntheticProject) = project.GetAllFiles() |> List.toArray |> getRandomItem + + let log = new ThreadLocal<_>((fun () -> ResizeArray<_>()), true) + + let exceptions = ConcurrentBag() + + let modificationLoop _ = task { + for _ in 1 .. modificationLoopIterations do + do! Task.Delay (rng.Next maxModificationDelayMs) + let modify project = + match getRandomModification() with + | Update n -> + + use _ = Activity.start "Update" [||] + let files = Set [ for _ in 1..n -> getRandomFile project |> snd ] + (project, files) + ||> Seq.fold (fun p file -> + let fileId = file.Id + let project, file = project.FindInAllProjects fileId + let opName, f = + if file.HasSignatureFile && rng.NextDouble() < signatureFileModificationProbability + then ModifiedSigFile, modifySigFile + else ModifiedImplFile, modifyImplFile + log.Value.Add (DateTime.Now.Ticks, opName, $"{project.Name} / {fileId}") + p |> updateFileInAnyProject fileId f) + | Add + | Remove -> + // TODO: + project + do! modifyProject modify + } + + let checkingLoop n _ = task { + for _ in 1 .. checkingLoopIterations do + let! project = getProject() + let p, file = project |> getRandomFile + + let timeout = rng.Next(minCheckingTimeoutMs, maxCheckingTimeoutMs) + + log.Value.Add (DateTime.Now.Ticks, StartedChecking, $"Loop #{n} {file.Id} ({timeout} ms timeout)") + let ct = new CancellationTokenSource() + ct.CancelAfter(timeout) + let job = Async.StartAsTask(checker |> checkFile file.Id p, cancellationToken = ct.Token) + try + use _ = Activity.start "Check" [||] + + let! parseResult, checkResult = job + log.Value.Add (DateTime.Now.Ticks, FinishedChecking (match checkResult with FSharpCheckFileAnswer.Succeeded _ -> true | _ -> false), $"Loop #{n} {file.Id}") + expectOk (parseResult, checkResult) () + with ex -> + let message = + match ex with + | :? AggregateException as e -> + match e.InnerException with + | :? GraphProcessingException as e -> $"GPE: {e.InnerException.Message}" + | _ -> e.Message + | _ -> ex.Message + log.Value.Add (DateTime.Now.Ticks, AbortedChecking (message), $"Loop #{n} {file.Id} %A{ex}") + if ex.Message <> "A task was canceled." then exceptions.Add ex + + do! Task.Delay (rng.Next maxCheckingDelayMs) + } + + use _tracerProvider = + Sdk.CreateTracerProviderBuilder() + .AddSource("fsc") + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(serviceName="F# Fuzzing", serviceVersion = "1")) + .AddJaegerExporter() + .Build() + + use _ = Activity.start $"Fuzzing {project.Name}" [ Activity.Tags.project, project.Name; "seed", seed.ToString() ] + + do! task { + let threads = + seq { + modificationLoop CancellationToken.None + // ignore modificationLoop + for n in 1..checkingThreads do + checkingLoop n CancellationToken.None + } + + try + let! _x = threads |> Task.WhenAll + () + with + | e -> + let _log = log.Values |> Seq.collect id |> Seq.sortBy p13 |> Seq.toArray + failwith $"Seed: {seed}\nException: %A{e}" + } + let log = log.Values |> Seq.collect id |> Seq.sortBy p13 |> Seq.toArray + + let _stats = log |> Array.groupBy (p23) |> Array.map (fun (op, xs) -> op, xs.Length) |> Map + + let _errors = _stats |> Map.toSeq |> Seq.filter (fst >> function AbortedChecking ex when ex <> "A task was canceled." -> true | _ -> false) |> Seq.toArray + + let _exceptions = exceptions + + Assert.Equal>([||], _errors) + + //Assert.Equal>(Map.empty, _stats) + + builder.DeleteProjectDir() +} + + +[] +[] +[] +[] +let Fuzzing signatureFiles = + + let seed = 1106087513 + let rng = System.Random(int seed) + + let fileCount = 30 + let maxDepsPerFile = 3 + + let fileName i = sprintf $"F%03d{i}" + + //let extraCode = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ "src" ++ "Compiler" ++ "Utilities" ++ "EditDistance.fs" |> File.ReadAllLines |> Seq.skip 5 |> String.concat "\n" + let extraCode = "" + + let files = + [| for i in 1 .. fileCount do + let name = fileName i + let deps = [ + for _ in 1 .. maxDepsPerFile do + if i > 1 then + fileName <| rng.Next(1, i) ] + let signature = + match signatureFiles with + | SignatureFiles.Yes -> AutoGenerated + | SignatureFiles.Some when rng.NextDouble() < 0.5 -> AutoGenerated + | _ -> No + + { sourceFile name deps + with + SignatureFile = signature + ExtraSource = extraCode } + |] + + let initialProject = SyntheticProject.Create(files) + + let builder = ProjectWorkflowBuilder(initialProject, useTransparentCompiler = true, autoStart = false) + let checker = builder.Checker + + let initialProject = initialProject |> absorbAutoGeneratedSignatures checker |> Async.RunSynchronously + + fuzzingTest seed initialProject + + +let reposDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." +let giraffeDir = reposDir ++ "Giraffe" ++ "src" ++ "Giraffe" |> Path.GetFullPath +let giraffeTestsDir = reposDir ++ "Giraffe" ++ "tests" ++ "Giraffe.Tests" |> Path.GetFullPath +let giraffeSignaturesDir = reposDir ++ "giraffe-signatures" ++ "src" ++ "Giraffe" |> Path.GetFullPath +let giraffeSignaturesTestsDir = reposDir ++ "giraffe-signatures" ++ "tests" ++ "Giraffe.Tests" |> Path.GetFullPath + + +type GiraffeTheoryAttribute() = + inherit Xunit.TheoryAttribute() + do + if not (Directory.Exists giraffeDir) then + do base.Skip <- $"Giraffe not found ({giraffeDir}). You can get it here: https://github.com/giraffe-fsharp/Giraffe" + if not (Directory.Exists giraffeSignaturesDir) then + do base.Skip <- $"Giraffe (with signatures) not found ({giraffeSignaturesDir}). You can get it here: https://github.com/nojaf/Giraffe/tree/signatures" + +[] +[] +[] +let GiraffeFuzzing signatureFiles = + let seed = System.Random().Next() + //let seed = 1044159179 + + let giraffeDir = if signatureFiles then giraffeSignaturesDir else giraffeDir + let giraffeTestsDir = if signatureFiles then giraffeSignaturesTestsDir else giraffeTestsDir + + let giraffeProject = SyntheticProject.CreateFromRealProject giraffeDir + let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } + + let testsProject = SyntheticProject.CreateFromRealProject giraffeTestsDir + let testsProject = + { testsProject + with + OtherOptions = "--nowarn:FS3520"::testsProject.OtherOptions + DependsOn = [ giraffeProject ] + NugetReferences = giraffeProject.NugetReferences @ testsProject.NugetReferences + } + + fuzzingTest seed testsProject + + + +[] +[] +[] +let ``File moving test`` signatureFiles = + let giraffeDir = if signatureFiles then giraffeSignaturesDir else giraffeDir + let giraffeProject = SyntheticProject.CreateFromRealProject giraffeDir + let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } + + giraffeProject.Workflow { + // clearCache -- for better tracing + checkFile "Json" expectOk + moveFile "Json" 1 Down + checkFile "Json" expectOk + } + + +[] +[] +let ``What happens if bootrstapInfoStatic needs to be recomputed`` _ = + + let giraffeProject = SyntheticProject.CreateFromRealProject giraffeSignaturesDir + let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } + + giraffeProject.Workflow { + updateFile "Helpers" (fun f -> { f with SignatureFile = Custom (f.SignatureFile.CustomText + "\n") }) + checkFile "EndpointRouting" expectOk + withChecker (fun checker -> + async { + checker.Caches.BootstrapInfoStatic.Clear() + checker.Caches.BootstrapInfo.Clear() + checker.Caches.FrameworkImports.Clear() + ignore checker + return () + }) + updateFile "Core" (fun f -> { f with SignatureFile = Custom (f.SignatureFile.CustomText + "\n") }) + checkFile "EndpointRouting" expectOk + } + + +module ParsedInputHashing = + + let source = """ + +type T = { A: int; B: string } + +module Stuff = + + // Some comment + let f x = x + 75 +""" + + let getParseResult source = + let fileName, snapshot, checker = singleFileChecker source + checker.ParseFile(fileName, snapshot) |> Async.RunSynchronously + + //[] + let ``Hash stays the same when whitespace changes`` () = + + //let parseResult = getParseResult source + + //let hash = parseResult.ParseTree |> parsedInputHash |> BitConverter.ToString + + //let parseResult2 = getParseResult (source + "\n \n") + + //let hash2 = parseResult2.ParseTree |> parsedInputHash |> BitConverter.ToString + + //Assert.Equal(hash, hash2) + + () + +/// Update these paths to a local response file with compiler arguments of existing F# projects. +/// References projects are expected to have been built. +let localResponseFiles = + [| + @"C:\Projects\fantomas\src\Fantomas.Core.Tests\Fantomas.Core.Tests.rsp" + |] + |> Array.collect (fun f -> + [| + [| true :> obj; f:> obj |] + [| false :> obj; f :> obj|] + |] + ) + +// Uncomment this attribute if you want run this test against local response files. +// [] +[] +let ``TypeCheck last file in project with transparent compiler`` useTransparentCompiler responseFile = + let responseFile = FileInfo responseFile + let syntheticProject = mkSyntheticProjectForResponseFile responseFile + + let workflow = + ProjectWorkflowBuilder( + syntheticProject, + isExistingProject = true, + useTransparentCompiler = useTransparentCompiler + ) + + let lastFile = + syntheticProject.SourceFiles + |> List.tryLast + |> Option.map (fun sf -> sf.Id) + + match lastFile with + | None -> failwithf "Last file of project could not be found" + | Some lastFile -> + + workflow { + clearCache + checkFile lastFile expectOk + } diff --git a/tests/FSharp.Compiler.ComponentTests/Language/BooleanReturningAndReturnTypeDirectedPartialActivePatternTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/BooleanReturningAndReturnTypeDirectedPartialActivePatternTests.fs new file mode 100644 index 00000000000..ce630714e64 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Language/BooleanReturningAndReturnTypeDirectedPartialActivePatternTests.fs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module Language.BooleanReturningAndReturnTypeDirectedPartialActivePatternTests + +open Xunit +open FSharp.Test.Compiler +open FSharp.Test.ScriptHelpers + +let fsiSession = getSessionForEval [||] LangVersion.Preview + +let runCode = evalInSharedSession fsiSession + +[] +let ``Partial struct active pattern returns ValueOption`1 without []`` () = + FSharp "let (|P1|_|) x = ValueNone" + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + +[] +let ``Partial struct active pattern returns bool`` () = + FSharp "let (|P1|_|) x = false" + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + +[] +let ``Single case active pattern returning bool should success`` () = + FSharp """ +let (|IsA|) x = x = "A" +let (IsA r) = "A" + """ + |> withLangVersionPreview + |> typecheck + |> shouldSucceed + +[] +let ``Partial struct active pattern results can be retrieved`` () = + Fsx """ +let fail msg = + printfn "%s" msg + failwith msg + +let (|P1|_|) x = x <> 0 +let (|EqualTo|_|) y x = x = y + +match 0, 1 with +| P1, _ -> fail "unit" +| _, P1 -> () +| _ -> fail "unit" + +match "x" with +| EqualTo "y" -> fail "with argument" +| EqualTo "x" -> () +| _ -> fail "with argument" + """ + |> withLangVersionPreview + |> runCode + |> shouldSucceed + +// negative tests + +[] +let ``bool active pattern (-langversion:8.0)`` () = + FSharp """let (|OddBool|_|) x = x % 2 = 1 +let (|OddVOption|_|) x = if x % 2 = 1 then ValueSome() else ValueNone + """ + |> withLangVersion80 + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3350, Line 1, Col 5, Line 1, Col 20, "Feature 'Boolean-returning and return-type-directed partial active patterns' is not available in F# 8.0. Please use language version 'PREVIEW' or greater.") + (Error 3350, Line 2, Col 5, Line 2, Col 23, "Feature 'Boolean-returning and return-type-directed partial active patterns' is not available in F# 8.0. Please use language version 'PREVIEW' or greater.") + ] + +[] +let ``Can not receive result from bool active pattern`` () = + FSharp """let (|IsA|_|) x = x = "A" + +match "A" with +| IsA result -> "A" +| _ -> "Not A" + +match "A" with +| IsA result -> result +| _ -> "Not A" + +match "A" with +| IsA "to match return value" -> "Matched" +| _ -> "not Matched" +""" + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 1, Line 4, Col 3, Line 4, Col 13, + "This expression was expected to have type + 'string -> bool' +but here has type + 'bool' ") + (Error 39, Line 4, Col 7, Line 4, Col 13, + "The value or constructor 'result' is not defined. Maybe you want one of the following: + Result") + (Error 1, Line 8, Col 3, Line 8, Col 13, + "This expression was expected to have type + 'string -> bool' +but here has type + 'bool' ") + (Error 39, Line 8, Col 7, Line 8, Col 13, + "The value or constructor 'result' is not defined. Maybe you want one of the following: + Result") + (Error 1, Line 12, Col 3, Line 12, Col 30, + "This expression was expected to have type + 'string -> bool' +but here has type + 'bool' ") + ] diff --git a/tests/FSharp.Compiler.ComponentTests/Language/DiscriminatedUnionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/DiscriminatedUnionTests.fs index abd5fb6e3b6..d7de4c35cd2 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/DiscriminatedUnionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/DiscriminatedUnionTests.fs @@ -17,6 +17,20 @@ if foo.IsBar then failwith "Should not be Bar" |> compileExeAndRun |> shouldSucceed + [] + let ``Simple Is* discriminated union properties are not visible for a single case union`` () = + Fsx """ +type Foo = Bar of string +let foo = Foo.Bar "hi" +if not foo.IsBar then failwith "Should be Bar" + + """ + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics [Error 39, Line 4, Col 12, Line 4, Col 17, "The type 'Foo' does not define the field, constructor or member 'IsBar'. Maybe you want one of the following: + Bar"] + [] let ``Simple Is* discriminated union property satisfies SRTP constraint`` () = Fsx """ diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/MissingDiagnostic.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/MissingDiagnostic.fs index 55181649b03..15b2ae61df0 100644 --- a/tests/FSharp.Compiler.ComponentTests/Signatures/MissingDiagnostic.fs +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/MissingDiagnostic.fs @@ -27,28 +27,34 @@ let ``Compile gives errors`` () = but here has type 'char' ") -[] -let ``Type check project with signature file doesn't get the diagnostic`` () = +[] +[] +[] +let ``Type check project with signature file doesn't get the diagnostic`` useTransparentCompiler = Fsi signature |> withAdditionalSourceFile (FsSource implementation) - |> typecheckProject false + |> typecheckProject false useTransparentCompiler |> fun projectResults -> projectResults.Diagnostics |> ignore Assert.False (projectResults.Diagnostics |> Array.isEmpty) -[] -let ``Type check project without signature file does get the diagnostic`` () = +[] +[] +[] +let ``Type check project without signature file does get the diagnostic`` useTransparentCompiler = Fs implementation - |> typecheckProject false + |> typecheckProject false useTransparentCompiler |> fun projectResults -> projectResults.Diagnostics |> ignore Assert.False (projectResults.Diagnostics |> Array.isEmpty) -[] -let ``Enabling enablePartialTypeChecking = true doesn't change the problem`` () = +[] +[] +[] +let ``Enabling enablePartialTypeChecking = true doesn't change the problem`` useTransparentCompiler = Fsi signature |> withAdditionalSourceFile (FsSource implementation) - |> typecheckProject true + |> typecheckProject true useTransparentCompiler |> fun projectResults -> projectResults.Diagnostics |> ignore Assert.False (projectResults.Diagnostics |> Array.isEmpty) \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/FileContentMappingTests.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/FileContentMappingTests.fs index 9504d4b019e..12f171de5fa 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/FileContentMappingTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/FileContentMappingTests.fs @@ -121,3 +121,37 @@ module B = C match content with | [ TopLevelNamespace "" [ PrefixedIdentifier "C" ] ] -> Assert.Pass() | content -> Assert.Fail($"Unexpected content: {content}") + + +module InvalidSyntax = + + [] + let ``Nested module`` () = + let content = + getContent + false + """ + module A + + module B.C + """ + + match content with + | [ TopLevelNamespace "" [] ] -> Assert.Pass() + | content -> Assert.Fail($"Unexpected content: {content}") + + + [] + let ``Module above namespace`` () = + let content = + getContent + false + """ + module + + namespace A.B.C + """ + + match content with + | [ TopLevelNamespace "" [] ] -> Assert.Pass() + | content -> Assert.Fail($"Unexpected content: {content}") diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs index c75aed594c3..80f7caecafb 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/Scenarios.fs @@ -800,4 +800,114 @@ printfn "Hello" """ Set.empty ] + scenario + "Nameof module with namespace" + [ + sourceFile + "A.fs" + """ +namespace X.Y.Z + +module Foo = + let x = 2 +""" + Set.empty + sourceFile + "B.fs" + """ +namespace X.Y.Z + +module Point = + let y = nameof Foo +""" + (set [| 0 |]) + ] + scenario + "Nameof module without namespace" + [ + sourceFile + "A.fs" + """ +module Foo + +let x = 2 +""" + Set.empty + sourceFile + "B.fs" + """ +module Point + +let y = nameof Foo +""" + (set [| 0 |]) + ] + scenario + "Single module name should always be checked, regardless of own namespace" + [ + sourceFile "X.fs" "namespace X.Y" Set.empty + sourceFile + "A.fs" + """ +module Foo + +let x = 2 +""" + Set.empty + sourceFile + "B.fs" + """ +namespace X.Y + +type T() = + let _ = nameof Foo +""" + (set [| 1 |]) + ] + scenario + "nameof pattern" + [ + sourceFile "A.fs" "module Foo" Set.empty + sourceFile + "B.fs" + """ +module Bar + +do + match "" with + | nameof Foo -> () + | _ -> () +""" + (set [| 0 |]) + ] + scenario + "parentheses around module name in nameof pattern" + [ + sourceFile "A.fs" "module Foo" Set.empty + sourceFile + "B.fs" + """ +module Bar + +do + match "" with + | nameof ((Foo)) -> () + | _ -> () +""" + (set [| 0 |]) + ] + + scenario + "parentheses around module name in nameof expression" + [ + sourceFile "A.fs" "module Foo" Set.empty + sourceFile + "B.fs" + """ +module Bar + +let _ = nameof ((Foo)) +""" + (set [| 0 |]) + ] ] \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TrieMappingTests.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TrieMappingTests.fs index e5bc3ef0299..6ad29818a2a 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TrieMappingTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TrieMappingTests.fs @@ -540,6 +540,27 @@ let ``Tries are built up incrementally`` () = ParsedInput = parseSourceCode ("D.fs", "module D") } |] - + for idx, t in trie do Assert.AreEqual(idx + 1, t.Children.Count) + + +module InvalidSyntax = + + [] + let ``Unnamed module`` () = + let trie = + getLastTrie + [| { Idx = 0 + FileName = "A.fs" + ParsedInput = + parseSourceCode ( + "A.fs", + """ + module + + () + """ + ) } |] + + Assert.True trie.Children.IsEmpty diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TypedTreeGraph.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TypedTreeGraph.fs index 9e25c623532..c4680870efc 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TypedTreeGraph.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TypedTreeGraph.fs @@ -140,7 +140,7 @@ let ``Create Graph from typed tree`` (projectArgumentsFilePath: string) = graphFromTypedTree |> Graph.map (fun n -> n,files.[n].File) - |> Graph.serialiseToMermaid $"{fileName}.typed-tree.deps.md" + |> Graph.writeMermaidToFile $"{fileName}.typed-tree.deps.md" let collectAllDeps (graph: Graph) = (Map.empty, [ 0 .. (sourceFiles.Length - 1) ]) @@ -161,7 +161,7 @@ let ``Create Graph from typed tree`` (projectArgumentsFilePath: string) = graphFromHeuristic |> Graph.map (fun n -> n, files.[n].File) - |> Graph.serialiseToMermaid $"{fileName}.heuristic-tree.deps.md" + |> Graph.writeMermaidToFile $"{fileName}.heuristic-tree.deps.md" let heuristicMap = collectAllDeps graphFromHeuristic diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs index 118ea134ff3..339fa1e2629 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs @@ -14,7 +14,7 @@ module TyparNameTests = (additionalFile: SourceCodeFileKind) : string array = let typeCheckResult = - cUnit |> withAdditionalSourceFile additionalFile |> typecheckProject false + cUnit |> withAdditionalSourceFile additionalFile |> typecheckProject false false assert (Array.isEmpty typeCheckResult.Diagnostics) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 25a540db859..e53606d2de3 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2038,7 +2038,7 @@ FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: FSharp.Compiler.Symbols. FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] DependencyFiles FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] get_DependencyFiles() -FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Instance FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker get_Instance() FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions GetProjectOptionsFromCommandLineArgs(System.String, System.String[], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) @@ -2049,14 +2049,19 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualCheckFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualParseFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer] CheckFileInProject(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] GetBackgroundParseResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpParsingOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFileInProject(System.String, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] CheckFileInProjectAllowingStaleCachedResults(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyFileChanged(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyProjectCleaned(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharpProjectSnapshot, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]]] GetProjectOptionsFromScript(System.String, FSharp.Compiler.Text.ISourceText, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.String[]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Int64], Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2077,6 +2082,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.CodeA FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParsingOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]] GetParsingOptionsFromProjectOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.Tokenization.FSharpTokenInfo[],FSharp.Compiler.Tokenization.FSharpTokenizerLexState] TokenizeLine(System.String, FSharp.Compiler.Tokenization.FSharpTokenizerLexState) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearCache(System.Collections.Generic.IEnumerable`1[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearCache(System.Collections.Generic.IEnumerable`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateAll() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateConfiguration(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2294,6 +2300,101 @@ FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_baggage() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_itemSpec() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String itemSpec FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.String],System.String], System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean IsSignatureFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean get_IsSignatureFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot Create(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot CreateFromFileSystem(System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String FileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String GetFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String Version +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String get_FileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String get_Version() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew] GetSource() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Boolean Equals(FSharpProjectIdentifier) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: FSharpProjectIdentifier NewFSharpProjectIdentifier(System.String, System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 CompareTo(FSharpProjectIdentifier) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 CompareTo(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 Tag +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 get_Tag() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_outputFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_projectFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String outputFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String projectFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier Identifier +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier get_Identifier() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Replace(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]]], Microsoft.FSharp.Core.FSharpOption`1[System.Collections.Generic.Dictionary`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, System.String, Int32, FSharp.Compiler.Text.ISourceText) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.String Label +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.String get_Label() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+FSharpReference: FSharpProjectSnapshot get_snapshot() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+FSharpReference: FSharpProjectSnapshot snapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+FSharpReference: System.String get_projectOutputFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+FSharpReference: System.String projectOutputFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.AbstractIL.ILBinaryReader+ILModuleReader] getReader +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.AbstractIL.ILBinaryReader+ILModuleReader] get_getReader() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime] getStamp +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime] get_getStamp() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference: System.String get_projectOutputFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference: System.String projectOutputFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+PEReference: FSharp.Compiler.CodeAnalysis.DelayedILModuleReader delayedReader +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+PEReference: FSharp.Compiler.CodeAnalysis.DelayedILModuleReader get_delayedReader() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+PEReference: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime] getStamp +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+PEReference: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime] get_getStamp() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+Tags: Int32 FSharpReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+Tags: Int32 ILModuleReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+Tags: Int32 PEReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean IsFSharpReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean IsILModuleReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean IsPEReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean get_IsFSharpReference() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean get_IsILModuleReference() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean get_IsPEReference() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+FSharpReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+PEReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+Tags +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpReferencedProjectSnapshot CreateFSharp(System.String, FSharpProjectSnapshot) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpReferencedProjectSnapshot NewFSharpReference(System.String, FSharpProjectSnapshot) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpReferencedProjectSnapshot NewILModuleReference(System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.AbstractIL.ILBinaryReader+ILModuleReader]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpReferencedProjectSnapshot NewPEReference(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime], FSharp.Compiler.CodeAnalysis.DelayedILModuleReader) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Int32 Tag +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Int32 get_Tag() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String OutputFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String get_OutputFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(ReferenceOnDisk) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 CompareTo(ReferenceOnDisk) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 CompareTo(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.DateTime LastModified +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.DateTime get_LastModified() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String Path +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String get_Path() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Void .ctor(System.String, System.DateTime) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk FSharp.Compiler.CompilerEnvironment: Boolean IsCheckerSupportedSubcategory(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsCompilable(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsScriptFile(System.String) @@ -4250,7 +4351,6 @@ FSharp.Compiler.EditorServices.TupledArgumentLocation: Int32 GetHashCode() FSharp.Compiler.EditorServices.TupledArgumentLocation: Int32 GetHashCode(System.Collections.IEqualityComparer) FSharp.Compiler.EditorServices.TupledArgumentLocation: System.String ToString() FSharp.Compiler.EditorServices.TupledArgumentLocation: Void .ctor(Boolean, FSharp.Compiler.Text.Range) -FSharp.Compiler.EditorServices.UnnecessaryParentheses: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] getUnnecessaryParentheses(Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.String], FSharp.Compiler.Syntax.ParsedInput) FSharp.Compiler.EditorServices.UnresolvedSymbol: Boolean Equals(FSharp.Compiler.EditorServices.UnresolvedSymbol) FSharp.Compiler.EditorServices.UnresolvedSymbol: Boolean Equals(System.Object) FSharp.Compiler.EditorServices.UnresolvedSymbol: Boolean Equals(System.Object, System.Collections.IEqualityComparer) @@ -4768,8 +4868,10 @@ FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp. FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpStaticParameter] get_StaticParameters() FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpType] AllInterfaces FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpType] DeclaredInterfaces +FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpType] GenericArguments FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpType] get_AllInterfaces() FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpType] get_DeclaredInterfaces() +FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpType] get_GenericArguments() FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpUnionCase] UnionCases FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpUnionCase] get_UnionCases() FSharp.Compiler.Symbols.FSharpEntity: System.String AccessPath @@ -5797,6 +5899,12 @@ FSharp.Compiler.Syntax.ParsedInput: Microsoft.FSharp.Collections.FSharpSet`1[Sys FSharp.Compiler.Syntax.ParsedInput: System.String FileName FSharp.Compiler.Syntax.ParsedInput: System.String ToString() FSharp.Compiler.Syntax.ParsedInput: System.String get_FileName() +FSharp.Compiler.Syntax.ParsedInputModule: Boolean exists(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode],Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SyntaxNode,System.Boolean]], FSharp.Compiler.Text.Position, FSharp.Compiler.Syntax.ParsedInput) +FSharp.Compiler.Syntax.ParsedInputModule: Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`2[FSharp.Compiler.Syntax.SyntaxNode,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode]]] tryNode(FSharp.Compiler.Text.Position, FSharp.Compiler.Syntax.ParsedInput) +FSharp.Compiler.Syntax.ParsedInputModule: Microsoft.FSharp.Core.FSharpOption`1[T] tryPickLast[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode],Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SyntaxNode,Microsoft.FSharp.Core.FSharpOption`1[T]]], FSharp.Compiler.Text.Position, FSharp.Compiler.Syntax.ParsedInput) +FSharp.Compiler.Syntax.ParsedInputModule: Microsoft.FSharp.Core.FSharpOption`1[T] tryPick[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode],Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SyntaxNode,Microsoft.FSharp.Core.FSharpOption`1[T]]], FSharp.Compiler.Text.Position, FSharp.Compiler.Syntax.ParsedInput) +FSharp.Compiler.Syntax.ParsedInputModule: State foldWhile[State](Microsoft.FSharp.Core.FSharpFunc`2[State,Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode],Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SyntaxNode,Microsoft.FSharp.Core.FSharpOption`1[State]]]], State, FSharp.Compiler.Syntax.ParsedInput) +FSharp.Compiler.Syntax.ParsedInputModule: State fold[State](Microsoft.FSharp.Core.FSharpFunc`2[State,Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode],Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SyntaxNode,State]]], State, FSharp.Compiler.Syntax.ParsedInput) FSharp.Compiler.Syntax.ParsedScriptInteraction: FSharp.Compiler.Syntax.ParsedScriptInteraction NewDefinitions(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynModuleDecl], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.ParsedScriptInteraction: FSharp.Compiler.Text.Range get_range() FSharp.Compiler.Syntax.ParsedScriptInteraction: FSharp.Compiler.Text.Range range @@ -7351,6 +7459,7 @@ FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Text.Range range FSharp.Compiler.Syntax.SynExprAndBang: Int32 Tag FSharp.Compiler.Syntax.SynExprAndBang: Int32 get_Tag() FSharp.Compiler.Syntax.SynExprAndBang: System.String ToString() +FSharp.Compiler.Syntax.SynExprModule: Boolean shouldBeParenthesizedInContext(Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], FSharp.Compiler.Syntax.SynExpr) FSharp.Compiler.Syntax.SynExprRecordField: FSharp.Compiler.Syntax.SynExprRecordField NewSynExprRecordField(System.Tuple`2[FSharp.Compiler.Syntax.SynLongIdent,System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynExpr], Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`2[FSharp.Compiler.Text.Range,Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Position]]]) FSharp.Compiler.Syntax.SynExprRecordField: Int32 Tag FSharp.Compiler.Syntax.SynExprRecordField: Int32 get_Tag() @@ -7610,8 +7719,8 @@ FSharp.Compiler.Syntax.SynMemberDefn+GetSetMember: Microsoft.FSharp.Core.FSharpO FSharp.Compiler.Syntax.SynMemberDefn+GetSetMember: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynBinding] get_memberDefnForSet() FSharp.Compiler.Syntax.SynMemberDefn+GetSetMember: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynBinding] memberDefnForGet FSharp.Compiler.Syntax.SynMemberDefn+GetSetMember: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynBinding] memberDefnForSet -FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.Syntax.SynSimplePats ctorArgs -FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.Syntax.SynSimplePats get_ctorArgs() +FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.Syntax.SynPat ctorArgs +FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.Syntax.SynPat get_ctorArgs() FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.SyntaxTrivia.SynMemberDefnImplicitCtorTrivia get_trivia() FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.SyntaxTrivia.SynMemberDefnImplicitCtorTrivia trivia FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.Text.Range get_range() @@ -7711,7 +7820,7 @@ FSharp.Compiler.Syntax.SynMemberDefn: Boolean get_IsValField() FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewAbstractSlot(FSharp.Compiler.Syntax.SynValSig, FSharp.Compiler.Syntax.SynMemberFlags, FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynMemberDefnAbstractSlotTrivia) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewAutoProperty(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList], Boolean, FSharp.Compiler.Syntax.Ident, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynType], FSharp.Compiler.Syntax.SynMemberKind, FSharp.Compiler.Syntax.SynMemberFlags, FSharp.Compiler.Syntax.SynMemberFlags, FSharp.Compiler.Xml.PreXmlDoc, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess], FSharp.Compiler.Syntax.SynExpr, FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynMemberDefnAutoPropertyTrivia) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewGetSetMember(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynBinding], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynBinding], FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynMemberGetSetTrivia) -FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewImplicitCtor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList], FSharp.Compiler.Syntax.SynSimplePats, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident], FSharp.Compiler.Xml.PreXmlDoc, FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynMemberDefnImplicitCtorTrivia) +FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewImplicitCtor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList], FSharp.Compiler.Syntax.SynPat, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident], FSharp.Compiler.Xml.PreXmlDoc, FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynMemberDefnImplicitCtorTrivia) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewImplicitInherit(FSharp.Compiler.Syntax.SynType, FSharp.Compiler.Syntax.SynExpr, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewInherit(FSharp.Compiler.Syntax.SynType, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewInterface(FSharp.Compiler.Syntax.SynType, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynMemberDefn]], FSharp.Compiler.Text.Range) @@ -8364,6 +8473,7 @@ FSharp.Compiler.Syntax.SynPat: FSharp.Compiler.Text.Range get_Range() FSharp.Compiler.Syntax.SynPat: Int32 Tag FSharp.Compiler.Syntax.SynPat: Int32 get_Tag() FSharp.Compiler.Syntax.SynPat: System.String ToString() +FSharp.Compiler.Syntax.SynPatModule: Boolean shouldBeParenthesizedInContext(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], FSharp.Compiler.Syntax.SynPat) FSharp.Compiler.Syntax.SynRationalConst+Integer: FSharp.Compiler.Text.Range get_range() FSharp.Compiler.Syntax.SynRationalConst+Integer: FSharp.Compiler.Text.Range range FSharp.Compiler.Syntax.SynRationalConst+Integer: Int32 get_value() @@ -9177,8 +9287,8 @@ FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Collectio FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[FSharp.Compiler.Syntax.SynValSig,FSharp.Compiler.Syntax.SynMemberFlags]] slotsigs FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Syntax.SynType,FSharp.Compiler.Text.Range,Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident]]] get_inherits() FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Syntax.SynType,FSharp.Compiler.Text.Range,Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident]]] inherits -FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynSimplePats] get_implicitCtorSynPats() -FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynSimplePats] implicitCtorSynPats +FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynPat] get_implicitCtorSynPats() +FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynPat] implicitCtorSynPats FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+LibraryOnlyILAssembly: FSharp.Compiler.Text.Range get_range() FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+LibraryOnlyILAssembly: FSharp.Compiler.Text.Range range FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+LibraryOnlyILAssembly: System.Object get_ilType() @@ -9229,7 +9339,7 @@ FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: Boolean get_IsTypeAbbrev() FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: Boolean get_IsUnion() FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewEnum(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynEnumCase], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewException(FSharp.Compiler.Syntax.SynExceptionDefnRepr) -FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewGeneral(FSharp.Compiler.Syntax.SynTypeDefnKind, Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Syntax.SynType,FSharp.Compiler.Text.Range,Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident]]], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[FSharp.Compiler.Syntax.SynValSig,FSharp.Compiler.Syntax.SynMemberFlags]], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynField], Boolean, Boolean, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynSimplePats], FSharp.Compiler.Text.Range) +FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewGeneral(FSharp.Compiler.Syntax.SynTypeDefnKind, Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Syntax.SynType,FSharp.Compiler.Text.Range,Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident]]], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[FSharp.Compiler.Syntax.SynValSig,FSharp.Compiler.Syntax.SynMemberFlags]], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynField], Boolean, Boolean, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynPat], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewLibraryOnlyILAssembly(System.Object, FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewNone(FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewRecord(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynField], FSharp.Compiler.Text.Range) @@ -9454,9 +9564,12 @@ FSharp.Compiler.Syntax.SyntaxNode: FSharp.Compiler.Syntax.SyntaxNode+SynTypeDefn FSharp.Compiler.Syntax.SyntaxNode: FSharp.Compiler.Syntax.SyntaxNode+SynTypeDefnSig FSharp.Compiler.Syntax.SyntaxNode: FSharp.Compiler.Syntax.SyntaxNode+SynValSig FSharp.Compiler.Syntax.SyntaxNode: FSharp.Compiler.Syntax.SyntaxNode+Tags +FSharp.Compiler.Syntax.SyntaxNode: FSharp.Compiler.Text.Range Range +FSharp.Compiler.Syntax.SyntaxNode: FSharp.Compiler.Text.Range get_Range() FSharp.Compiler.Syntax.SyntaxNode: Int32 Tag FSharp.Compiler.Syntax.SyntaxNode: Int32 get_Tag() FSharp.Compiler.Syntax.SyntaxNode: System.String ToString() +FSharp.Compiler.Syntax.SyntaxNodeModule: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList] |Attributes|(FSharp.Compiler.Syntax.SyntaxNode) FSharp.Compiler.Syntax.SyntaxTraversal: Microsoft.FSharp.Core.FSharpOption`1[T] Traverse[T](FSharp.Compiler.Text.Position, FSharp.Compiler.Syntax.ParsedInput, FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitAttributeApplication(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], FSharp.Compiler.Syntax.SynAttributeList) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitBinding(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SynBinding,Microsoft.FSharp.Core.FSharpOption`1[T]], FSharp.Compiler.Syntax.SynBinding) @@ -9476,7 +9589,7 @@ FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOptio FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitPat(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SynPat,Microsoft.FSharp.Core.FSharpOption`1[T]], FSharp.Compiler.Syntax.SynPat) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitRecordDefn(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynField], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitRecordField(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynExpr], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynLongIdent]) -FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitSimplePats(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynSimplePat]) +FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitSimplePats(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], FSharp.Compiler.Syntax.SynPat) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitType(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SynType,Microsoft.FSharp.Core.FSharpOption`1[T]], FSharp.Compiler.Syntax.SynType) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitTypeAbbrev(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], FSharp.Compiler.Syntax.SynType, FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitUnionDefn(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynUnionCase], FSharp.Compiler.Text.Range) @@ -10193,6 +10306,7 @@ FSharp.Compiler.Text.ISourceText: System.String GetSubTextFromRange(FSharp.Compi FSharp.Compiler.Text.ISourceText: System.String GetSubTextString(Int32, Int32) FSharp.Compiler.Text.ISourceText: System.Tuple`2[System.Int32,System.Int32] GetLastCharacterPosition() FSharp.Compiler.Text.ISourceText: Void CopyTo(Int32, Char[], Int32, Int32) +FSharp.Compiler.Text.ISourceTextNew: System.Collections.Immutable.ImmutableArray`1[System.Byte] GetChecksum() FSharp.Compiler.Text.Line: Int32 fromZ(Int32) FSharp.Compiler.Text.Line: Int32 toZ(Int32) FSharp.Compiler.Text.NavigableTaggedText: FSharp.Compiler.Text.Range Range @@ -10272,6 +10386,8 @@ FSharp.Compiler.Text.RangeModule: System.Tuple`2[System.String,System.Tuple`2[Sy FSharp.Compiler.Text.RangeModule: System.Tuple`2[System.Tuple`2[System.Int32,System.Int32],System.Tuple`2[System.Int32,System.Int32]] toZ(FSharp.Compiler.Text.Range) FSharp.Compiler.Text.RangeModule: Void outputRange(System.IO.TextWriter, FSharp.Compiler.Text.Range) FSharp.Compiler.Text.SourceText: FSharp.Compiler.Text.ISourceText ofString(System.String) +FSharp.Compiler.Text.SourceTextNew: FSharp.Compiler.Text.ISourceTextNew ofISourceText(FSharp.Compiler.Text.ISourceText) +FSharp.Compiler.Text.SourceTextNew: FSharp.Compiler.Text.ISourceTextNew ofString(System.String) FSharp.Compiler.Text.TaggedText: FSharp.Compiler.Text.TextTag Tag FSharp.Compiler.Text.TaggedText: FSharp.Compiler.Text.TextTag get_Tag() FSharp.Compiler.Text.TaggedText: System.String Text diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 25a540db859..e53606d2de3 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2038,7 +2038,7 @@ FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: FSharp.Compiler.Symbols. FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] DependencyFiles FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] get_DependencyFiles() -FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Instance FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker get_Instance() FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions GetProjectOptionsFromCommandLineArgs(System.String, System.String[], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) @@ -2049,14 +2049,19 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualCheckFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualParseFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer] CheckFileInProject(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] GetBackgroundParseResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpParsingOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFileInProject(System.String, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] CheckFileInProjectAllowingStaleCachedResults(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyFileChanged(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyProjectCleaned(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharpProjectSnapshot, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]]] GetProjectOptionsFromScript(System.String, FSharp.Compiler.Text.ISourceText, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.String[]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Int64], Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2077,6 +2082,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.CodeA FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParsingOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]] GetParsingOptionsFromProjectOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.Tokenization.FSharpTokenInfo[],FSharp.Compiler.Tokenization.FSharpTokenizerLexState] TokenizeLine(System.String, FSharp.Compiler.Tokenization.FSharpTokenizerLexState) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearCache(System.Collections.Generic.IEnumerable`1[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearCache(System.Collections.Generic.IEnumerable`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateAll() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateConfiguration(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2294,6 +2300,101 @@ FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_baggage() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_itemSpec() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String itemSpec FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.String],System.String], System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean IsSignatureFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean get_IsSignatureFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot Create(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot CreateFromFileSystem(System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String FileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String GetFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String Version +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String get_FileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String get_Version() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew] GetSource() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Boolean Equals(FSharpProjectIdentifier) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: FSharpProjectIdentifier NewFSharpProjectIdentifier(System.String, System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 CompareTo(FSharpProjectIdentifier) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 CompareTo(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 Tag +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 get_Tag() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_outputFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_projectFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String outputFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String projectFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier Identifier +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier get_Identifier() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Replace(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]]], Microsoft.FSharp.Core.FSharpOption`1[System.Collections.Generic.Dictionary`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, System.String, Int32, FSharp.Compiler.Text.ISourceText) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.String Label +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.String get_Label() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+FSharpReference: FSharpProjectSnapshot get_snapshot() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+FSharpReference: FSharpProjectSnapshot snapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+FSharpReference: System.String get_projectOutputFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+FSharpReference: System.String projectOutputFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.AbstractIL.ILBinaryReader+ILModuleReader] getReader +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.AbstractIL.ILBinaryReader+ILModuleReader] get_getReader() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime] getStamp +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime] get_getStamp() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference: System.String get_projectOutputFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference: System.String projectOutputFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+PEReference: FSharp.Compiler.CodeAnalysis.DelayedILModuleReader delayedReader +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+PEReference: FSharp.Compiler.CodeAnalysis.DelayedILModuleReader get_delayedReader() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+PEReference: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime] getStamp +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+PEReference: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime] get_getStamp() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+Tags: Int32 FSharpReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+Tags: Int32 ILModuleReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+Tags: Int32 PEReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean IsFSharpReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean IsILModuleReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean IsPEReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean get_IsFSharpReference() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean get_IsILModuleReference() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean get_IsPEReference() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+FSharpReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+PEReference +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+Tags +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpReferencedProjectSnapshot CreateFSharp(System.String, FSharpProjectSnapshot) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpReferencedProjectSnapshot NewFSharpReference(System.String, FSharpProjectSnapshot) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpReferencedProjectSnapshot NewILModuleReference(System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.AbstractIL.ILBinaryReader+ILModuleReader]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpReferencedProjectSnapshot NewPEReference(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime], FSharp.Compiler.CodeAnalysis.DelayedILModuleReader) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Int32 Tag +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Int32 get_Tag() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String OutputFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String get_OutputFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(ReferenceOnDisk) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 CompareTo(ReferenceOnDisk) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 CompareTo(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.DateTime LastModified +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.DateTime get_LastModified() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String Path +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String get_Path() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Void .ctor(System.String, System.DateTime) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk FSharp.Compiler.CompilerEnvironment: Boolean IsCheckerSupportedSubcategory(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsCompilable(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsScriptFile(System.String) @@ -4250,7 +4351,6 @@ FSharp.Compiler.EditorServices.TupledArgumentLocation: Int32 GetHashCode() FSharp.Compiler.EditorServices.TupledArgumentLocation: Int32 GetHashCode(System.Collections.IEqualityComparer) FSharp.Compiler.EditorServices.TupledArgumentLocation: System.String ToString() FSharp.Compiler.EditorServices.TupledArgumentLocation: Void .ctor(Boolean, FSharp.Compiler.Text.Range) -FSharp.Compiler.EditorServices.UnnecessaryParentheses: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] getUnnecessaryParentheses(Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.String], FSharp.Compiler.Syntax.ParsedInput) FSharp.Compiler.EditorServices.UnresolvedSymbol: Boolean Equals(FSharp.Compiler.EditorServices.UnresolvedSymbol) FSharp.Compiler.EditorServices.UnresolvedSymbol: Boolean Equals(System.Object) FSharp.Compiler.EditorServices.UnresolvedSymbol: Boolean Equals(System.Object, System.Collections.IEqualityComparer) @@ -4768,8 +4868,10 @@ FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp. FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpStaticParameter] get_StaticParameters() FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpType] AllInterfaces FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpType] DeclaredInterfaces +FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpType] GenericArguments FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpType] get_AllInterfaces() FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpType] get_DeclaredInterfaces() +FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpType] get_GenericArguments() FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpUnionCase] UnionCases FSharp.Compiler.Symbols.FSharpEntity: System.Collections.Generic.IList`1[FSharp.Compiler.Symbols.FSharpUnionCase] get_UnionCases() FSharp.Compiler.Symbols.FSharpEntity: System.String AccessPath @@ -5797,6 +5899,12 @@ FSharp.Compiler.Syntax.ParsedInput: Microsoft.FSharp.Collections.FSharpSet`1[Sys FSharp.Compiler.Syntax.ParsedInput: System.String FileName FSharp.Compiler.Syntax.ParsedInput: System.String ToString() FSharp.Compiler.Syntax.ParsedInput: System.String get_FileName() +FSharp.Compiler.Syntax.ParsedInputModule: Boolean exists(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode],Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SyntaxNode,System.Boolean]], FSharp.Compiler.Text.Position, FSharp.Compiler.Syntax.ParsedInput) +FSharp.Compiler.Syntax.ParsedInputModule: Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`2[FSharp.Compiler.Syntax.SyntaxNode,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode]]] tryNode(FSharp.Compiler.Text.Position, FSharp.Compiler.Syntax.ParsedInput) +FSharp.Compiler.Syntax.ParsedInputModule: Microsoft.FSharp.Core.FSharpOption`1[T] tryPickLast[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode],Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SyntaxNode,Microsoft.FSharp.Core.FSharpOption`1[T]]], FSharp.Compiler.Text.Position, FSharp.Compiler.Syntax.ParsedInput) +FSharp.Compiler.Syntax.ParsedInputModule: Microsoft.FSharp.Core.FSharpOption`1[T] tryPick[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode],Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SyntaxNode,Microsoft.FSharp.Core.FSharpOption`1[T]]], FSharp.Compiler.Text.Position, FSharp.Compiler.Syntax.ParsedInput) +FSharp.Compiler.Syntax.ParsedInputModule: State foldWhile[State](Microsoft.FSharp.Core.FSharpFunc`2[State,Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode],Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SyntaxNode,Microsoft.FSharp.Core.FSharpOption`1[State]]]], State, FSharp.Compiler.Syntax.ParsedInput) +FSharp.Compiler.Syntax.ParsedInputModule: State fold[State](Microsoft.FSharp.Core.FSharpFunc`2[State,Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode],Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SyntaxNode,State]]], State, FSharp.Compiler.Syntax.ParsedInput) FSharp.Compiler.Syntax.ParsedScriptInteraction: FSharp.Compiler.Syntax.ParsedScriptInteraction NewDefinitions(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynModuleDecl], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.ParsedScriptInteraction: FSharp.Compiler.Text.Range get_range() FSharp.Compiler.Syntax.ParsedScriptInteraction: FSharp.Compiler.Text.Range range @@ -7351,6 +7459,7 @@ FSharp.Compiler.Syntax.SynExprAndBang: FSharp.Compiler.Text.Range range FSharp.Compiler.Syntax.SynExprAndBang: Int32 Tag FSharp.Compiler.Syntax.SynExprAndBang: Int32 get_Tag() FSharp.Compiler.Syntax.SynExprAndBang: System.String ToString() +FSharp.Compiler.Syntax.SynExprModule: Boolean shouldBeParenthesizedInContext(Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], FSharp.Compiler.Syntax.SynExpr) FSharp.Compiler.Syntax.SynExprRecordField: FSharp.Compiler.Syntax.SynExprRecordField NewSynExprRecordField(System.Tuple`2[FSharp.Compiler.Syntax.SynLongIdent,System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynExpr], Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`2[FSharp.Compiler.Text.Range,Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Position]]]) FSharp.Compiler.Syntax.SynExprRecordField: Int32 Tag FSharp.Compiler.Syntax.SynExprRecordField: Int32 get_Tag() @@ -7610,8 +7719,8 @@ FSharp.Compiler.Syntax.SynMemberDefn+GetSetMember: Microsoft.FSharp.Core.FSharpO FSharp.Compiler.Syntax.SynMemberDefn+GetSetMember: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynBinding] get_memberDefnForSet() FSharp.Compiler.Syntax.SynMemberDefn+GetSetMember: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynBinding] memberDefnForGet FSharp.Compiler.Syntax.SynMemberDefn+GetSetMember: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynBinding] memberDefnForSet -FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.Syntax.SynSimplePats ctorArgs -FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.Syntax.SynSimplePats get_ctorArgs() +FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.Syntax.SynPat ctorArgs +FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.Syntax.SynPat get_ctorArgs() FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.SyntaxTrivia.SynMemberDefnImplicitCtorTrivia get_trivia() FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.SyntaxTrivia.SynMemberDefnImplicitCtorTrivia trivia FSharp.Compiler.Syntax.SynMemberDefn+ImplicitCtor: FSharp.Compiler.Text.Range get_range() @@ -7711,7 +7820,7 @@ FSharp.Compiler.Syntax.SynMemberDefn: Boolean get_IsValField() FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewAbstractSlot(FSharp.Compiler.Syntax.SynValSig, FSharp.Compiler.Syntax.SynMemberFlags, FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynMemberDefnAbstractSlotTrivia) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewAutoProperty(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList], Boolean, FSharp.Compiler.Syntax.Ident, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynType], FSharp.Compiler.Syntax.SynMemberKind, FSharp.Compiler.Syntax.SynMemberFlags, FSharp.Compiler.Syntax.SynMemberFlags, FSharp.Compiler.Xml.PreXmlDoc, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess], FSharp.Compiler.Syntax.SynExpr, FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynMemberDefnAutoPropertyTrivia) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewGetSetMember(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynBinding], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynBinding], FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynMemberGetSetTrivia) -FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewImplicitCtor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList], FSharp.Compiler.Syntax.SynSimplePats, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident], FSharp.Compiler.Xml.PreXmlDoc, FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynMemberDefnImplicitCtorTrivia) +FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewImplicitCtor(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList], FSharp.Compiler.Syntax.SynPat, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident], FSharp.Compiler.Xml.PreXmlDoc, FSharp.Compiler.Text.Range, FSharp.Compiler.SyntaxTrivia.SynMemberDefnImplicitCtorTrivia) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewImplicitInherit(FSharp.Compiler.Syntax.SynType, FSharp.Compiler.Syntax.SynExpr, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewInherit(FSharp.Compiler.Syntax.SynType, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynMemberDefn: FSharp.Compiler.Syntax.SynMemberDefn NewInterface(FSharp.Compiler.Syntax.SynType, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.Range], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynMemberDefn]], FSharp.Compiler.Text.Range) @@ -8364,6 +8473,7 @@ FSharp.Compiler.Syntax.SynPat: FSharp.Compiler.Text.Range get_Range() FSharp.Compiler.Syntax.SynPat: Int32 Tag FSharp.Compiler.Syntax.SynPat: Int32 get_Tag() FSharp.Compiler.Syntax.SynPat: System.String ToString() +FSharp.Compiler.Syntax.SynPatModule: Boolean shouldBeParenthesizedInContext(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], FSharp.Compiler.Syntax.SynPat) FSharp.Compiler.Syntax.SynRationalConst+Integer: FSharp.Compiler.Text.Range get_range() FSharp.Compiler.Syntax.SynRationalConst+Integer: FSharp.Compiler.Text.Range range FSharp.Compiler.Syntax.SynRationalConst+Integer: Int32 get_value() @@ -9177,8 +9287,8 @@ FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Collectio FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[FSharp.Compiler.Syntax.SynValSig,FSharp.Compiler.Syntax.SynMemberFlags]] slotsigs FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Syntax.SynType,FSharp.Compiler.Text.Range,Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident]]] get_inherits() FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Syntax.SynType,FSharp.Compiler.Text.Range,Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident]]] inherits -FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynSimplePats] get_implicitCtorSynPats() -FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynSimplePats] implicitCtorSynPats +FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynPat] get_implicitCtorSynPats() +FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+General: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynPat] implicitCtorSynPats FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+LibraryOnlyILAssembly: FSharp.Compiler.Text.Range get_range() FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+LibraryOnlyILAssembly: FSharp.Compiler.Text.Range range FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr+LibraryOnlyILAssembly: System.Object get_ilType() @@ -9229,7 +9339,7 @@ FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: Boolean get_IsTypeAbbrev() FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: Boolean get_IsUnion() FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewEnum(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynEnumCase], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewException(FSharp.Compiler.Syntax.SynExceptionDefnRepr) -FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewGeneral(FSharp.Compiler.Syntax.SynTypeDefnKind, Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Syntax.SynType,FSharp.Compiler.Text.Range,Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident]]], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[FSharp.Compiler.Syntax.SynValSig,FSharp.Compiler.Syntax.SynMemberFlags]], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynField], Boolean, Boolean, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynSimplePats], FSharp.Compiler.Text.Range) +FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewGeneral(FSharp.Compiler.Syntax.SynTypeDefnKind, Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Syntax.SynType,FSharp.Compiler.Text.Range,Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.Ident]]], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[FSharp.Compiler.Syntax.SynValSig,FSharp.Compiler.Syntax.SynMemberFlags]], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynField], Boolean, Boolean, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynPat], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewLibraryOnlyILAssembly(System.Object, FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewNone(FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr: FSharp.Compiler.Syntax.SynTypeDefnSimpleRepr NewRecord(Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynAccess], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynField], FSharp.Compiler.Text.Range) @@ -9454,9 +9564,12 @@ FSharp.Compiler.Syntax.SyntaxNode: FSharp.Compiler.Syntax.SyntaxNode+SynTypeDefn FSharp.Compiler.Syntax.SyntaxNode: FSharp.Compiler.Syntax.SyntaxNode+SynTypeDefnSig FSharp.Compiler.Syntax.SyntaxNode: FSharp.Compiler.Syntax.SyntaxNode+SynValSig FSharp.Compiler.Syntax.SyntaxNode: FSharp.Compiler.Syntax.SyntaxNode+Tags +FSharp.Compiler.Syntax.SyntaxNode: FSharp.Compiler.Text.Range Range +FSharp.Compiler.Syntax.SyntaxNode: FSharp.Compiler.Text.Range get_Range() FSharp.Compiler.Syntax.SyntaxNode: Int32 Tag FSharp.Compiler.Syntax.SyntaxNode: Int32 get_Tag() FSharp.Compiler.Syntax.SyntaxNode: System.String ToString() +FSharp.Compiler.Syntax.SyntaxNodeModule: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynAttributeList] |Attributes|(FSharp.Compiler.Syntax.SyntaxNode) FSharp.Compiler.Syntax.SyntaxTraversal: Microsoft.FSharp.Core.FSharpOption`1[T] Traverse[T](FSharp.Compiler.Text.Position, FSharp.Compiler.Syntax.ParsedInput, FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitAttributeApplication(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], FSharp.Compiler.Syntax.SynAttributeList) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitBinding(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SynBinding,Microsoft.FSharp.Core.FSharpOption`1[T]], FSharp.Compiler.Syntax.SynBinding) @@ -9476,7 +9589,7 @@ FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOptio FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitPat(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SynPat,Microsoft.FSharp.Core.FSharpOption`1[T]], FSharp.Compiler.Syntax.SynPat) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitRecordDefn(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynField], FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitRecordField(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynExpr], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Syntax.SynLongIdent]) -FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitSimplePats(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynSimplePat]) +FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitSimplePats(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], FSharp.Compiler.Syntax.SynPat) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitType(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.Syntax.SynType,Microsoft.FSharp.Core.FSharpOption`1[T]], FSharp.Compiler.Syntax.SynType) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitTypeAbbrev(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], FSharp.Compiler.Syntax.SynType, FSharp.Compiler.Text.Range) FSharp.Compiler.Syntax.SyntaxVisitorBase`1[T]: Microsoft.FSharp.Core.FSharpOption`1[T] VisitUnionDefn(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SyntaxNode], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Syntax.SynUnionCase], FSharp.Compiler.Text.Range) @@ -10193,6 +10306,7 @@ FSharp.Compiler.Text.ISourceText: System.String GetSubTextFromRange(FSharp.Compi FSharp.Compiler.Text.ISourceText: System.String GetSubTextString(Int32, Int32) FSharp.Compiler.Text.ISourceText: System.Tuple`2[System.Int32,System.Int32] GetLastCharacterPosition() FSharp.Compiler.Text.ISourceText: Void CopyTo(Int32, Char[], Int32, Int32) +FSharp.Compiler.Text.ISourceTextNew: System.Collections.Immutable.ImmutableArray`1[System.Byte] GetChecksum() FSharp.Compiler.Text.Line: Int32 fromZ(Int32) FSharp.Compiler.Text.Line: Int32 toZ(Int32) FSharp.Compiler.Text.NavigableTaggedText: FSharp.Compiler.Text.Range Range @@ -10272,6 +10386,8 @@ FSharp.Compiler.Text.RangeModule: System.Tuple`2[System.String,System.Tuple`2[Sy FSharp.Compiler.Text.RangeModule: System.Tuple`2[System.Tuple`2[System.Int32,System.Int32],System.Tuple`2[System.Int32,System.Int32]] toZ(FSharp.Compiler.Text.Range) FSharp.Compiler.Text.RangeModule: Void outputRange(System.IO.TextWriter, FSharp.Compiler.Text.Range) FSharp.Compiler.Text.SourceText: FSharp.Compiler.Text.ISourceText ofString(System.String) +FSharp.Compiler.Text.SourceTextNew: FSharp.Compiler.Text.ISourceTextNew ofISourceText(FSharp.Compiler.Text.ISourceText) +FSharp.Compiler.Text.SourceTextNew: FSharp.Compiler.Text.ISourceTextNew ofString(System.String) FSharp.Compiler.Text.TaggedText: FSharp.Compiler.Text.TextTag Tag FSharp.Compiler.Text.TaggedText: FSharp.Compiler.Text.TextTag get_Tag() FSharp.Compiler.Text.TaggedText: System.String Text diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj index 909402a2a61..780ba10f3f4 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj @@ -92,7 +92,8 @@ - + + Program.fs diff --git a/tests/FSharp.Compiler.Service.Tests/SynExprTests.fs b/tests/FSharp.Compiler.Service.Tests/SynExprTests.fs new file mode 100644 index 00000000000..1e402a1d252 --- /dev/null +++ b/tests/FSharp.Compiler.Service.Tests/SynExprTests.fs @@ -0,0 +1,88 @@ +module FSharp.Compiler.Syntax.Tests.SynExpr + +open FSharp.Compiler.Service.Tests.Common +open FSharp.Compiler.Syntax +open FSharp.Compiler.Text +open NUnit.Framework + +type Parenthesization = Needed | Unneeded + +module Parenthesization = + let ofBool shouldParenthesize = + if shouldParenthesize then Needed + else Unneeded + +let exprs: obj array list = + [ + [|([] : Parenthesization list); "()"|] + [|[Needed]; "(1 + 2) * 3"|] + [|[Unneeded]; "1 + (2 * 3)"|] + [|[Unneeded]; "1 * (2 * 3)"|] + [|[Unneeded]; "(1 * 2) * 3"|] + [|[Needed]; "1 / (2 / 3)"|] + [|[Unneeded]; "(1 / 2) / 3"|] + [|[Unneeded]; "(printfn \"Hello, world.\")"|] + [|[Needed]; "let (~-) x = x in id -(<@ 3 @>)"|] + [|[Unneeded; Unneeded]; "let (~-) x = x in id (-(<@ 3 @>))"|] + [|[Unneeded]; "(())"|] + [|[Unneeded]; "(3)"|] + [|[Needed]; + " + let x = (x + + y) + in x + " + |] + [|[Unneeded]; + " + let x = (x + + y) + in x + " + |] + [|[Needed]; + " + async { + return ( + 1 + ) + } + " + |] + [|[Unneeded]; + " + async { + return ( + 1 + ) + } + " + |] + ] + +#if !NET6_0_OR_GREATER +open System + +type String with + // This is not a true polyfill, but it suffices for the .NET Framework target. + member this.ReplaceLineEndings() = this.Replace("\r", "") +#endif + +// `expected` represents whether each parenthesized expression, from the inside outward, requires its parentheses. +[] +let shouldBeParenthesizedInContext (expected: Parenthesization list) src = + let ast = getParseResults src + + let getSourceLineStr = + let lines = src.ReplaceLineEndings().Split '\n' + Line.toZ >> Array.get lines + + let actual = + ([], ast) + ||> ParsedInput.fold (fun actual path node -> + match node, path with + | SyntaxNode.SynExpr expr, SyntaxNode.SynExpr(SynExpr.Paren _) :: path -> + Parenthesization.ofBool (SynExpr.shouldBeParenthesizedInContext getSourceLineStr path expr) :: actual + | _ -> actual) + + CollectionAssert.AreEqual(expected, actual) diff --git a/tests/FSharp.Compiler.Service.Tests/SynPatTests.fs b/tests/FSharp.Compiler.Service.Tests/SynPatTests.fs new file mode 100644 index 00000000000..42a5e8711e0 --- /dev/null +++ b/tests/FSharp.Compiler.Service.Tests/SynPatTests.fs @@ -0,0 +1,36 @@ +module FSharp.Compiler.Syntax.Tests.SynPat + +open FSharp.Compiler.Service.Tests.Common +open FSharp.Compiler.Syntax +open NUnit.Framework + +type Parenthesization = Needed | Unneeded + +module Parenthesization = + let ofBool shouldParenthesize = + if shouldParenthesize then Needed + else Unneeded + +let pats: obj array list = + [ + [|[Needed]; "match () with () -> ()"|] + [|[Needed]; "let (Lazy x) = lazy 1"|] + [|[Unneeded; Unneeded]; "let ((Lazy x)) = lazy 1"|] + [|[Needed; Unneeded]; "let (()) = ()"|] + [|[Needed; Unneeded; Unneeded]; "let ((())) = ()"|] + ] + +// `expected` represents whether each parenthesized pattern, from the inside outward, requires its parentheses. +[] +let shouldBeParenthesizedInContext (expected: Parenthesization list) src = + let ast = getParseResults src + + let actual = + ([], ast) + ||> ParsedInput.fold (fun actual path node -> + match node, path with + | SyntaxNode.SynPat pat, SyntaxNode.SynPat(SynPat.Paren _) :: path -> + Parenthesization.ofBool (SynPat.shouldBeParenthesizedInContext path pat) :: actual + | _ -> actual) + + CollectionAssert.AreEqual(expected, actual) \ No newline at end of file diff --git a/tests/FSharp.Compiler.Service.Tests/UnnecessaryParenthesesTests.fs b/tests/FSharp.Compiler.Service.Tests/UnnecessaryParenthesesTests.fs deleted file mode 100644 index 4ccbe93e470..00000000000 --- a/tests/FSharp.Compiler.Service.Tests/UnnecessaryParenthesesTests.fs +++ /dev/null @@ -1,52 +0,0 @@ -module FSharp.Compiler.EditorServices.Tests.UnnecessaryParenthesesTests - -open FSharp.Compiler.EditorServices -open FSharp.Compiler.Service.Tests.Common -open NUnit.Framework - -let noUnneededParens = - [ - "printfn \"Hello, world.\"" - "()" - "(1 + 2) * 3" - "let (~-) x = x in id -(<@ 3 @>)" - ] - -[] -let ``No results returned when there are no unnecessary parentheses`` src = - task { - let ast = getParseResults src - let! unnecessaryParentheses = UnnecessaryParentheses.getUnnecessaryParentheses (fun _ -> src) ast - Assert.IsEmpty unnecessaryParentheses - } - -let unneededParens = - [ - "(printfn \"Hello, world.\")" - "(())" - "(1 * 2) * 3" - "let (~-) x = x in -(<@ 3 @>)" - ] - -[] -let ``Results returned when there are unnecessary parentheses`` src = - task { - let ast = getParseResults src - let! unnecessaryParentheses = UnnecessaryParentheses.getUnnecessaryParentheses (fun _ -> src) ast - Assert.AreEqual(1, Seq.length unnecessaryParentheses, $"Expected one range but got: %A{unnecessaryParentheses}.") - } - -let nestedUnneededParens = - [ - "((printfn \"Hello, world.\"))" - "((3))" - "let (~-) x = x in id (-(<@ 3 @>))" - ] - -[] -let ``Results returned for nested, potentially mutually-exclusive, unnecessary parentheses`` src = - task { - let ast = getParseResults src - let! unnecessaryParentheses = UnnecessaryParentheses.getUnnecessaryParentheses (fun _ -> src) ast - Assert.AreEqual(2, Seq.length unnecessaryParentheses, $"Expected two ranges but got: %A{unnecessaryParentheses}.") - } diff --git a/tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs b/tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs index 75f53eb6e5c..556a9b5bc42 100644 --- a/tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs +++ b/tests/FSharp.Compiler.UnitTests/BuildGraphTests.fs @@ -8,6 +8,7 @@ open Xunit open FSharp.Test open FSharp.Test.Compiler open FSharp.Compiler.BuildGraph +open FSharp.Compiler.DiagnosticsLogger open Internal.Utilities.Library module BuildGraphTests = @@ -233,3 +234,42 @@ module BuildGraphTests = Assert.shouldBeTrue graphNode.HasValue Assert.shouldBe (ValueSome 1) (graphNode.TryPeekValue()) + + + [] + let internal ``NodeCode preserves DiagnosticsThreadStatics`` () = + let random = + let rng = Random() + fun n -> rng.Next n + + let job phase _ = node { + do! random 10 |> Async.Sleep |> NodeCode.AwaitAsync + Assert.Equal(phase, DiagnosticsThreadStatics.BuildPhase) + } + + let work (phase: BuildPhase) = + node { + use _ = new CompilationGlobalsScope(DiscardErrorsLogger, phase) + let! _ = Seq.init 8 (job phase) |> NodeCode.Parallel + Assert.Equal(phase, DiagnosticsThreadStatics.BuildPhase) + } + + let phases = [| + BuildPhase.DefaultPhase + BuildPhase.Compile + BuildPhase.Parameter + BuildPhase.Parse + BuildPhase.TypeCheck + BuildPhase.CodeGen + BuildPhase.Optimize + BuildPhase.IlxGen + BuildPhase.IlGen + BuildPhase.Output + BuildPhase.Interactive + |] + + let pickRandomPhase _ = phases[random phases.Length] + Seq.init 100 pickRandomPhase + |> Seq.map (work >> Async.AwaitNodeCode) + |> Async.Parallel + |> Async.RunSynchronously diff --git a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj index 915332ac4ac..0e4b91b42a7 100644 --- a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj +++ b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj @@ -74,6 +74,7 @@ CompilerService\ServiceUntypedParseTests.fs + diff --git a/tests/FSharp.Compiler.UnitTests/ParsedInputModuleTests.fs b/tests/FSharp.Compiler.UnitTests/ParsedInputModuleTests.fs new file mode 100644 index 00000000000..635c000f4af --- /dev/null +++ b/tests/FSharp.Compiler.UnitTests/ParsedInputModuleTests.fs @@ -0,0 +1,449 @@ +module Tests.Service.ParsedInputModuleTests + +open FSharp.Compiler.Service.Tests.Common +open FSharp.Compiler.Syntax +open FSharp.Compiler.Text.Position +open Xunit + +[] +let ``tryPick type test`` () = + let source = "123 :? int" + let parseTree = parseSourceCode ("C:\\test.fs", source) + + (mkPos 1 11, parseTree) + ||> ParsedInput.tryPick (fun _path node -> match node with SyntaxNode.SynType _ -> Some() | _ -> None) + |> Option.defaultWith (fun _ -> failwith "Did not visit type") + + (mkPos 1 3, parseTree) + ||> ParsedInput.tryPick (fun _path node -> match node with SyntaxNode.SynType _ -> Some() | _ -> None) + |> Option.iter (fun _ -> failwith "Should not visit type") + +[] +let ``tryPick record definition test`` () = + let source = "type R = { A: int; B: string }" + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let fields = + (pos0, parseTree) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynTypeDefn(SynTypeDefn(typeRepr = SynTypeDefnRepr.Simple(SynTypeDefnSimpleRepr.Record(recordFields = fields), _))) -> Some fields + | _ -> None) + + match fields with + | Some [ SynField (idOpt = Some id1); SynField (idOpt = Some id2) ] when id1.idText = "A" && id2.idText = "B" -> () + | _ -> failwith "Did not visit record definition" + +[] +let ``tryPick union definition test`` () = + let source = "type U = A | B of string" + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let cases = + (pos0, parseTree) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynTypeDefn(SynTypeDefn(typeRepr = SynTypeDefnRepr.Simple(SynTypeDefnSimpleRepr.Union(unionCases = cases), _))) -> Some cases + | _ -> None) + + match cases with + | Some [ SynUnionCase (ident = SynIdent(id1,_)); SynUnionCase (ident = SynIdent(id2,_)) ] when id1.idText = "A" && id2.idText = "B" -> () + | _ -> failwith "Did not visit union definition" + +[] +let ``tryPick enum definition test`` () = + let source = "type E = A = 0 | B = 1" + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let cases = + (pos0, parseTree) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynTypeDefn(SynTypeDefn(typeRepr = SynTypeDefnRepr.Simple(SynTypeDefnSimpleRepr.Enum(cases = cases), _))) -> Some cases + | _ -> None) + + match cases with + | Some [ SynEnumCase (ident = SynIdent (id1, _)); SynEnumCase (ident = SynIdent (id2, _)) ] when id1.idText = "A" && id2.idText = "B" -> () + | _ -> failwith "Did not visit enum definition" + +[] +let ``tryPick recursive let binding`` () = + let source = "let rec fib n = if n < 2 then n else fib (n - 1) + fib (n - 2) in fib 10" + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let bindings = + (pos0, parseTree) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynExpr(SynExpr.LetOrUse(isRecursive = false)) -> failwith "isRecursive should be true" + | SyntaxNode.SynExpr(SynExpr.LetOrUse(isRecursive = true; bindings = bindings)) -> Some bindings + | _ -> None) + + match bindings with + | Some [ SynBinding(valData = SynValData(valInfo = SynValInfo(curriedArgInfos = [ [ SynArgInfo(ident = Some id) ] ]))) ] when id.idText = "n" -> () + | _ -> failwith "Did not visit recursive let binding" + +[] +let ``tryPick ValSig`` () = + let source = """ +module X + +val y: int -> int +""" + + let parseTree = parseSourceCode ("C:\\test.fsi", source) + + let ident = + (pos0, parseTree) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynValSig(SynValSig(ident = SynIdent(ident = ident))) -> Some ident.idText + | _ -> None) + + match ident with + | Some "y" -> () + | _ -> failwith "Did not visit SynValSig" + +[] +let ``tryPick nested ValSig`` () = + let source = """ +module X + +module Y = + val z: int -> int +""" + + let parseTree = parseSourceCode ("C:\\test.fsi", source) + + let ident = + (pos0, parseTree) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynValSig(SynValSig(ident = SynIdent(ident = ident))) -> Some ident.idText + | _ -> None) + + match ident with + | Some "z" -> () + | _ -> failwith "Did not visit SynValSig" + +[] +let ``tryPick Record in SynTypeDefnSig`` () = + let source = """ +module X + +type Y = + { + A: int + B: char + C: string + } +""" + + let parseTree = parseSourceCode ("C:\\test.fsi", source) + + let ident = + (pos0, parseTree) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynTypeDefnSig(SynTypeDefnSig(typeRepr = SynTypeDefnSigRepr.Simple(SynTypeDefnSimpleRepr.Record(recordFields = fields), _))) -> + fields + |> List.choose (function SynField(idOpt = Some ident) -> Some ident.idText | _ -> None) + |> String.concat "," + |> Some + | _ -> None) + + match ident with + | Some "A,B,C" -> () + | _ -> failwith "Did not visit SynTypeDefnSimpleRepr.Record in SynTypeDefnSig" + +[] +let ``tryPick SynValSig in SynMemberSig`` () = + let source = """ +module Lib + +type Meh = + new: unit -> Meh + member Foo: y: int -> int +""" + + let parseTree = parseSourceCode ("C:\\test.fsi", source) + let pos = mkPos 6 4 + + let ident = + (pos, parseTree) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynValSig(SynValSig(ident = SynIdent(ident = valIdent))) -> Some valIdent.idText + | _ -> None) + + match ident with + | Some "Foo" -> () + | _ -> failwith "Did not visit SynValSig in SynMemberSig.Member" + +[] +let ``tryPick picks the first matching node`` () = + let source = """ +module M + +module N = + module O = + module P = begin end +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let ``module`` = + (mkPos 6 28, parseTree) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longIdent))) -> + Some(longIdent |> List.map (fun ident -> ident.idText)) + | _ -> None) + + Assert.Equal(Some ["N"], ``module``) + +[] +let ``tryPick falls back to the nearest matching node to the left if pos is out of range`` () = + let source = """ +module M + +module N = + module O = + module P = begin end +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let ``module`` = + (mkPos 7 30, parseTree) + ||> ParsedInput.tryPick (fun _path node -> + match node with + | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longIdent))) -> + Some(longIdent |> List.map (fun ident -> ident.idText)) + | _ -> None) + + Assert.Equal(Some ["N"], ``module``) + +[] +let ``tryPickLast picks the last matching node`` () = + let source = """ +module M + +module N = + module O = + module P = begin end +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let ``module`` = + (mkPos 6 28, parseTree) + ||> ParsedInput.tryPickLast (fun _path node -> + match node with + | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longIdent))) -> + Some(longIdent |> List.map (fun ident -> ident.idText)) + | _ -> None) + + Assert.Equal(Some ["P"], ``module``) + +[] +let ``tryPickLast falls back to the nearest matching node to the left if pos is out of range`` () = + let source = """ +module M + +module N = + module O = + module P = begin end +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let ``module`` = + (mkPos 7 30, parseTree) + ||> ParsedInput.tryPickLast (fun _path node -> + match node with + | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longIdent))) -> + Some(longIdent |> List.map (fun ident -> ident.idText)) + | _ -> None) + + Assert.Equal(Some ["P"], ``module``) + +[] +let ``exists returns true for the first matching node`` () = + let source = """ +module M + +module N = + module O = + module P = begin end +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let mutable start = 0, 0 + + let found = + (mkPos 6 28, parseTree) + ||> ParsedInput.exists (fun _path node -> + match node with + | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longIdent))) -> + start <- node.Range.StartLine, node.Range.StartColumn + true + | _ -> false) + + Assert.True found + Assert.Equal((4, 0), start) + +[] +let ``exists falls back to the nearest matching node to the left if pos is out of range`` () = + let source = """ +module M + +module N = + module O = + module P = begin end +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let mutable start = 0, 0 + + let found = + (mkPos 7 30, parseTree) + ||> ParsedInput.exists (fun _path node -> + match node with + | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longIdent))) -> + start <- node.Range.StartLine, node.Range.StartColumn + true + | _ -> false) + + Assert.True found + Assert.Equal((4, 0), start) + +[] +let ``tryNode picks the last node containing the given position`` () = + let source = """ +module M + +module N = + module O = + module P = begin end +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let ``module`` = + parseTree + |> ParsedInput.tryNode (mkPos 6 28) + |> Option.bind (fun (node, _path) -> + match node with + | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longIdent))) -> + Some(longIdent |> List.map (fun ident -> ident.idText)) + | _ -> None) + + Assert.Equal(Some ["P"], ``module``) + +[] +let ``tryNode returns None if no node contains the given position`` () = + let source = """ +module M + +module N = + module O = + module P = begin end +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let ``module`` = parseTree |> ParsedInput.tryNode (mkPos 6 30) + + Assert.Equal(None, ``module``) + +[] +let ``fold traverses nodes in order`` () = + let source = """ +module M + +module N = + module O = + module P = begin end + +module Q = + module R = + module S = begin end +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let modules = + ([], parseTree) + ||> ParsedInput.fold (fun acc _path node -> + match node with + | SyntaxNode.SynModuleOrNamespace(SynModuleOrNamespace(longId = longIdent)) + | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longIdent))) -> + (longIdent |> List.map (fun ident -> ident.idText)) :: acc + | _ -> acc) + + Assert.Equal( + [["M"]; ["N"]; ["O"]; ["P"]; ["Q"]; ["R"]; ["S"]], + List.rev modules) + +[] +let ``foldWhile traverses nodes in order`` () = + let source = """ +module M + +module N = + module O = + module P = begin end + +module Q = + module R = + module S = begin end +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let modules = + ([], parseTree) + ||> ParsedInput.foldWhile (fun acc _path node -> + match node with + | SyntaxNode.SynModuleOrNamespace(SynModuleOrNamespace(longId = longIdent)) + | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longIdent))) -> + Some((longIdent |> List.map (fun ident -> ident.idText)) :: acc) + | _ -> Some acc) + + Assert.Equal( + [["M"]; ["N"]; ["O"]; ["P"]; ["Q"]; ["R"]; ["S"]], + List.rev modules) + +[] +let ``foldWhile traverses nodes in order until the folder returns None`` () = + let source = """ +module M + +module N = + module O = + module P = begin end + +module Q = + module R = + module S = begin end +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + let modules = + ([], parseTree) + ||> ParsedInput.foldWhile (fun acc _path node -> + if posGt node.Range.Start (mkPos 7 0) then None + else + match node with + | SyntaxNode.SynModuleOrNamespace(SynModuleOrNamespace(longId = longIdent)) + | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longIdent))) -> + Some((longIdent |> List.map (fun ident -> ident.idText)) :: acc) + | _ -> Some acc) + + Assert.Equal( + [["M"]; ["N"]; ["O"]; ["P"]], + List.rev modules) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl index 5a36ea3c288..e156eba4598 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl @@ -672,12 +672,14 @@ Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Microsoft.FSharp.Contro Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Microsoft.FSharp.Control.FSharpAsync`1[T] Scan[T](Microsoft.FSharp.Core.FSharpFunc`2[TMsg,Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Control.FSharpAsync`1[T]]], Microsoft.FSharp.Core.FSharpOption`1[System.Int32]) Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Microsoft.FSharp.Control.FSharpHandler`1[System.Exception] Error Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg] Start(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg],Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit]], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) +Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg] StartImmediate(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg],Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit]], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Microsoft.FSharp.Core.FSharpOption`1[TReply] TryPostAndReply[TReply](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1[TReply],TMsg], Microsoft.FSharp.Core.FSharpOption`1[System.Int32]) Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: TReply PostAndReply[TReply](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1[TReply],TMsg], Microsoft.FSharp.Core.FSharpOption`1[System.Int32]) Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void .ctor(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg],Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit]], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void Dispose() Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void Post(TMsg) Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void Start() +Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void StartImmediate() Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void add_Error(Microsoft.FSharp.Control.FSharpHandler`1[System.Exception]) Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void remove_Error(Microsoft.FSharp.Control.FSharpHandler`1[System.Exception]) Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void set_DefaultTimeout(Int32) diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 52c9b033119..46462d0a473 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -27,6 +27,7 @@ open TestFramework open System.Runtime.CompilerServices open System.Runtime.InteropServices +open FSharp.Compiler.CodeAnalysis module rec Compiler = @@ -917,7 +918,7 @@ module rec Compiler = CompilerAssert.TypeCheck(options, fileName, source) | _ -> failwith "Typecheck only supports F#" - let typecheckProject enablePartialTypeChecking (cUnit: CompilationUnit) : FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults = + let typecheckProject enablePartialTypeChecking useTransparentCompiler (cUnit: CompilationUnit) : FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults = match cUnit with | FS fsSource -> let options = fsSource.Options |> Array.ofList @@ -935,7 +936,8 @@ module rec Compiler = |> async.Return let sourceFiles = Array.map fst sourceFiles - CompilerAssert.TypeCheckProject(options, sourceFiles, getSourceText, enablePartialTypeChecking) + + CompilerAssert.TypeCheckProject(options, sourceFiles, getSourceText, enablePartialTypeChecking, useTransparentCompiler) | _ -> failwith "Typecheck only supports F#" let run (result: CompilationResult) : CompilationResult = diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index b5e12d8cd5a..7951ba31d5d 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -11,6 +11,7 @@ open System.Reflection open FSharp.Compiler.Interactive.Shell open FSharp.Compiler.IO open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot open FSharp.Compiler.Diagnostics open FSharp.Compiler.Text #if NETCOREAPP @@ -258,7 +259,8 @@ and Compilation = module rec CompilerAssertHelpers = - let checker = FSharpChecker.Create(suggestNamesForErrors=true) + let useTransparentCompiler = FSharp.Compiler.CompilerConfig.FSharpExperimentalFeaturesEnabledAutomatically + let checker = FSharpChecker.Create(suggestNamesForErrors=true, useTransparentCompiler=useTransparentCompiler) // Unlike C# whose entrypoint is always string[] F# can make an entrypoint with 0 args, or with an array of string[] let mkDefaultArgs (entryPoint:MethodBase) : obj[] = [| @@ -886,14 +888,31 @@ Updated automatically, please check diffs in your pull request, changes must be static member TypeCheckSingleError (source: string) (expectedSeverity: FSharpDiagnosticSeverity) (expectedErrorNumber: int) (expectedErrorRange: int * int * int * int) (expectedErrorMsg: string) = CompilerAssert.TypeCheckWithErrors source [| expectedSeverity, expectedErrorNumber, expectedErrorRange, expectedErrorMsg |] - static member TypeCheckProject(options: string array, sourceFiles: string array, getSourceText, enablePartialTypeChecking) : FSharpCheckProjectResults = - let checker = FSharpChecker.Create(documentSource = DocumentSource.Custom getSourceText, enablePartialTypeChecking = enablePartialTypeChecking) + static member TypeCheckProject(options: string array, sourceFiles: string array, getSourceText, enablePartialTypeChecking, useTransparentCompiler) : FSharpCheckProjectResults = + let checker = FSharpChecker.Create(documentSource = DocumentSource.Custom getSourceText, enablePartialTypeChecking = enablePartialTypeChecking, useTransparentCompiler = useTransparentCompiler) let defaultOptions = defaultProjectOptions TargetFramework.Current let projectOptions = { defaultOptions with OtherOptions = Array.append options defaultOptions.OtherOptions; SourceFiles = sourceFiles } - checker.ParseAndCheckProject(projectOptions) + if useTransparentCompiler then + let getFileSnapshot _ fileName = + async.Return + (FSharpFileSnapshot( + FileName = fileName, + Version = "1", + GetSource = fun () -> task { + match! getSourceText fileName with + | Some source -> return SourceTextNew.ofISourceText source + | None -> return failwith $"couldn't get source for {fileName}" + } + )) + + let snapshot = FSharpProjectSnapshot.FromOptions(projectOptions, getFileSnapshot) |> Async.RunSynchronously + + checker.ParseAndCheckProject(snapshot) + else + checker.ParseAndCheckProject(projectOptions) |> Async.RunImmediate - + static member CompileExeWithOptions(options, (source: SourceCodeFileKind)) = compile true options source (fun (errors, _, _) -> if errors.Length > 0 then diff --git a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj index 6867d180ae9..5cfcba98ca7 100644 --- a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj +++ b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj @@ -73,6 +73,18 @@ + + + + + + + + + + + + @@ -88,6 +100,7 @@ + diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 73f7fd1f0e4..144e535bccb 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -25,6 +25,7 @@ open System.Threading.Tasks open System.Xml open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot open FSharp.Compiler.Diagnostics open FSharp.Compiler.Text @@ -41,7 +42,7 @@ let private projectRoot = "test-projects" let private defaultFunctionName = "f" type Reference = { - Name: string + Name: string Version: string option } module ReferenceHelpers = @@ -68,13 +69,13 @@ module ReferenceHelpers = } |> String.concat "\n" - let runtimeList = lazy ( + let runtimeList = lazy ( // You can see which versions of the .NET runtime are currently installed with the following command. let psi = ProcessStartInfo("dotnet", "--list-runtimes", RedirectStandardOutput = true, UseShellExecute = false) let proc = Process.Start(psi) - proc.WaitForExit() + proc.WaitForExit(1000) |> ignore let output = seq { @@ -92,7 +93,8 @@ module ReferenceHelpers = { Name = matches.Groups.[1].Value Version = version - Path = DirectoryInfo(Path.Combine(matches.Groups[3].Value, version)) })) + Path = DirectoryInfo(Path.Combine(matches.Groups[3].Value, version)) }) + |> Seq.toList) let getFrameworkReference (reference: Reference) = @@ -191,9 +193,13 @@ type SyntheticSourceFile = Source: string ExtraSource: string EntryPoint: bool + /// Indicates whether this is an existing F# file on disk. + IsPhysicalFile: bool } - member this.FileName = $"File{this.Id}.fs" + member this.FileName = + if this.IsPhysicalFile then $"%s{this.Id}.fs" else $"File%s{this.Id}.fs" + member this.SignatureFileName = $"{this.FileName}i" member this.TypeName = $"T{this.Id}V_{this.PublicVersion}" member this.ModuleName = $"Module{this.Id}" @@ -214,7 +220,8 @@ let sourceFile fileId deps = HasErrors = false Source = "" ExtraSource = "" - EntryPoint = false } + EntryPoint = false + IsPhysicalFile = false } let OptionsCache = ConcurrentDictionary() @@ -466,6 +473,76 @@ let private writeFile (p: SyntheticProject) (f: SyntheticSourceFile) = let content = renderSourceFile p f writeFileIfChanged fileName content +/// Creates a SyntheticProject from the compiler arguments found in the response file. +let mkSyntheticProjectForResponseFile (responseFile: FileInfo) : SyntheticProject = + if not responseFile.Exists then + failwith $"%s{responseFile.FullName} does not exist" + + let compilerArgs = File.ReadAllLines responseFile.FullName + + let fsharpFileExtensions = set [| ".fs" ; ".fsi" ; ".fsx" |] + + let isFSharpFile (file : string) = + Set.exists (fun (ext : string) -> file.EndsWith (ext, StringComparison.Ordinal)) fsharpFileExtensions + + let fsharpFiles = + compilerArgs + |> Array.choose (fun (line : string) -> + if not (isFSharpFile line) then + None + else + + let fullPath = Path.Combine (responseFile.DirectoryName, line) + if not (File.Exists fullPath) then + None + else + Some fullPath + ) + |> Array.toList + + let signatureFiles, implementationFiles = + fsharpFiles |> List.partition (fun path -> path.EndsWith ".fsi") + + let signatureFiles = set signatureFiles + + let sourceFiles = + implementationFiles + |> List.map (fun implPath -> + let id = + let fileNameWithoutExtension = Path.GetFileNameWithoutExtension implPath + let directoryOfFile = FileInfo(implPath).DirectoryName + let relativeUri = Uri(responseFile.FullName).MakeRelativeUri(Uri(directoryOfFile)) + let relativeFolderPath = Uri.UnescapeDataString(relativeUri.ToString()).Replace('/', Path.DirectorySeparatorChar) + Path.Combine(relativeFolderPath, fileNameWithoutExtension) + + { + Id = id + PublicVersion = 1 + InternalVersion = 1 + DependsOn = [] + FunctionName = "f" + SignatureFile = + let sigPath = $"%s{implPath}i" in + if signatureFiles.Contains sigPath then Custom(File.ReadAllText sigPath) else No + HasErrors = false + Source = File.ReadAllText implPath + ExtraSource = "" + EntryPoint = false + IsPhysicalFile = true + } + ) + + let otherOptions = + compilerArgs + |> Array.filter (fun line -> not (isFSharpFile line)) + |> Array.toList + + { SyntheticProject.Create(Path.GetFileNameWithoutExtension responseFile.Name) with + ProjectDir = responseFile.DirectoryName + SourceFiles = sourceFiles + OtherOptions = otherOptions + AutoAddModules = false + } [] module ProjectOperations = @@ -540,10 +617,45 @@ module ProjectOperations = filePath |> project.FindByPath |> renderSourceFile project - |> SourceText.ofString + |> SourceTextNew.ofString + + let internal getFileSnapshot (project: SyntheticProject) _options (path: string) = + async { + let project, filePath = + if path.EndsWith(".fsi") then + let implFilePath = path[..path.Length - 2] + let p, f = project.FindInAllProjectsByPath implFilePath + p, getSignatureFilePath p f + else + let p, f = project.FindInAllProjectsByPath path + p, getFilePath p f + + let source = getSourceText project path + use md5 = System.Security.Cryptography.MD5.Create() + let inputBytes = Encoding.UTF8.GetBytes(source.ToString()) + let hash = md5.ComputeHash(inputBytes) |> Array.map (fun b -> b.ToString("X2")) |> String.concat "" + + return FSharpFileSnapshot( + FileName = filePath, + Version = hash, + GetSource = fun () -> source |> Task.FromResult + ) + } + + let checkFileWithTransparentCompiler fileId (project: SyntheticProject) (checker: FSharpChecker) = + async { + let file = project.Find fileId + let absFileName = getFilePath project file + let options = project.GetProjectOptions checker + let! projectSnapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot project) + return! checker.ParseAndCheckFileInProject(absFileName, projectSnapshot) + } let checkFile fileId (project: SyntheticProject) (checker: FSharpChecker) = - checkFileWithIncrementalBuilder fileId project checker + (if checker.UsesTransparentCompiler then + checkFileWithTransparentCompiler + else + checkFileWithIncrementalBuilder) fileId project checker let getTypeCheckResult (parseResults: FSharpParseFileResults, checkResults: FSharpCheckFileAnswer) = Assert.True(not parseResults.ParseHadErrors) @@ -566,11 +678,21 @@ module ProjectOperations = |> Option.map (fun s -> s.ToString()) |> Option.defaultValue "" + let filterErrors (diagnostics: FSharpDiagnostic array) = + diagnostics + |> Array.filter (fun diag -> + match diag.Severity with + | FSharpDiagnosticSeverity.Hidden + | FSharpDiagnosticSeverity.Info + | FSharpDiagnosticSeverity.Warning -> false + | FSharpDiagnosticSeverity.Error -> true) + let expectOk parseAndCheckResults _ = let checkResult = getTypeCheckResult parseAndCheckResults + let errors = filterErrors checkResult.Diagnostics - if checkResult.Diagnostics.Length > 0 then - failwith $"Expected no errors, but there were some: \n%A{checkResult.Diagnostics}" + if errors.Length > 0 then + failwith $"Expected no errors, but there were some: \n%A{errors}" let expectSingleWarningAndNoErrors (warningSubString:string) parseAndCheckResults _ = let checkResult = getTypeCheckResult parseAndCheckResults @@ -680,7 +802,7 @@ module ProjectOperations = module Helpers = - let getSymbolUse fileName (source: string) (symbolName: string) options (checker: FSharpChecker) = + let internal getSymbolUse fileName (source: string) (symbolName: string) snapshot (checker: FSharpChecker) = async { let lines = source.Split '\n' |> Seq.skip 1 // module definition let lineNumber, fullLine, colAtEndOfNames = @@ -694,8 +816,7 @@ module Helpers = |> Seq.tryPick id |> Option.defaultValue (-1, "", -1) - let! results = checker.ParseAndCheckFileInProject( - fileName, 0, SourceText.ofString source, options) + let! results = checker.ParseAndCheckFileInProject(fileName, snapshot) let typeCheckResults = getTypeCheckResult results @@ -706,18 +827,23 @@ module Helpers = failwith $"No symbol found in {fileName} at {lineNumber}:{colAtEndOfNames}\nFile contents:\n\n{source}\n") } - let singleFileChecker source = + let internal singleFileChecker source = let fileName = "test.fs" - let getSource _ = source |> SourceText.ofString |> Some |> async.Return + let getSource _ fileName = + FSharpFileSnapshot( + FileName = fileName, + Version = "1", + GetSource = fun () -> source |> SourceTextNew.ofString |> Task.FromResult ) + |> async.Return let checker = FSharpChecker.Create( keepAllBackgroundSymbolUses = false, enableBackgroundItemKeyStoreAndSemanticClassification = true, enablePartialTypeChecking = true, captureIdentifiersWhenParsing = true, - documentSource = DocumentSource.Custom getSource) + useTransparentCompiler = true) let options = let baseOptions, _ = @@ -739,7 +865,9 @@ module Helpers = OriginalLoadReferences = [] Stamp = None } - fileName, options, checker + let snapshot = FSharpProjectSnapshot.FromOptions(options, getSource) |> Async.RunSynchronously + + fileName, snapshot, checker open Helpers @@ -749,19 +877,23 @@ type WorkflowContext = Signatures: Map Cursor: FSharpSymbolUse option } -let SaveAndCheckProject project checker = +let SaveAndCheckProject project checker isExistingProject = async { use _ = Activity.start "SaveAndCheckProject" [ Activity.Tags.project, project.Name ] - do! saveProject project true checker + // Don't save the project if it is a real world project that exists on disk. + if not isExistingProject then + do! saveProject project true checker let options = project.GetProjectOptions checker + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot project) - let! results = checker.ParseAndCheckProject(options) + let! results = checker.ParseAndCheckProject(snapshot) + let errors = filterErrors results.Diagnostics - if not (Array.isEmpty results.Diagnostics || project.SkipInitialCheck) then - failwith $"Project {project.Name} failed initial check: \n%A{results.Diagnostics}" + if not (Array.isEmpty errors || project.SkipInitialCheck) then + failwith $"Project {project.Name} failed initial check: \n%A{errors}" let! signatures = Async.Sequential @@ -778,6 +910,8 @@ let SaveAndCheckProject project checker = Cursor = None } } +type MoveFileDirection = Up | Down + type ProjectWorkflowBuilder ( initialProject: SyntheticProject, @@ -788,19 +922,21 @@ type ProjectWorkflowBuilder ?useSyntaxTreeCache, ?useTransparentCompiler, ?runTimeout, - ?autoStart + ?autoStart, + ?isExistingProject ) = - let useTransparentCompiler = defaultArg useTransparentCompiler false + let useTransparentCompiler = defaultArg useTransparentCompiler FSharp.Compiler.CompilerConfig.FSharpExperimentalFeaturesEnabledAutomatically let useGetSource = not useTransparentCompiler && defaultArg useGetSource false let useChangeNotifications = not useTransparentCompiler && defaultArg useChangeNotifications false let autoStart = defaultArg autoStart true + let isExistingProject = defaultArg isExistingProject false let mutable latestProject = initialProject let mutable activity = None let mutable tracerProvider = None - let getSource f = f |> getSourceText latestProject |> Some |> async.Return + let getSource f = f |> getSourceText latestProject :> ISourceText |> Some |> async.Return let checker = defaultArg @@ -811,7 +947,9 @@ type ProjectWorkflowBuilder enablePartialTypeChecking = true, captureIdentifiersWhenParsing = true, documentSource = (if useGetSource then DocumentSource.Custom getSource else DocumentSource.FileSystem), - useSyntaxTreeCache = defaultArg useSyntaxTreeCache false)) + useSyntaxTreeCache = defaultArg useSyntaxTreeCache false, + useTransparentCompiler = useTransparentCompiler + )) let mapProjectAsync f workflow = async { @@ -826,7 +964,7 @@ type ProjectWorkflowBuilder let getInitialContext() = match initialContext with | Some ctx -> async.Return ctx - | None -> SaveAndCheckProject initialProject checker + | None -> SaveAndCheckProject initialProject checker isExistingProject /// Creates a ProjectWorkflowBuilder which will already have the project /// saved and checked so time won't be spent on that. @@ -859,13 +997,15 @@ type ProjectWorkflowBuilder member this.DeleteProjectDir() = if Directory.Exists initialProject.ProjectDir then - Directory.Delete(initialProject.ProjectDir, true) + try + Directory.Delete(initialProject.ProjectDir, true) + with _ -> () member this.Execute(workflow: Async) = try Async.RunSynchronously(workflow, timeout = defaultArg runTimeout 600_000) finally - if initialContext.IsNone then + if initialContext.IsNone && not isExistingProject then this.DeleteProjectDir() activity |> Option.iter (fun x -> x.Dispose()) tracerProvider |> Option.iter (fun x -> @@ -971,10 +1111,13 @@ type ProjectWorkflowBuilder async { let! ctx = workflow - use _ = + use activity = Activity.start "ProjectWorkflowBuilder.CheckFile" [ Activity.Tags.project, initialProject.Name; "fileId", fileId ] - let! results = checkFile fileId ctx.Project checker + let! results = + checkFile fileId ctx.Project checker + + activity.Dispose() let oldSignature = ctx.Signatures[fileId] let newSignature = getSignature results @@ -1000,6 +1143,30 @@ type ProjectWorkflowBuilder return { ctx with Signatures = ctx.Signatures.Add(fileId, newSignature) } } + [] + member this.MoveFile(workflow: Async, fileId: string, count, direction: MoveFileDirection) = + + workflow + |> mapProject (fun project -> + let index = + project.SourceFiles + |> List.tryFindIndex (fun f -> f.Id = fileId) + |> Option.defaultWith (fun () -> failwith $"File {fileId} not found") + + let dir = if direction = Up then -1 else 1 + let newIndex = index + count * dir + + if newIndex < 0 || newIndex > project.SourceFiles.Length - 1 then + failwith $"Cannot move file {fileId} {count} times {direction} as it would be out of bounds" + + let file = project.SourceFiles.[index] + let newFiles = + project.SourceFiles + |> List.filter (fun f -> f.Id <> fileId) + |> List.insertAt newIndex file + + { project with SourceFiles = newFiles }) + /// Find a symbol using the provided range, mimicking placing a cursor on it in IDE scenarios [] member this.PlaceCursor(workflow: Async, fileId, line, colAtEndOfNames, fullLine, symbolNames) = @@ -1025,8 +1192,9 @@ type ProjectWorkflowBuilder let project, file = ctx.Project.FindInAllProjects fileId let fileName = project.ProjectDir ++ file.FileName let source = renderSourceFile project file - let options= project.GetProjectOptions checker - return! getSymbolUse fileName source symbolName options checker + let options = project.GetProjectOptions checker + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot ctx.Project) + return! getSymbolUse fileName source symbolName snapshot checker } /// Find a symbol by finding the first occurrence of the symbol name in the file @@ -1085,7 +1253,10 @@ type ProjectWorkflowBuilder [ for p, f in ctx.Project.GetAllFiles() do let options = p.GetProjectOptions checker for fileName in [getFilePath p f; if f.SignatureFile <> No then getSignatureFilePath p f] do - checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol, fastCheck = true) ] + async { + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot ctx.Project) + return! checker.FindBackgroundReferencesInFile(fileName, snapshot, symbolUse.Symbol) + } ] |> Async.Parallel results |> Seq.collect id |> Seq.toList |> processResults @@ -1210,7 +1381,7 @@ type SyntheticProject with projectDir ++ node.Attributes["Include"].InnerText ] |> List.partition (fun path -> path.EndsWith ".fsi") let signatureFiles = set signatureFiles - + let parseReferences refType = [ for node in fsproj.DocumentElement.SelectNodes($"//{refType}") do { Name = node.Attributes["Include"].InnerText diff --git a/tests/FSharp.Test.Utilities/Utilities.fs b/tests/FSharp.Test.Utilities/Utilities.fs index a2a84a5b725..d434d837485 100644 --- a/tests/FSharp.Test.Utilities/Utilities.fs +++ b/tests/FSharp.Test.Utilities/Utilities.fs @@ -15,6 +15,10 @@ open Microsoft.CodeAnalysis.CSharp open TestFramework open NUnit.Framework open System.Collections.Generic +open FSharp.Compiler.CodeAnalysis +open Newtonsoft.Json +open Newtonsoft.Json.Linq + type TheoryForNETCOREAPPAttribute() = inherit Xunit.TheoryAttribute() @@ -104,12 +108,12 @@ module Utilities = let outputLines = StringBuilder() let errorLines = StringBuilder() - do redirector.OutputProduced.Add (fun line -> outputLines.AppendLine line |>ignore) - do redirector.ErrorProduced.Add(fun line -> errorLines.AppendLine line |>ignore) + do redirector.OutputProduced.Add (fun line -> lock outputLines <| fun () -> outputLines.AppendLine line |>ignore) + do redirector.ErrorProduced.Add(fun line -> lock errorLines <| fun () -> errorLines.AppendLine line |>ignore) - member _.Output () = outputLines.ToString() + member _.Output () = lock outputLines outputLines.ToString - member _.ErrorOutput () = errorLines.ToString() + member _.ErrorOutput () = lock errorLines errorLines.ToString interface IDisposable with member _.Dispose() = (redirector :> IDisposable).Dispose() @@ -381,3 +385,10 @@ An error occurred getting netcoreapp references: %A{e} | TargetFramework.NetStandard20 -> netStandard20Files.Value |> Seq.toArray | TargetFramework.NetCoreApp31 -> [||] //ToDo --- Perhaps NetCoreApp31Files | TargetFramework.Current -> currentReferences + + +module internal FSharpProjectSnapshotSerialization = + + let serializeSnapshotToJson (snapshot: FSharpProjectSnapshot) = + + JsonConvert.SerializeObject(snapshot, Formatting.Indented, new JsonSerializerSettings(ReferenceLoopHandling = ReferenceLoopHandling.Ignore)) \ No newline at end of file diff --git a/tests/benchmarks/CompiledCodeBenchmarks/MicroPerf/CS/MicroPerfCSharp.csproj b/tests/benchmarks/CompiledCodeBenchmarks/MicroPerf/CS/MicroPerfCSharp.csproj index 96ac25da87b..50bba8f6f23 100644 --- a/tests/benchmarks/CompiledCodeBenchmarks/MicroPerf/CS/MicroPerfCSharp.csproj +++ b/tests/benchmarks/CompiledCodeBenchmarks/MicroPerf/CS/MicroPerfCSharp.csproj @@ -6,11 +6,10 @@ 8.0 - - - - - + + + $(NoWarn);CS1591 + diff --git a/tests/benchmarks/CompiledCodeBenchmarks/TaskPerf/TaskPerfCSharp/TaskPerfCSharp.csproj b/tests/benchmarks/CompiledCodeBenchmarks/TaskPerf/TaskPerfCSharp/TaskPerfCSharp.csproj index a4e2d968480..d23714e40f2 100644 --- a/tests/benchmarks/CompiledCodeBenchmarks/TaskPerf/TaskPerfCSharp/TaskPerfCSharp.csproj +++ b/tests/benchmarks/CompiledCodeBenchmarks/TaskPerf/TaskPerfCSharp/TaskPerfCSharp.csproj @@ -6,6 +6,11 @@ 8.0 + + + $(NoWarn);CS1591 + + diff --git a/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/HistoricalBenchmark.Runner/HistoricalBenchmark.Runner.fsproj b/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/HistoricalBenchmark.Runner/HistoricalBenchmark.Runner.fsproj index 13add70323f..fe6d95f9050 100644 --- a/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/HistoricalBenchmark.Runner/HistoricalBenchmark.Runner.fsproj +++ b/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/HistoricalBenchmark.Runner/HistoricalBenchmark.Runner.fsproj @@ -4,6 +4,7 @@ net8.0 true HistoricalBenchmark.Utilities + $(NoWarn);NETSDK1206 diff --git a/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/HistoricalBenchmark.fsproj b/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/HistoricalBenchmark.fsproj index 2534ba292b5..09ca5cb6912 100644 --- a/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/HistoricalBenchmark.fsproj +++ b/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/HistoricalBenchmark.fsproj @@ -59,6 +59,9 @@ $(FSharpCoreDllPath) + + + diff --git a/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/Program.fs b/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/Program.fs index 779a18a7709..2a4f8af4ce5 100644 --- a/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/Program.fs +++ b/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/Program.fs @@ -3,6 +3,7 @@ namespace HistoricalBenchmark open System.IO open BenchmarkDotNet.Attributes open BenchmarkDotNet.Running +open FSharp.Benchmarks.Common.Categories [] type SingleFileCompilerBenchmarkBase(compiler : SingleFileCompiler) = @@ -20,6 +21,7 @@ type SingleFileCompilerBenchmarkBase(compiler : SingleFileCompiler) = [] [] +[] type DecentlySizedStandAloneFileBenchmark() = inherit SingleFileCompilerBenchmarkBase( SingleFileCompiler( diff --git a/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/README.md b/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/README.md index d3bd6c823e6..72a6acc3f1e 100644 --- a/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/README.md +++ b/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/README.md @@ -12,7 +12,7 @@ To run a benchmark for a local FCS in the current codebase you can run the bench ```dotnet run --project HistoricalBenchmark.fsproj -c Release --filter *``` -To run a comparison use the `runner.ipynb` .NET notebook +To run a comparison use the `runner.ipynb` .NET notebook. ## How it works @@ -35,9 +35,4 @@ As of now the minimum supported version of FCS is 13.0.0 ## Sample results -Below is a sample result of running the notebook locally with a selection of versions: -![a](./sample_result.png?raw=true) - -## Other - -You can find this document under 'tests/benchmarks/FCSBenchmarks/BenchmarkComparison/README.md'. \ No newline at end of file +See sample results in the dedicated [sample_results](./sample_results/) folder. \ No newline at end of file diff --git a/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/sample_results/README.md b/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/sample_results/README.md index 098d4f8a0c4..803e47ed266 100644 --- a/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/sample_results/README.md +++ b/tests/benchmarks/FCSBenchmarks/BenchmarkComparison/sample_results/README.md @@ -1,13 +1,16 @@ # Sample results -This folder contains a selection of results obtained by running the notebook located in `../runner.ipynb` + +This folder contains a selection of results obtained by running the notebook located in `../runner.ipynb`. ## Timings are not accurate + The results were gathered on a busy machine without much care taken to provide a reliable performance environment. While this means the timing metrics are not very useful, the results can still be useful for two reasons: * allocation data is quite accurate as it doesn't tend to depend much on the environment * they work as examples that can make using the benchmarks easier ## Structure + Each directory contains 3 files output by `HistoricalBenchmark.Runner.runAll` function for a given selection of versions. The three different version sets are: @@ -16,10 +19,12 @@ The three different version sets are: - `10_latest_nuget_versions` - 10 FCS NuGet versions between `v41.0.2` and ``v41.0.5-preview.22327.2` ## Observations -One thing that can be observed by looking at the results in `between_2_nuget_versions` is the noticable increase of allocations in https://github.com/dotnet/fsharp/pull/11517 + +One thing that can be observed by looking at the results in `between_2_nuget_versions` is the noticeable increase of allocations in https://github.com/dotnet/fsharp/pull/11517. While this isn't necessarily something worth addressing, partly because later revisions show reduced allocations, it shows how running a historical benchmark can be potentially useful. ## Notes + - The metrics gathered here are very limited - much more data can be gathered from each benchmark. - Such historical benchmarks run locally might be mostly deprecated once CI setup exists for performance tests that will provide the necessary historical information \ No newline at end of file diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs index a6ce4b36487..ff67e1acabe 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs @@ -6,14 +6,11 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Text open FSharp.Compiler.Diagnostics open FSharp.Test.ProjectGeneration - - -[] -let FSharpCategory = "fsharp" - +open BenchmarkDotNet.Engines +open FSharp.Benchmarks.Common.Categories [] -[] +[] type BackgroundCompilerBenchmarks () = let size = 50 @@ -103,7 +100,7 @@ type BackgroundCompilerBenchmarks () = this.Benchmark.DeleteProjectDir() [] -[] +[] type ParsingBenchmark() = let mutable checker: FSharpChecker = Unchecked.defaultof<_> @@ -133,7 +130,7 @@ type ParsingBenchmark() = failwith "ParseHadErrors" [] -[] +[] type NoFileSystemCheckerBenchmark() = let size = 30 @@ -217,3 +214,215 @@ type NoFileSystemCheckerBenchmark() = [] member this.Cleanup() = benchmark.DeleteProjectDir() + + + +type TestProjectType = + | DependencyChain = 1 + | DependentGroups = 2 + | ParallelGroups = 3 + + +[] +[] +[] +[] +type TransparentCompilerBenchmark() = + + let size = 30 + + let groups = 6 + let filesPerGroup = size / groups + let somethingToCompile = File.ReadAllText (__SOURCE_DIRECTORY__ ++ "SomethingToCompileSmaller.fs") + + let projects = Map [ + + TestProjectType.DependencyChain, + SyntheticProject.Create("SingleDependencyChain", [| + sourceFile $"File%03d{0}" [] + for i in 1..size do + { sourceFile $"File%03d{i}" [$"File%03d{i-1}"] with ExtraSource = somethingToCompile } + |]) + + TestProjectType.DependentGroups, + SyntheticProject.Create("GroupDependenciesProject", [| + for group in 1..groups do + for i in 1..filesPerGroup do + { sourceFile $"G{group}_F%03d{i}" [ + if group > 1 then $"G1_F%03d{1}" + if i > 1 then $"G{group}_F%03d{i - 1}" ] + with ExtraSource = somethingToCompile } + |]) + + TestProjectType.ParallelGroups, + SyntheticProject.Create("ParallelGroupsProject", [| + for group in 1..groups do + for i in 1..filesPerGroup do + { sourceFile $"G{group}_F%03d{i}" [ + if group > 1 then + for i in 1..filesPerGroup do + $"G{group-1}_F%03d{i}" ] + with ExtraSource = somethingToCompile } + |]) + ] + + let mutable benchmark : ProjectWorkflowBuilder = Unchecked.defaultof<_> + + member val UseGetSource = true with get,set + + member val UseChangeNotifications = true with get,set + + //[] + member val EmptyCache = false with get,set + + [] + member val UseTransparentCompiler = true with get,set + + [] + member val ProjectType = TestProjectType.ParallelGroups with get,set + + member this.Project = projects[this.ProjectType] + + [] + member this.Setup() = + benchmark <- + ProjectWorkflowBuilder( + this.Project, + useGetSource = this.UseGetSource, + useChangeNotifications = this.UseChangeNotifications, + useTransparentCompiler = this.UseTransparentCompiler, + runTimeout = 15_000).CreateBenchmarkBuilder() + + [] + member this.EditFirstFile_OnlyInternalChange() = + if this.EmptyCache then + benchmark.Checker.InvalidateAll() + benchmark.Checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() + + [] + member this.ExampleWorkflow() = + + use _ = Activity.start "Benchmark" [ + "UseTransparentCompiler", this.UseTransparentCompiler.ToString() + ] + + let first = this.Project.SourceFiles[0].Id + let middle = this.Project.SourceFiles[size / 2].Id + let last = this.Project.SourceFiles |> List.last |> fun f -> f.Id + + benchmark { + updateFile first updatePublicSurface + checkFile first expectSignatureChanged + checkFile last expectSignatureChanged + updateFile middle updatePublicSurface + checkFile last expectOk + addFileAbove middle (sourceFile "addedFile" [first]) + updateFile middle (addDependency "addedFile") + checkFile middle expectSignatureChanged + checkFile last expectOk + } + + [] + member this.Cleanup() = + benchmark.DeleteProjectDir() + + +// needs Giraffe repo somewhere nearby, hence benchmarks disabled +[] +[] +[] +type TransparentCompilerGiraffeBenchmark() = + + let mutable benchmark : ProjectWorkflowBuilder = Unchecked.defaultof<_> + + let rng = System.Random() + + let addComment s = $"{s}\n// {rng.NextDouble().ToString()}" + let prependSlash s = $"/{s}\n// {rng.NextDouble()}" + + let modify (sourceFile: SyntheticSourceFile) = + { sourceFile with Source = addComment sourceFile.Source } + + let break' (sourceFile: SyntheticSourceFile) = + { sourceFile with Source = prependSlash sourceFile.Source } + + let fix (sourceFile: SyntheticSourceFile) = + { sourceFile with Source = sourceFile.Source.Substring 1 } + + [] + member val UseTransparentCompiler = true with get,set + + [] + member val SignatureFiles = true with get,set + + member this.Project = + let projectDir = if this.SignatureFiles then "Giraffe-signatures" else "Giraffe" + + let project = SyntheticProject.CreateFromRealProject (__SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ ".." ++ projectDir ++ "src/Giraffe") + { project with OtherOptions = "--nowarn:FS3520"::project.OtherOptions } + + [] + member this.Setup() = + benchmark <- + ProjectWorkflowBuilder( + this.Project, + useGetSource = true, + useChangeNotifications = true, + useTransparentCompiler = this.UseTransparentCompiler, + runTimeout = 15_000).CreateBenchmarkBuilder() + + //[] + member this.ChangeFirstCheckLast() = + + use _ = Activity.start "Benchmark" [ + "UseTransparentCompiler", this.UseTransparentCompiler.ToString() + ] + + benchmark { + updateFile this.Project.SourceFiles.Head.Id modify + checkFile (this.Project.SourceFiles |> List.last).Id expectOk + } + + //[] + member this.ChangeSecondCheckLast() = + + use _ = Activity.start "Benchmark" [ + "UseTransparentCompiler", this.UseTransparentCompiler.ToString() + ] + + benchmark { + updateFile this.Project.SourceFiles[1].Id modify + checkFile (this.Project.SourceFiles |> List.last).Id expectOk + } + + // [] + member this.SomeWorkflow() = + + use _ = Activity.start "Benchmark" [ + "UseTransparentCompiler", this.UseTransparentCompiler.ToString() + ] + + benchmark { + updateFile "Json" modify + checkFile "Json" expectOk + checkFile "ModelValidation" expectOk + updateFile "ModelValidation" modify + checkFile "ModelValidation" expectOk + updateFile "Xml" modify + checkFile "Xml" expectOk + updateFile "ModelValidation" modify + checkFile "ModelValidation" expectOk + + updateFile "Core" break' + checkFile "Core" expectErrors + checkFile "Routing" (if this.SignatureFiles then expectOk else expectErrors) + updateFile "Routing" modify + checkFile "Streaming" (if this.SignatureFiles then expectOk else expectErrors) + checkFile "EndpointRouting" (if this.SignatureFiles then expectOk else expectErrors) + + updateFile "Core" fix + checkFile "Core" expectOk + checkFile "Routing" expectOk + checkFile "Streaming" expectOk + checkFile "EndpointRouting" expectOk + } \ No newline at end of file diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/CompilerServiceBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/CompilerServiceBenchmarks.fs index 4910b2e19eb..2b28cdce001 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/CompilerServiceBenchmarks.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/CompilerServiceBenchmarks.fs @@ -12,6 +12,7 @@ open FSharp.Compiler.AbstractIL.ILBinaryReader open BenchmarkDotNet.Attributes open FSharp.Compiler.Benchmarks open Microsoft.CodeAnalysis.Text +open FSharp.Benchmarks.Common.Categories type private Config = { @@ -66,6 +67,7 @@ let function%s{moduleName} (x: %s{moduleName}) = [] +[] type CompilerServiceBenchmarks() = let mutable configOpt : Config option = None let sourcePath = Path.Combine(__SOURCE_DIRECTORY__, "../decentlySizedStandAloneFile.fs") diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ComputationExpressionBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ComputationExpressionBenchmarks.fs new file mode 100644 index 00000000000..815d4818344 --- /dev/null +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ComputationExpressionBenchmarks.fs @@ -0,0 +1,51 @@ +module FSharp.Benchmarks.ComputationExpressionBenchmarks + +open System.IO +open BenchmarkDotNet.Attributes +open FSharp.Compiler.CodeAnalysis +open FSharp.Test.ProjectGeneration +open FSharp.Benchmarks.Common.Categories + +[] +[] +type ComputationExpressionBenchmarks() = + + let mutable sourceFileName = "" + + [] + member public this.Source + with get () = File.ReadAllText(__SOURCE_DIRECTORY__ ++ "ce" ++ sourceFileName) + and set f = sourceFileName <- f + + member val Benchmark = Unchecked.defaultof<_> with get, set + + member this.setup(project) = + let checker = FSharpChecker.Create() + this.Benchmark <- ProjectWorkflowBuilder(project, checker = checker).CreateBenchmarkBuilder() + saveProject project false checker |> Async.RunSynchronously + + [] + member this.SetupWithSource() = + this.setup + { SyntheticProject.Create() with + SourceFiles = + [ + + { sourceFile "File" [] with + ExtraSource = this.Source + } + ] + OtherOptions = [] + } + + [] + member this.CheckCE() = + this.Benchmark { checkFile "File" expectOk } + + [] + member this.CompileCE() = this.Benchmark { compileWithFSC } diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/DecentlySizedStandAloneFileBenchmark.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/DecentlySizedStandAloneFileBenchmark.fs index f1525cfc734..b1926b44e66 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/DecentlySizedStandAloneFileBenchmark.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/DecentlySizedStandAloneFileBenchmark.fs @@ -1,22 +1,63 @@ namespace FSharp.Compiler.Benchmarks open System.IO -open HistoricalBenchmark +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.Text open BenchmarkDotNet.Attributes +open FSharp.Benchmarks.Common.Categories -type SingleFileCompilerWithILCacheClearing(file, options) = - inherit SingleFileCompiler(file, options) - - override this.Cleanup() = - base.Cleanup() - FSharp.Compiler.AbstractIL.ILBinaryReader.ClearAllILModuleReaderCache() +type private SingleFileCompilerConfig = + { + Checker : FSharpChecker + Options : FSharpProjectOptions + } [] +[] type DecentlySizedStandAloneFileBenchmark() = - inherit SingleFileCompilerBenchmarkBase( - SingleFileCompilerWithILCacheClearing( - Path.Combine(__SOURCE_DIRECTORY__, "../decentlySizedStandAloneFile.fs"), - OptionsCreationMethod.FromScript - ) - ) + let mutable configOpt : SingleFileCompilerConfig option = None + let filePath = Path.Combine(__SOURCE_DIRECTORY__, "../decentlySizedStandAloneFile.fs") + + let getFileSourceText (filePath : string) = + let text = File.ReadAllText(filePath) + SourceText.ofString text + + let getConfig () = + configOpt + |> Option.defaultWith (fun () -> failwith "Setup not run") + + [] + member _.Setup() = + configOpt <- + match configOpt with + | Some _ -> configOpt + | None -> + let checker = FSharpChecker.Create(projectCacheSize = 200) + let options = + checker.GetProjectOptionsFromScript(filePath, getFileSourceText filePath) + |> Async.RunSynchronously + |> fst + { + Checker = checker + Options = options + } + |> Some + + [] + member _.Run() = + let config = getConfig() + let _, result = + config.Checker.ParseAndCheckFileInProject(filePath, 0, getFileSourceText filePath, config.Options) + |> Async.RunSynchronously + + match result with + | FSharpCheckFileAnswer.Aborted -> failwith "checker aborted" + | FSharpCheckFileAnswer.Succeeded results -> + if results.Diagnostics.Length > 0 then failwithf $"had errors: %A{results.Diagnostics}" + + [] + member _.Cleanup() = + let checker = getConfig().Checker + checker.InvalidateAll() + checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj index 19c7f7a7e96..043de02d374 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj @@ -14,6 +14,7 @@ + @@ -24,7 +25,7 @@ - + diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FileCascadeBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FileCascadeBenchmarks.fs index 686153577fa..6eddb5eb5b3 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FileCascadeBenchmarks.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FileCascadeBenchmarks.fs @@ -2,18 +2,13 @@ open System open System.IO -open System.Text open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.Diagnostics -open FSharp.Compiler.EditorServices open FSharp.Compiler.Text -open FSharp.Compiler.AbstractIL.IL -open FSharp.Compiler.AbstractIL.ILBinaryReader open BenchmarkDotNet.Attributes open FSharp.Compiler.Benchmarks -open Microsoft.CodeAnalysis.Text open BenchmarkDotNet.Order open BenchmarkDotNet.Mathematics +open FSharp.Benchmarks.Common.Categories [] module private CascadeProjectHelpers = @@ -87,6 +82,7 @@ val processFunc{number}: x: MyType{number} -> func: MyFunctionType{number} -> As [] [] [] +[] type FileCascadeBenchmarks() = let mutable project : FSharpProjectOptions option = None let filesToCreate = 128 diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/GraphTypeCheckingBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/GraphTypeCheckingBenchmarks.fs index dd16e8b13a0..3f2f170a410 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/GraphTypeCheckingBenchmarks.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/GraphTypeCheckingBenchmarks.fs @@ -4,12 +4,10 @@ open System.IO open BenchmarkDotNet.Attributes open FSharp.Compiler.CodeAnalysis open FSharp.Test.ProjectGeneration - -[] -let FSharpCategory = "fsharp" +open FSharp.Benchmarks.Common.Categories [] -[] +[] type GraphTypeCheckingBenchmarks() = let size = 250 diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/README.md b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/README.md index 90e90b0fd6f..1b574ec1b56 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/README.md +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/README.md @@ -2,8 +2,8 @@ ## What is it -* A selection of BDN benchmarks analysing FCS performance -* Uses BDN's commandline API +* A selection of BDN benchmarks analyzing FCS performance +* Uses BDN's command line API ## How to run it @@ -11,12 +11,15 @@ Running all benchmarks: ```dotnet run -c Release --filter *``` Running a specific benchmark: -```dotnet run -c Release --filter *ParsingTypeCheckerFs*``` +```dotnet run -c Release --filter *ParsingCheckExpressionsFs*``` ## Sample results -*TODO* - -## Other - -You can find this document under 'tests/benchmarks/FCSBenchmarks/BenchmarkComparison/README.md'. \ No newline at end of file +| Method | Job | UnrollFactor | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated | +|--------------------------------------- |----------- |------------- |---------------:|-------------:|--------------:|---------------:|------------:|-----------:|----------:|-----------:| +| SimplifyNames | DefaultJob | 16 | 17,221.4 us | 378.14 us | 1,097.04 us | 17,164.1 us | 1875.0000 | 31.2500 | - | 11,654 KB | +| UnusedOpens | DefaultJob | 16 | 852.7 us | 16.96 us | 36.87 us | 852.0 us | 120.1172 | 37.1094 | - | 736 KB | +| UnusedDeclarations | DefaultJob | 16 | 208.2 us | 6.65 us | 19.09 us | 202.7 us | 71.5332 | 3.6621 | - | 438 KB | +| ParsingCheckExpressionsFs | Job-CXFNSP | 1 | 255,107.0 us | 39,778.24 us | 117,287.03 us | 186,340.7 us | 4000.0000 | 1000.0000 | - | 30,082 KB | +| ILReading | Job-CXFNSP | 1 | 1,256,653.6 us | 24,802.85 us | 48,958.41 us | 1,249,170.3 us | 102000.0000 | 31000.0000 | 2000.0000 | 671,507 KB | +| TypeCheckFileWith100ReferencedProjects | Job-CXFNSP | 1 | 6,541.1 us | 242.62 us | 700.00 us | 6,614.2 us | - | - | - | 3,547 KB | diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CE100xnest1.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CE100xnest1.fs new file mode 100644 index 00000000000..094513ff80c --- /dev/null +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CE100xnest1.fs @@ -0,0 +1,115 @@ +module SomeModule = + + type StrChainBuilder() = + member this.Zero() = "" + member this.Delay(f) = f () + member this.Yield(x: string) = x + member this.Combine(a, b) = a + b + + let strchain = StrChainBuilder() + + let test = + // 100 x nesting of 1 + strchain { + "test0" + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + strchain { "test1" } + } diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CE100xnest10.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CE100xnest10.fs new file mode 100644 index 00000000000..0afe4a1d3f6 --- /dev/null +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CE100xnest10.fs @@ -0,0 +1,3616 @@ +module SomeModule = + + type StrChainBuilder() = + member this.Zero() = "" + member this.Delay(f) = f () + member this.Yield(x: string) = x + member this.Combine(a, b) = a + b + + let strchain = StrChainBuilder() + + // 100 x nesting of 10 + let test = + strchain { + "test0" + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { + "test5" + + strchain { + "test6" + + strchain { + "test7" + + strchain { + "test8" + + strchain { + "test9" + strchain { "test10" } + } + } + } + } + } + } + } + } + } + } diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CE100xnest5.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CE100xnest5.fs new file mode 100644 index 00000000000..28ccd4c1593 --- /dev/null +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CE100xnest5.fs @@ -0,0 +1,1615 @@ +module SomeModule = + + type StrChainBuilder() = + member this.Zero() = "" + member this.Delay(f) = f () + member this.Yield(x: string) = x + member this.Combine(a, b) = a + b + + let strchain = StrChainBuilder() + + // 100 x nesting of 5 + let test = + strchain { + "test0" + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + strchain { "test5" } + } + } + } + } + } diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CE200xnest5.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CE200xnest5.fs new file mode 100644 index 00000000000..0295cec82cf --- /dev/null +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CE200xnest5.fs @@ -0,0 +1,3615 @@ +module SomeModule = + + type StrChainBuilder() = + member this.Zero() = "" + member this.Delay(f) = f () + member this.Yield(x: string) = x + member this.Combine(a, b) = a + b + + let strchain = StrChainBuilder() + + // 200 x nesting of 5 + let test = + strchain { + "test0" + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + + strchain { + "test1" + + strchain { + "test2" + + strchain { + "test3" + + strchain { + "test4" + + strchain { "test5" } + } + } + } + } + } diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CEwCO100xnest5.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CEwCO100xnest5.fs new file mode 100644 index 00000000000..d196ed0d91a --- /dev/null +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CEwCO100xnest5.fs @@ -0,0 +1,2937 @@ +module CalculationCE = + + type CalcState = { r: int } + + type CalculationBuilder() = + member _.Yield _ = { r = 0 } + member _.Return(x: int) = { r = x } + member _.ReturnFrom(x) = x + + [] + member _.Add(state: CalcState, x) = { r = state.r + x } + + [] + member _.Sub(state: CalcState, x) = { r = state.r - x } + + [] + member _.Mul(state: CalcState, x) = { r = state.r * x } + + [] + member _.Div(state: CalcState, x) = + if x = 0 then + failwith "can't divide by 0" + + { r = state.r / x } + + let calculation = CalculationBuilder() + +open CalculationCE + +// 100 x nesting of 5 +let c = + calculation { + + let c1 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c2 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c3 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c4 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c5 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c6 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c7 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c8 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c9 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c10 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c11 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c12 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c13 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c14 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c15 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c16 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c17 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c18 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c19 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c20 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c21 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c22 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c23 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c24 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c25 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c26 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c27 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c28 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c29 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c30 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c31 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c32 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c33 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c34 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c35 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c36 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c37 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c38 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c39 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c40 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c41 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c42 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c43 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c44 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c45 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c46 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c47 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c48 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c49 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c50 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c51 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c52 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c53 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c54 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c55 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c56 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c57 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c58 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c59 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c60 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c61 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c62 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c63 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c64 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c65 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c66 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c67 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c68 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c69 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c70 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c71 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c72 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c73 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c74 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c75 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c76 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c77 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c78 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c79 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c80 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c81 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c82 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c83 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c84 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c85 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c86 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c87 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c88 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c89 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c90 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c91 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c92 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c93 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c94 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c95 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c96 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c97 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c98 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c99 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let c100 = + calculation { + let nest2 = + calculation { + let nest3 = + calculation { + let nest4 = + calculation { + let nest5 = + calculation { + add 1 + sub 1 + mul 1 + div 1 + } + + return! nest5 + } + + return! nest4 + } + + return! nest3 + } + + return! nest2 + } + + let s = + [ c1.r + c2.r + c3.r + c4.r + c5.r + c6.r + c7.r + c8.r + c9.r + c10.r + c11.r + c12.r + c13.r + c14.r + c15.r + c16.r + c17.r + c18.r + c19.r + c20.r + c21.r + c22.r + c23.r + c24.r + c25.r + c26.r + c27.r + c28.r + c29.r + c30.r + c31.r + c32.r + c33.r + c34.r + c35.r + c36.r + c37.r + c38.r + c39.r + c40.r + c41.r + c42.r + c43.r + c44.r + c45.r + c46.r + c47.r + c48.r + c49.r + c50.r + c51.r + c52.r + c53.r + c54.r + c55.r + c56.r + c57.r + c58.r + c59.r + c60.r + c61.r + c62.r + c63.r + c64.r + c65.r + c66.r + c67.r + c68.r + c69.r + c70.r + c71.r + c72.r + c73.r + c74.r + c75.r + c76.r + c77.r + c78.r + c79.r + c80.r + c81.r + c82.r + c83.r + c84.r + c85.r + c86.r + c87.r + c88.r + c89.r + c90.r + c91.r + c92.r + c93.r + c94.r + c95.r + c96.r + c97.r + c98.r + c99.r + c100.r ] + + return! s |> List.sum + } diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CEwCO500xnest1.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CEwCO500xnest1.fs new file mode 100644 index 00000000000..c67f8e73528 --- /dev/null +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ce/CEwCO500xnest1.fs @@ -0,0 +1,539 @@ +module CalculationCE = + + type CalcState = + { r: int } + + type CalculationBuilder() = + member _.Yield _ = + { r = 0 } + + [] + member _.Add(state: CalcState, x) = + { state with + r = state.r + x } + + [] + member _.Sub(state: CalcState, x) = + { state with + r = state.r - x } + + [] + member _.Mul(state: CalcState, x) = + { state with + r = state.r * x } + + [] + member _.Div(state: CalcState, x) = + if x = 0 then + failwith "can't divide by 0" + { state with + r = state.r / x } + + let calculation = CalculationBuilder() + +open CalculationCE + +let c = + // 500 applications of custom operations + calculation { + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + add 1 + sub 1 + mul 1 + div 1 + } diff --git a/tests/benchmarks/FCSBenchmarks/FCSSourceFiles/FCSSourceFiles.fsproj b/tests/benchmarks/FCSBenchmarks/FCSSourceFiles/FCSSourceFiles.fsproj index 3774aa87023..92255adb462 100644 --- a/tests/benchmarks/FCSBenchmarks/FCSSourceFiles/FCSSourceFiles.fsproj +++ b/tests/benchmarks/FCSBenchmarks/FCSSourceFiles/FCSSourceFiles.fsproj @@ -16,6 +16,7 @@ + diff --git a/tests/benchmarks/FCSBenchmarks/FCSSourceFiles/Program.fs b/tests/benchmarks/FCSBenchmarks/FCSSourceFiles/Program.fs index 9c8148ed4da..7b716505156 100644 --- a/tests/benchmarks/FCSBenchmarks/FCSSourceFiles/Program.fs +++ b/tests/benchmarks/FCSBenchmarks/FCSSourceFiles/Program.fs @@ -5,6 +5,7 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Text open BenchmarkDotNet.Attributes open BenchmarkDotNet.Running +open FSharp.Benchmarks.Common.Categories module Project = let nugetCache = @@ -874,6 +875,7 @@ module Project = Stamp = None } [] +[] type CompilerService() = let mutable checkerOpt = None let mutable sourceOpt : (string * ISourceText) array option = None diff --git a/tests/benchmarks/FCSBenchmarks/FCSSourceFiles/README.md b/tests/benchmarks/FCSBenchmarks/FCSSourceFiles/README.md index 534e2762961..0cd8807aa16 100644 --- a/tests/benchmarks/FCSBenchmarks/FCSSourceFiles/README.md +++ b/tests/benchmarks/FCSBenchmarks/FCSSourceFiles/README.md @@ -9,12 +9,18 @@ ## How to run it 1. Run `dotnet run -c release` -2. Output available on the commandline and in `BenchmarkDotNet.Artifacts/` +2. Output available on the command line and in `BenchmarkDotNet.Artifacts/` ## Sample results -*TODO* - -## Other - -You can find this document under 'tests/benchmarks/FCSBenchmarks/FCSSourceFiles/README.md'. \ No newline at end of file +``` +BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22621 +11th Gen Intel Core i7-1185G7 3.00GHz, 1 CPU, 8 logical and 4 physical cores +.NET SDK=6.0.320 + [Host] : .NET 6.0.25 (6.0.2523.51912), X64 RyuJIT DEBUG + DefaultJob : .NET 6.0.25 (6.0.2523.51912), X64 RyuJIT +``` + +| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | +|--------------------------- |--------:|--------:|--------:|-------------:|------------:|----------:|----------:| +| ParseAndCheckFileInProject | 22.14 s | 0.543 s | 1.522 s | 1645000.0000 | 307000.0000 | 6000.0000 | 10 GB | diff --git a/tests/benchmarks/FCSBenchmarks/README.md b/tests/benchmarks/FCSBenchmarks/README.md index 6d4a3a674f0..aedaf8c38aa 100644 --- a/tests/benchmarks/FCSBenchmarks/README.md +++ b/tests/benchmarks/FCSBenchmarks/README.md @@ -2,26 +2,19 @@ ## What can be found here -Benchmarks that exercise performance of `FSharp.Compiler.Service` +Benchmarks that exercise performance of `FSharp.Compiler.Service`. + +## Testing performance of FSharp.Compiler.Service -## Testing performance of FSharp.Compiler.Service Performance of the compiler service is crucial for having good developer experience. This includes compilation, type checking and any other parts of the API used by IDEs. When making changes to the FCS source code, consider running some of these to assess impact of the changes on performance. ## Benchmark list + * `BenchmarkComparison/` - a Notebook-based benchmark that analyses performance of `FSharpChecker.ParseAndCheckFileInProject` on a single-file F# project. Supports comparing different revisions of the FCS codebase and fetching the code automatically. -* `CompilerServiceBenchmarks/` - +* `CompilerServiceBenchmarks/` - a selection of BDN benchmarks analyzing FCS performance * `FCSSourceFiles/` - analyses performance of `FSharpChecker.ParseAndCheckFileInProject` for the `FSharp.Core` project. Uses locally available source code. All the above benchmarks use BenchmarkDotNet. - -## Quickly validating that the benchmarks work -`SmokeTestAllBenchmarks.ps1` allows to run all BDN benchmarks in this directory with a minimum number of iterations, as a way to verify that the benchmarks still work. - -This doesn't validate the notebook-based meta-benchmarks. - -## Other - -You can find this document under 'tests/benchmarks/FCSBenchmarks/README.md'. \ No newline at end of file diff --git a/tests/benchmarks/FCSBenchmarks/SmokeTestAllBenchmarks.ps1 b/tests/benchmarks/FCSBenchmarks/SmokeTestAllBenchmarks.ps1 deleted file mode 100644 index 688434a176e..00000000000 --- a/tests/benchmarks/FCSBenchmarks/SmokeTestAllBenchmarks.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -# Smoke test for checking that all the benchmarks work -# The test is successful if all the benchmarks run and produce results. -# The actual numbers produced aren't accurate. -# Results can be checked in BenchmarkDotNet.Artifacts/results - -function Run { - param ( - [string[]]$path - ) - # Build the benchmark project itself but no dependencies - still fast, but reduces risk of not accounting for code changes - dotnet build -c release --no-dependencies $path - # Run the minimum the CLI API allows - ideally we would use ColdStart but that's not part of the CLI API - # For API details see https://benchmarkdotnet.org/articles/guides/console-args.html - dotnet run -c release --project $path --no-build -- --warmupCount 0 --iterationCount 1 --runOncePerIteration --filter * -} - -Run "BenchmarkComparison/HistoricalBenchmark.fsproj" -Run "CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj" -Run "FCSSourceFiles/FCSSourceFiles.fsproj" \ No newline at end of file diff --git a/tests/benchmarks/FSharp.Benchmarks.Common/Categories.fs b/tests/benchmarks/FSharp.Benchmarks.Common/Categories.fs new file mode 100644 index 00000000000..deadacbfd6e --- /dev/null +++ b/tests/benchmarks/FSharp.Benchmarks.Common/Categories.fs @@ -0,0 +1,10 @@ +namespace FSharp.Benchmarks.Common + +module Categories = + + [] + let ShortCategory = "short" + + [] + let LongCategory = "long" + diff --git a/tests/benchmarks/FSharp.Benchmarks.Common/FSharp.Benchmarks.Common.fsproj b/tests/benchmarks/FSharp.Benchmarks.Common/FSharp.Benchmarks.Common.fsproj new file mode 100644 index 00000000000..2e6b2e9ceb7 --- /dev/null +++ b/tests/benchmarks/FSharp.Benchmarks.Common/FSharp.Benchmarks.Common.fsproj @@ -0,0 +1,12 @@ + + + + net8.0 + true + + + + + + + diff --git a/tests/benchmarks/Fsharp.ProfilingStartpointProject/Fsharp.ProfilingStartpointProject.fsproj b/tests/benchmarks/Fsharp.ProfilingStartpointProject/Fsharp.ProfilingStartpointProject.fsproj deleted file mode 100644 index 6cb9e4bd78c..00000000000 --- a/tests/benchmarks/Fsharp.ProfilingStartpointProject/Fsharp.ProfilingStartpointProject.fsproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - net8.0 - - - - - - - - - - - diff --git a/tests/benchmarks/Fsharp.ProfilingStartpointProject/Program.fs b/tests/benchmarks/Fsharp.ProfilingStartpointProject/Program.fs deleted file mode 100644 index 5d851e60865..00000000000 --- a/tests/benchmarks/Fsharp.ProfilingStartpointProject/Program.fs +++ /dev/null @@ -1,47 +0,0 @@ -open FSharp.Compiler.Benchmarks - -open System -open System.IO -open System.Text -open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.Diagnostics -open FSharp.Compiler.EditorServices -open FSharp.Compiler.Text -open FSharp.Compiler.AbstractIL.IL -open FSharp.Compiler.AbstractIL.ILBinaryReader -open BenchmarkDotNet.Attributes -open FSharp.Compiler.Benchmarks -open Microsoft.CodeAnalysis.Text -open BenchmarkDotNet.Order -open BenchmarkDotNet.Mathematics - -let bench = new FileCascadeBenchmarks() -bench.GenerateFSI <- true -do bench.Setup() - -(* -This project was created as an easy entry point for low-level profiling of FCS operations. -The only purpose is the easy of setup (simply set as startup project and launch) so that a profiler can be connected. -There is definitely no harm in deleting it if it starts bothering anyone. -*) - - -[] -let main args = - - match args |> Array.toList with - | ["no-change"] -> - for i=1 to 256 do - printfn "***************************" - printfn "ITERATION %i" i - printfn "***************************" - bench.ParseAndCheckLastFileProjectAsIs() |> ignore - | ["mid-change"] -> - for i=1 to 16 do - printfn "***************************" - printfn "ITERATION %i" i - printfn "***************************" - bench.ParseProjectWithChangingMiddleFile() |> ignore - | _ -> failwith "Invalid args. Use cache-clear or mid-change" - |> ignore - 0 diff --git a/tests/benchmarks/README.md b/tests/benchmarks/README.md index c3500eb8bb5..dc304fed2ce 100644 --- a/tests/benchmarks/README.md +++ b/tests/benchmarks/README.md @@ -12,10 +12,6 @@ Each of them assesses a slightly different use case and is run in a different wa Since there is currently no dedicated hardware setup for running benchmarks in a highly accurate fashion, the results obtained by running them locally have to be treated carefully. Specifically results obtained on different hardware or in different environments should be treated differently. -Note that there are plans to update the performance testing infrastructure. More information can be found at the following links: -* https://github.com/dotnet/fsharp/discussions/12526 -* https://github.com/dotnet/performance/issues/2457 - ### Types of performance tests Performance tests in this codebase can be broadly put into two groups: @@ -26,9 +22,10 @@ Group 1. affects end users of programs, while group 2. affects developer experie ### Directory structure -Tests are structured as follows +The code is structured as follows * `CompiledCodeBenchmarks/` - benchmarks that test compiled code performance. * `FCSBenchmarks/` - benchmarks of the compiler service itself. +* `FSharp.Benchmarks.Common/` - the library with common code. ### Jupyter notebooks @@ -43,7 +40,11 @@ It helps avoid common benchmarking pitfalls and provide highly-accurate, repeata A BDN benchmark is an executable. To run it, simply run `dotnet run %BenchmarkProject.fsproj%` in the benchmark's directory. -### Writing a new benchmark +## Quickly validating that the benchmarks work + +`SmokeTestBenchmarks.ps1` allows to run faster BDN benchmarks with a minimum number of iterations, as a way to verify that the benchmarks still work. This doesn't validate the notebook-based meta-benchmarks. + +## Authoring benchmarks When adding a benchmark, consider: * choosing an appropriate subdirectory @@ -52,8 +53,147 @@ When adding a benchmark, consider: * * how to run the benchmark, including any environment requirements * * an example of the results it produces -For instructions on how to write a BDN benchmark see [DEVGUIDE](https://github.com/dotnet/fsharp/blob/main/DEVGUIDE.md). - -## Other +Here are the steps for creating benchmarks: + +1. Perform a clean build of the compiler and FCS from source (as described in this document, build can be done with `-noVisualStudio` in case if FCS/FSharp.Core is being benchmarked/profiled). + +2. Create a benchmark project (in this example, the project will be created in `tests\benchmarks\FCSBenchmarks`). + + ```shell + cd tests\benchmarks\FCSBenchmarks + dotnet new console -o FcsBench --name FcsBench -lang F# + ``` + +3. Add needed packages and project references. + + ```shell + cd FcsBench + dotnet add package BenchmarkDotNet + dotnet add reference ..\..\..\src\Compiler\FSharp.Compiler.Service.fsproj + ``` + +4. Additionally, if you want to test changes to the FSharp.Core (note that the relative path can be different) + + ```shell + dotnet add reference ..\..\..\src\FSharp.Core\FSharp.Core.fsproj + ``` + + And the following property has to be added to `FcsBench.fsproj`: + + ```xml + + true + + ``` + +5. Add a new benchmark for FCS/FSharp.Core by editing `Program.fs`. + + ```fsharp + open System.IO + open FSharp.Compiler.CodeAnalysis + open FSharp.Compiler.Diagnostics + open FSharp.Compiler.Text + open BenchmarkDotNet.Attributes + open BenchmarkDotNet.Running + + [] + type CompilerService() = + let mutable checkerOpt = None + let mutable sourceOpt = None + + let parsingOptions = + { + SourceFiles = [|"CheckExpressions.fs"|] + ConditionalDefines = [] + DiagnosticOptions = FSharpDiagnosticOptions.Default + LangVersionText = "default" + IsInteractive = false + LightSyntax = None + CompilingFsLib = false + IsExe = false + } + + [] + member _.Setup() = + match checkerOpt with + | None -> + checkerOpt <- Some(FSharpChecker.Create(projectCacheSize = 200)) + | _ -> () + + match sourceOpt with + | None -> + sourceOpt <- Some <| SourceText.ofString(File.ReadAllText("""C:\Users\vlza\code\fsharp\src\Compiler\Checking\CheckExpressions.fs""")) + | _ -> () + + [] + member _.ParsingCheckExpressionsFs() = + match checkerOpt, sourceOpt with + | None, _ -> failwith "no checker" + | _, None -> failwith "no source" + | Some(checker), Some(source) -> + let results = checker.ParseFile("CheckExpressions.fs", source, parsingOptions) |> Async.RunSynchronously + if results.ParseHadErrors then failwithf "parse had errors: %A" results.Diagnostics + + [] + member _.ParsingCheckExpressionsFsSetup() = + match checkerOpt with + | None -> failwith "no checker" + | Some(checker) -> + checker.InvalidateAll() + checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() + checker.ParseFile("dummy.fs", SourceText.ofString "dummy", parsingOptions) |> Async.RunSynchronously |> ignore + + [] + let main _ = + BenchmarkRunner.Run() |> ignore + 0 + ``` + + For more detailed information about available BenchmarkDotNet options, please refer to [BenchmarkDotNet Documentation](https://benchmarkdotnet.org/articles/overview.html). + +6. Build and run the benchmark. + + ```shell + dotnet build -c Release + dotnet run -c Release + ``` + +7. You can find results in `.\BenchmarkDotNet.Artifacts\results\` in the current benchmark project directory. + + ```shell + > ls .\BenchmarkDotNet.Artifacts\results\ + + Directory: C:\Users\vlza\code\fsharp\tests\benchmarks\FCSBenchmarks\FcsBench\BenchmarkDotNet.Artifacts\results + + Mode LastWriteTime Length Name + ---- ------------- ------ ---- + -a--- 4/25/2022 1:42 PM 638 Program.CompilerService-report-github.md + -a--- 4/25/2022 1:42 PM 1050 Program.CompilerService-report.csv + -a--- 4/25/2022 1:42 PM 1169 Program.CompilerService-report.html + ``` + + *-report-github.md can be used to post benchmark results to GitHub issue/PR/discussion or RFC. + + *-report.csv can be used for comparison purposes. + + **Example output:** + + ``` ini + + BenchmarkDotNet=v0.13.1, OS=Windows 10.0.25102 + Intel Core i7-8750H CPU 2.20GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores + .NET SDK=6.0.200 + [Host] : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT DEBUG + Job-GDIBXX : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT + + InvocationCount=1 UnrollFactor=1 + + ``` + + | Method | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Allocated | + |-------------------------- |---------:|--------:|--------:|---------:|----------:|----------:|----------:| + | ParsingCheckExpressionsFs | 199.4 ms | 3.84 ms | 9.78 ms | 195.5 ms | 4000.0000 | 1000.0000 | 28 MB | + +8. Repeat for any number of changes you would like to test. +9. **Optionally:** benchmark code and results can be included as part of the PR for future reference. -You can find this document under 'tests/benchmarks/README.md'. diff --git a/tests/benchmarks/SmokeTestBenchmarks.ps1 b/tests/benchmarks/SmokeTestBenchmarks.ps1 new file mode 100644 index 00000000000..710d8c3e2ba --- /dev/null +++ b/tests/benchmarks/SmokeTestBenchmarks.ps1 @@ -0,0 +1,21 @@ +# Smoke test for checking that some (faster) benchmarks work. +# The test is successful if all the benchmarks run and produce results. +# The actual numbers produced aren't accurate. + +function Run { + param ( + [string[]]$path + ) + + dotnet run ` + --project $path ` + -c release ` + --no-build ` + --job Dry ` + --allCategories short ` + --stopOnFirstError +} + +Run "FCSBenchmarks/BenchmarkComparison/HistoricalBenchmark.fsproj" +Run "FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj" +Run "FCSBenchmarks/FCSSourceFiles/FCSSourceFiles.fsproj" \ No newline at end of file diff --git a/tests/benchmarks/SmokeTestBenchmarks.sh b/tests/benchmarks/SmokeTestBenchmarks.sh new file mode 100644 index 00000000000..0da2c8f2213 --- /dev/null +++ b/tests/benchmarks/SmokeTestBenchmarks.sh @@ -0,0 +1,18 @@ +# Smoke test for checking that some (faster) benchmarks work. +# The test is successful if all the benchmarks run and produce results. +# The actual numbers produced aren't accurate. + +run() { + local path=$1 + dotnet run \ + --project $path \ + -c release \ + --no-build \ + --job Dry \ + --allCategories short \ + --stopOnFirstError +} + +run "FCSBenchmarks/BenchmarkComparison/HistoricalBenchmark.fsproj" +run "FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj" +run "FCSBenchmarks/FCSSourceFiles/FCSSourceFiles.fsproj" diff --git a/tests/fsharp/Compiler/Language/StructActivePatternTests.fs b/tests/fsharp/Compiler/Language/StructActivePatternTests.fs index 8780e6c8469..c1e71755e1b 100644 --- a/tests/fsharp/Compiler/Language/StructActivePatternTests.fs +++ b/tests/fsharp/Compiler/Language/StructActivePatternTests.fs @@ -179,11 +179,8 @@ let (|Foo|_|) x = ValueNone """ [|(FSharpDiagnosticSeverity.Error, 842, (2, 3, 2, 9), "This attribute is not valid for use on this language element"); - (FSharpDiagnosticSeverity.Error, 1, (2, 1, 3, 16), - "This expression was expected to have type - ''a option' -but here has type - ''b voption' ")|] + (FSharpDiagnosticSeverity.Error, 3350, (2, 1, 3, 16), + "Feature 'Boolean-returning and return-type-directed partial active patterns' is not available in F# 8.0. Please use language version 'PREVIEW' or greater.")|] [] let ``StructAttribute not allowed on other bindings than partial active pattern definitions`` () = diff --git a/tests/fsharpqa/Source/Diagnostics/NONTERM/tuplewithlazy01.fs b/tests/fsharpqa/Source/Diagnostics/NONTERM/tuplewithlazy01.fs index 8e2724f49ce..cfbd4185bd4 100644 --- a/tests/fsharpqa/Source/Diagnostics/NONTERM/tuplewithlazy01.fs +++ b/tests/fsharpqa/Source/Diagnostics/NONTERM/tuplewithlazy01.fs @@ -1,8 +1,8 @@ // #Regression #Diagnostics // Regression test for DevDiv:64339 // Note that the bug still repros in CHK/DBG bits - we will knownfail it -//Unexpected keyword 'lazy' in type definition$ -//Unexpected keyword 'lazy'$ +//Unexpected keyword 'lazy' in pattern$ +//Unexpected keyword 'lazy' in pattern$ // 5 elements -> ok type Ok(a, b, c, d, e : lazy) = class end diff --git a/tests/fsharpqa/Source/Diagnostics/NONTERM/tuplewithlazy01b.fs b/tests/fsharpqa/Source/Diagnostics/NONTERM/tuplewithlazy01b.fs index 8e2724f49ce..cfbd4185bd4 100644 --- a/tests/fsharpqa/Source/Diagnostics/NONTERM/tuplewithlazy01b.fs +++ b/tests/fsharpqa/Source/Diagnostics/NONTERM/tuplewithlazy01b.fs @@ -1,8 +1,8 @@ // #Regression #Diagnostics // Regression test for DevDiv:64339 // Note that the bug still repros in CHK/DBG bits - we will knownfail it -//Unexpected keyword 'lazy' in type definition$ -//Unexpected keyword 'lazy'$ +//Unexpected keyword 'lazy' in pattern$ +//Unexpected keyword 'lazy' in pattern$ // 5 elements -> ok type Ok(a, b, c, d, e : lazy) = class end diff --git a/tests/scripts/update-baselines.fsx b/tests/scripts/update-baselines.fsx index 06e9c13ef80..6cc8631d898 100644 --- a/tests/scripts/update-baselines.fsx +++ b/tests/scripts/update-baselines.fsx @@ -1,13 +1,13 @@ open System open System.IO -// this script is usefull for tests using a .bsl file (baseline) containing expected compiler output +// this script is useful for tests using a .bsl file (baseline) containing expected compiler output // which is matched against .vserr or .err file aside once the test has run // the script replaces all the .bsl/.bslpp with either .err or .vserr let diff path1 path2 = let result = System.Text.StringBuilder() - let append s = result.AppendLine s |> ignore + let append (s: string) = result.AppendLine s |> ignore if not <| File.Exists(path1) then failwithf "Invalid path %s" path1 if not <| File.Exists(path2) then failwithf "Invalid path %s" path2 diff --git a/tests/service/CSharpProjectAnalysis.fs b/tests/service/CSharpProjectAnalysis.fs index f23f3038e42..69a13799d7f 100644 --- a/tests/service/CSharpProjectAnalysis.fs +++ b/tests/service/CSharpProjectAnalysis.fs @@ -27,7 +27,7 @@ let internal getProjectReferences (content: string, dllFiles, libDirs, otherFlag let projFileName = Path.ChangeExtension(base1, ".fsproj") FileSystem.OpenFileForWriteShim(fileName1).Write(content) let options = - checker.GetProjectOptionsFromCommandLineArgs(projFileName, + { checker.GetProjectOptionsFromCommandLineArgs(projFileName, [| yield "--debug:full" yield "--define:DEBUG" yield "--optimize-" @@ -41,8 +41,7 @@ let internal getProjectReferences (content: string, dllFiles, libDirs, otherFlag yield "-r:"+dllFile for libDir in libDirs do yield "-I:"+libDir - yield! otherFlags - yield fileName1 |]) + yield! otherFlags |]) with SourceFiles = [| fileName1 |] } let results = checker.ParseAndCheckProject(options) |> Async.RunImmediate if results.HasCriticalErrors then let builder = System.Text.StringBuilder() diff --git a/tests/service/Common.fs b/tests/service/Common.fs index a8d4782de0d..8516948626a 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -31,7 +31,7 @@ type Async with task.Result // Create one global interactive checker instance -let checker = FSharpChecker.Create() +let checker = FSharpChecker.Create(useTransparentCompiler=FSharp.Compiler.CompilerConfig.FSharpExperimentalFeaturesEnabledAutomatically) type TempFile(ext, contents: string) = let tmpFile = Path.ChangeExtension(tryCreateTemporaryFileName (), ext) @@ -137,8 +137,8 @@ let mkTestFileAndOptions source additionalArgs = let fileSource1 = "module M" FileSystem.OpenFileForWriteShim(fileName).Write(fileSource1) - let args = Array.append (mkProjectCommandLineArgs (dllName, [fileName])) additionalArgs - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let args = Array.append (mkProjectCommandLineArgs (dllName, [])) additionalArgs + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = [| fileName |] } fileName, options let parseAndCheckFile fileName source options = @@ -158,7 +158,7 @@ let parseAndCheckScriptWithOptions (file:string, input, opts) = let fname = Path.Combine(path, Path.GetFileName(file)) let dllName = Path.ChangeExtension(fname, ".dll") let projName = Path.ChangeExtension(fname, ".fsproj") - let args = mkProjectCommandLineArgsForScript (dllName, [file]) + let args = mkProjectCommandLineArgsForScript (dllName, []) printfn "file = %A, args = %A" file args checker.GetProjectOptionsFromCommandLineArgs (projName, args) @@ -171,7 +171,7 @@ let parseAndCheckScriptWithOptions (file:string, input, opts) = //printfn "projectOptions = %A" projectOptions #endif - let projectOptions = { projectOptions with OtherOptions = Array.append opts projectOptions.OtherOptions } + let projectOptions = { projectOptions with OtherOptions = Array.append opts projectOptions.OtherOptions; SourceFiles = [|file|] } let parseResult, typedRes = checker.ParseAndCheckFileInProject(file, 0, SourceText.ofString input, projectOptions) |> Async.RunImmediate // if parseResult.Errors.Length > 0 then diff --git a/tests/service/ExprTests.fs b/tests/service/ExprTests.fs index 662a60edc80..7b35568e38d 100644 --- a/tests/service/ExprTests.fs +++ b/tests/service/ExprTests.fs @@ -364,8 +364,8 @@ let createOptionsAux fileSources extraArgs = Utils.createTempDir() for fileSource: string, fileName in List.zip fileSources fileNames do FileSystem.OpenFileForWriteShim(fileName).Write(fileSource) - let args = [| yield! extraArgs; yield! mkProjectCommandLineArgs (dllName, fileNames) |] - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let args = [| yield! extraArgs; yield! mkProjectCommandLineArgs (dllName, []) |] + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames |> List.toArray } Utils.cleanupTempFiles (fileNames @ [dllName; projFileName]), options @@ -732,11 +732,13 @@ let ignoreTestIfStackOverflowExpected () = #endif /// This test is run in unison with its optimized counterpart below +[] +[] [] -let ``Test Unoptimized Declarations Project1`` () = +let ``Test Unoptimized Declarations Project1`` useTransparentCompiler = let cleanup, options = Project1.createOptionsWithArgs [ "--langversion:preview" ] use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -871,11 +873,13 @@ let ``Test Unoptimized Declarations Project1`` () = () +[] +[] [] -let ``Test Optimized Declarations Project1`` () = +let ``Test Optimized Declarations Project1`` useTransparentCompiler = let cleanup, options = Project1.createOptionsWithArgs [ "--langversion:preview" ] use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -1017,7 +1021,7 @@ let testOperators dnName fsName excludedTests expectedUnoptimized expectedOptimi let filePath = Utils.getTempFilePathChangeExt tempFileName ".fs" let dllPath =Utils.getTempFilePathChangeExt tempFileName ".dll" let projFilePath = Utils.getTempFilePathChangeExt tempFileName ".fsproj" - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=true) begin use _cleanup = Utils.cleanupTempFiles [filePath; dllPath; projFilePath] @@ -1027,9 +1031,9 @@ let testOperators dnName fsName excludedTests expectedUnoptimized expectedOptimi let fileSource = excludedTests |> List.fold replace source FileSystem.OpenFileForWriteShim(filePath).Write(fileSource) - let args = mkProjectCommandLineArgsSilent (dllPath, [filePath]) + let args = mkProjectCommandLineArgsSilent (dllPath, []) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFilePath, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFilePath, args) with SourceFiles = [|filePath|] } let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate let referencedAssemblies = wholeProjectResults.ProjectContext.GetReferencedAssemblies() @@ -3206,12 +3210,14 @@ let BigSequenceExpression(outFileOpt,docFileOpt,baseAddressOpt) = let createOptions() = createOptionsAux [fileSource1] [] +[] +[] [] -let ``Test expressions of declarations stress big expressions`` () = +let ``Test expressions of declarations stress big expressions`` useTransparentCompiler = ignoreTestIfStackOverflowExpected () let cleanup, options = ProjectStressBigExpressions.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate wholeProjectResults.Diagnostics.Length |> shouldEqual 0 @@ -3223,12 +3229,14 @@ let ``Test expressions of declarations stress big expressions`` () = printDeclarations None (List.ofSeq file1.Declarations) |> Seq.toList |> ignore +[] +[] [] -let ``Test expressions of optimized declarations stress big expressions`` () = +let ``Test expressions of optimized declarations stress big expressions`` useTransparentCompiler = ignoreTestIfStackOverflowExpected () let cleanup, options = ProjectStressBigExpressions.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate wholeProjectResults.Diagnostics.Length |> shouldEqual 0 @@ -3284,11 +3292,13 @@ let f8() = callXY (D()) (C()) let createOptions() = createOptionsAux [fileSource1] ["--langversion:7.0"] +[] +[] [] -let ``Test ProjectForWitnesses1`` () = +let ``Test ProjectForWitnesses1`` useTransparentCompiler = let cleanup, options = ProjectForWitnesses1.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3328,11 +3338,13 @@ let ``Test ProjectForWitnesses1`` () = |> shouldPairwiseEqual expected +[] +[] [] -let ``Test ProjectForWitnesses1 GetWitnessPassingInfo`` () = +let ``Test ProjectForWitnesses1 GetWitnessPassingInfo`` useTransparentCompiler = let cleanup, options = ProjectForWitnesses1.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3408,11 +3420,13 @@ type MyNumberWrapper = let createOptions() = createOptionsAux [fileSource1] ["--langversion:7.0"] +[] +[] [] -let ``Test ProjectForWitnesses2`` () = +let ``Test ProjectForWitnesses2`` useTransparentCompiler = let cleanup, options = ProjectForWitnesses2.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3428,7 +3442,6 @@ let ``Test ProjectForWitnesses2`` () = "member Neg(p) = {x = Operators.op_UnaryNegation (fun arg0_0 -> LanguagePrimitives.UnaryNegationDynamic (arg0_0),p.x); y = Operators.op_UnaryNegation (fun arg0_0 -> LanguagePrimitives.UnaryNegationDynamic (arg0_0),p.y)} @ (7,34--7,56)"; "member op_Addition(p1,p2) = {x = Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),p1.x,p2.x); y = Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),p1.y,p2.y)} @ (8,33--8,68)"; "type MyNumber"; - "member get_IsMyNumber(this) (unitArg) = (if this.IsMyNumber then True else False) @ (10,5--10,13)"; "member get_Zero(unitVar0) = MyNumber(0) @ (12,25--12,35)"; "member op_Addition(_arg1,_arg2) = let x: Microsoft.FSharp.Core.int = _arg1.Item in let y: Microsoft.FSharp.Core.int = _arg2.Item in MyNumber(Operators.op_Addition (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.AdditionDynamic (arg0_0,arg1_0),x,y)) @ (13,23--13,33)"; "member DivideByInt(_arg3,i) = let x: Microsoft.FSharp.Core.int = _arg3.Item in MyNumber(Operators.op_Division (fun arg0_0 -> fun arg1_0 -> LanguagePrimitives.DivisionDynamic (arg0_0,arg1_0),x,i)) @ (15,31--15,41)"; @@ -3465,11 +3478,13 @@ let s2 = sign p1 let createOptions() = createOptionsAux [fileSource1] ["--langversion:7.0"] +[] +[] [] -let ``Test ProjectForWitnesses3`` () = +let ``Test ProjectForWitnesses3`` useTransparentCompiler = let cleanup, options = createOptionsAux [ ProjectForWitnesses3.fileSource1 ] ["--langversion:7.0"] use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3496,11 +3511,13 @@ let ``Test ProjectForWitnesses3`` () = actual |> shouldPairwiseEqual expected +[] +[] [] -let ``Test ProjectForWitnesses3 GetWitnessPassingInfo`` () = +let ``Test ProjectForWitnesses3 GetWitnessPassingInfo`` useTransparentCompiler = let cleanup, options = ProjectForWitnesses3.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3559,11 +3576,13 @@ let isNullQuoted (ts : 't[]) = let createOptions() = createOptionsAux [fileSource1] ["--langversion:7.0"] +[] +[] [] -let ``Test ProjectForWitnesses4 GetWitnessPassingInfo`` () = +let ``Test ProjectForWitnesses4 GetWitnessPassingInfo`` useTransparentCompiler = let cleanup, options = ProjectForWitnesses4.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do diff --git a/tests/service/ModuleReaderCancellationTests.fs b/tests/service/ModuleReaderCancellationTests.fs index 434ddca02db..a401c637fe6 100644 --- a/tests/service/ModuleReaderCancellationTests.fs +++ b/tests/service/ModuleReaderCancellationTests.fs @@ -135,7 +135,7 @@ let createPreTypeDefs typeData = |> Array.ofList |> Array.map (fun data -> PreTypeDef data :> ILPreTypeDef) -let referenceReaderProject getPreTypeDefs (cancelOnModuleAccess: bool) options = +let referenceReaderProject getPreTypeDefs (cancelOnModuleAccess: bool) (options: FSharpProjectOptions) = let reader = new ModuleReader("Reference", mkILTypeDefsComputed getPreTypeDefs, cancelOnModuleAccess) let project = FSharpReferencedProject.ILModuleReference( diff --git a/tests/service/MultiProjectAnalysisTests.fs b/tests/service/MultiProjectAnalysisTests.fs index eab86f98324..350cf73095e 100644 --- a/tests/service/MultiProjectAnalysisTests.fs +++ b/tests/service/MultiProjectAnalysisTests.fs @@ -23,6 +23,7 @@ open TestFramework let toIList (x: _ array) = x :> IList<_> let numProjectsForStressTest = 100 let internal checker = FSharpChecker.Create(projectCacheSize=numProjectsForStressTest + 10) +let internal transparentCompilerChecker = FSharpChecker.Create(projectCacheSize=numProjectsForStressTest + 10, useTransparentCompiler=true) /// Extract range info let internal tups (m:range) = (m.StartLine, m.StartColumn), (m.EndLine, m.EndColumn) @@ -67,9 +68,9 @@ type U = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -95,9 +96,9 @@ let x = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } // A project referencing two sub-projects @@ -120,19 +121,24 @@ let u = Case1 3 """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) let options = - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with + SourceFiles = fileNames OtherOptions = Array.append options.OtherOptions [| ("-r:" + Project1A.dllName); ("-r:" + Project1B.dllName) |] ReferencedProjects = [| FSharpReferencedProject.FSharpReference(Project1A.dllName, Project1A.options); FSharpReferencedProject.FSharpReference(Project1B.dllName, Project1B.options); |] } let cleanFileName a = if a = fileName1 then "file1" else "??" [] -let ``Test multi project 1 basic`` () = +[] +[] +let ``Test multi project 1 basic`` useTransparentCompiler = + let checker = if useTransparentCompiler then transparentCompilerChecker else checker + let wholeProjectResults = checker.ParseAndCheckProject(MultiProject1.options) |> Async.RunImmediate [ for x in wholeProjectResults.AssemblySignature.Entities -> x.DisplayName ] |> shouldEqual ["MultiProject1"] @@ -144,7 +150,11 @@ let ``Test multi project 1 basic`` () = |> shouldEqual ["p"; "c"; "u"] [] -let ``Test multi project 1 all symbols`` () = +[] +[] +let ``Test multi project 1 all symbols`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker let p1A = checker.ParseAndCheckProject(Project1A.options) |> Async.RunImmediate let p1B = checker.ParseAndCheckProject(Project1B.options) |> Async.RunImmediate @@ -182,7 +192,11 @@ let ``Test multi project 1 all symbols`` () = usesOfx1FromProject1AInMultiProject1 |> shouldEqual usesOfx1FromMultiProject1InMultiProject1 [] -let ``Test multi project 1 xmldoc`` () = +[] +[] +let ``Test multi project 1 xmldoc`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker let p1A = checker.ParseAndCheckProject(Project1A.options) |> Async.RunImmediate let p1B = checker.ParseAndCheckProject(Project1B.options) |> Async.RunImmediate @@ -284,9 +298,9 @@ let p = C.Print() let baseName = tryCreateTemporaryFileName () let dllName = Path.ChangeExtension(baseName, ".dll") let projFileName = Path.ChangeExtension(baseName, ".fsproj") - let fileNames = [fileName1 ] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } yield { ModuleName = moduleName; FileName=fileName1; Options = options; DllName=dllName } ] let jointProject = @@ -305,10 +319,10 @@ let p = (""" + String.concat ",\r\n " [ for p in projects -> p.ModuleName + ".v" ] + ")" FileSystem.OpenFileForWriteShim(fileName).Write(fileSource) - let fileNames = [fileName] + let fileNames = [|fileName|] let args = mkProjectCommandLineArgs (dllName, fileNames) let options = - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with OtherOptions = Array.append options.OtherOptions [| for p in projects -> ("-r:" + p.DllName) |] ReferencedProjects = [| for p in projects -> FSharpReferencedProject.FSharpReference(p.DllName, p.Options); |] } @@ -319,14 +333,16 @@ let p = (""" |> function Some x -> x | None -> if a = jointProject.FileName then "fileN" else "??" - let makeCheckerForStressTest ensureBigEnough = + let makeCheckerForStressTest ensureBigEnough useTransparentCompiler = let size = (if ensureBigEnough then numProjectsForStressTest + 10 else numProjectsForStressTest / 2 ) - FSharpChecker.Create(projectCacheSize=size) + FSharpChecker.Create(projectCacheSize=size, useTransparentCompiler=useTransparentCompiler) [] -let ``Test ManyProjectsStressTest basic`` () = +[] +[] +let ``Test ManyProjectsStressTest basic`` useTransparentCompiler = - let checker = ManyProjectsStressTest.makeCheckerForStressTest true + let checker = ManyProjectsStressTest.makeCheckerForStressTest true useTransparentCompiler let wholeProjectResults = checker.ParseAndCheckProject(ManyProjectsStressTest.jointProject.Options) |> Async.RunImmediate @@ -338,9 +354,11 @@ let ``Test ManyProjectsStressTest basic`` () = |> shouldEqual ["p"] [] -let ``Test ManyProjectsStressTest cache too small`` () = +[] +[] +let ``Test ManyProjectsStressTest cache too small`` useTransparentCompiler = - let checker = ManyProjectsStressTest.makeCheckerForStressTest false + let checker = ManyProjectsStressTest.makeCheckerForStressTest false useTransparentCompiler let wholeProjectResults = checker.ParseAndCheckProject(ManyProjectsStressTest.jointProject.Options) |> Async.RunImmediate @@ -352,9 +370,11 @@ let ``Test ManyProjectsStressTest cache too small`` () = |> shouldEqual ["p"] [] -let ``Test ManyProjectsStressTest all symbols`` () = +[] +[] +let ``Test ManyProjectsStressTest all symbols`` useTransparentCompiler = - let checker = ManyProjectsStressTest.makeCheckerForStressTest true + let checker = ManyProjectsStressTest.makeCheckerForStressTest true useTransparentCompiler for i in 1 .. 10 do printfn "stress test iteration %d (first may be slow, rest fast)" i let projectsResults = [ for p in ManyProjectsStressTest.projects -> p, checker.ParseAndCheckProject(p.Options) |> Async.RunImmediate ] @@ -397,11 +417,11 @@ let x = "F#" let cleanFileName a = if a = fileName1 then "Project1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let getOptions() = let args = mkProjectCommandLineArgs (dllName, fileNames) - checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } module internal MultiProjectDirty2 = @@ -422,17 +442,21 @@ let z = Project1.x let cleanFileName a = if a = fileName1 then "Project2" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let getOptions() = let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with OtherOptions = Array.append options.OtherOptions [| ("-r:" + MultiProjectDirty1.dllName) |] ReferencedProjects = [| FSharpReferencedProject.FSharpReference(MultiProjectDirty1.dllName, MultiProjectDirty1.getOptions()) |] } [] -let ``Test multi project symbols should pick up changes in dependent projects`` () = +[] +[] +let ``Test multi project symbols should pick up changes in dependent projects`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker // register to count the file checks let count = ref 0 @@ -614,9 +638,9 @@ type C() = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } //Project2A.fileSource1 // A project referencing Project2A @@ -633,10 +657,10 @@ let v = Project2A.C().InternalMember // access an internal symbol """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, [||]) let options = - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with OtherOptions = Array.append options.OtherOptions [| ("-r:" + Project2A.dllName); |] ReferencedProjects = [| FSharpReferencedProject.FSharpReference(Project2A.dllName, Project2A.options); |] } @@ -657,17 +681,21 @@ let v = Project2A.C().InternalMember // access an internal symbol """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, [||]) let options = - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with OtherOptions = Array.append options.OtherOptions [| ("-r:" + Project2A.dllName); |] ReferencedProjects = [| FSharpReferencedProject.FSharpReference(Project2A.dllName, Project2A.options); |] } let cleanFileName a = if a = fileName1 then "file1" else "??" [] -let ``Test multi project2 errors`` () = +[] +[] +let ``Test multi project2 errors`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker let wholeProjectResults = checker.ParseAndCheckProject(Project2B.options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -680,9 +708,12 @@ let ``Test multi project2 errors`` () = wholeProjectResultsC.Diagnostics.Length |> shouldEqual 1 - [] -let ``Test multi project 2 all symbols`` () = +[] +[] +let ``Test multi project 2 all symbols`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker let mpA = checker.ParseAndCheckProject(Project2A.options) |> Async.RunImmediate let mpB = checker.ParseAndCheckProject(Project2B.options) |> Async.RunImmediate @@ -725,9 +756,9 @@ let (|DivisibleBy|_|) by n = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } // A project referencing a sub-project @@ -750,17 +781,22 @@ let fizzBuzz = function """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, [||]) let options = - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with + SourceFiles = fileNames OtherOptions = Array.append options.OtherOptions [| ("-r:" + Project3A.dllName) |] ReferencedProjects = [| FSharpReferencedProject.FSharpReference(Project3A.dllName, Project3A.options) |] } let cleanFileName a = if a = fileName1 then "file1" else "??" [] -let ``Test multi project 3 whole project errors`` () = +[] +[] +let ``Test multi project 3 whole project errors`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker let wholeProjectResults = checker.ParseAndCheckProject(MultiProject3.options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -769,7 +805,11 @@ let ``Test multi project 3 whole project errors`` () = wholeProjectResults.Diagnostics.Length |> shouldEqual 0 [] -let ``Test active patterns' XmlDocSig declared in referenced projects`` () = +[] +[] +let ``Test active patterns' XmlDocSig declared in referenced projects`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker let wholeProjectResults = checker.ParseAndCheckProject(MultiProject3.options) |> Async.RunImmediate let backgroundParseResults1, backgroundTypedParse1 = @@ -799,7 +839,12 @@ let ``Test active patterns' XmlDocSig declared in referenced projects`` () = [] -let ``In-memory cross-project references to projects using generative type provides should fallback to on-disk references`` () = +[] +[] +let ``In-memory cross-project references to projects using generative type provides should fallback to on-disk references`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker + // The type provider and its dependency are compiled as part of the solution build #if DEBUG let csDLL = __SOURCE_DIRECTORY__ + @"/../../artifacts/bin/TestTP/Debug/netstandard2.0/CSharp_Analysis.dll" diff --git a/tests/service/ProjectAnalysisTests.fs b/tests/service/ProjectAnalysisTests.fs index dab349fd0ab..a1a58a709b2 100644 --- a/tests/service/ProjectAnalysisTests.fs +++ b/tests/service/ProjectAnalysisTests.fs @@ -90,9 +90,10 @@ let mmmm2 : M.CAbbrev = new M.CAbbrev() // note, these don't count as uses of C FileSystem.OpenFileForWriteShim(fileName2).Write(fileSource2Text) let fileNames = [fileName1; fileName2] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) - let parsingOptions, _ = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames |> List.toArray } + let parsingOptions', _ = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) + let parsingOptions = { parsingOptions' with SourceFiles = fileNames |> List.toArray } let cleanFileName a = if a = fileName1 then "file1" else if a = fileName2 then "file2" else "??" [] @@ -675,9 +676,9 @@ let _ = GenericFunction(3, 4) """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -713,11 +714,11 @@ let ``Test project2 all symbols in signature`` () = "DUWithNormalFields"; "member get_IsD"; "member get_IsDU1"; "member get_IsDU2"; "property IsD"; "property IsDU1"; "property IsDU2"; "DU1"; "field Item1"; "field Item2"; "DU2"; "field Item1"; "field Item2"; "D"; "field Item1"; - "field Item2"; "DUWithNamedFields"; "member get_IsDU"; "property IsDU"; "DU"; + "field Item2"; "DUWithNamedFields"; "DU"; "field x"; "field y"; "GenericClass`1"; "generic parameter T"; "member .ctor"; "member GenericMethod"; "generic parameter U"] |> List.sort - shouldEqual e r + shouldPairwiseEqual e r [] let ``Test project2 all uses of all signature symbols`` () = @@ -735,12 +736,10 @@ let ``Test project2 all uses of all signature symbols`` () = ("generic parameter T", [("file1", ((22, 23), (22, 25))); ("file1", ((22, 30), (22, 32))); ("file1", ((22, 45), (22, 47))); ("file1", ((22, 50), (22, 52)))]); - ("member get_IsD", []); - ("member get_IsDU", []); + ("member get_IsD", []); ("member get_IsDU1", []); ("member get_IsDU2", []); - ("property IsD", []); - ("property IsDU", []); + ("property IsD", []); ("property IsDU1", []); ("property IsDU2", []); ("DUWithNormalFields", [("file1", ((3, 5), (3, 23)))]); @@ -928,9 +927,9 @@ let getM (foo: IFoo) = foo.InterfaceMethod("d") """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -1297,9 +1296,9 @@ let inline twice(x : ^U, y : ^U) = x + y """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -1471,9 +1470,9 @@ let parseNumeric str = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -1683,9 +1682,9 @@ let f () = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -1739,9 +1738,9 @@ let x2 = C.M(arg1 = 3, arg2 = 4, ?arg3 = Some 5) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -1800,9 +1799,9 @@ let x = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -1880,9 +1879,9 @@ let inline check< ^T when ^T : (static member IsInfinity : ^T -> bool)> (num: ^T let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -1959,9 +1958,9 @@ C.M("http://goo", query = 1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2039,9 +2038,9 @@ let fff (x:System.Collections.Generic.Dictionary.Enumerator) = () let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2108,9 +2107,9 @@ let x2 = query { for i in 0 .. 100 do let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2175,9 +2174,9 @@ let x3 = new System.DateTime() let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2334,9 +2333,9 @@ let x2 = S(3) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2401,9 +2400,9 @@ let f x = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2488,9 +2487,9 @@ and G = Case1 | Case2 of int FileSystem.OpenFileForWriteShim(sigFileName1).Write(sigFileSource1Text) let cleanFileName a = if a = fileName1 then "file1" elif a = sigFileName1 then "sig1" else "??" - let fileNames = [sigFileName1; fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|sigFileName1; fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2754,9 +2753,9 @@ let f3 (x: System.Exception) = x.HelpLink <- "" // check use of .NET setter prop FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2841,9 +2840,9 @@ let _ = list<_>.Empty FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2897,9 +2896,9 @@ let s = System.DayOfWeek.Monday FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2972,9 +2971,9 @@ type A<'T>() = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -3033,9 +3032,9 @@ let _ = { new IMyInterface with FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -3107,9 +3106,9 @@ let f5 (x: int[,,]) = () // test a multi-dimensional array FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -3254,9 +3253,9 @@ module Setter = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test Project23 whole project errors`` () = @@ -3425,9 +3424,9 @@ TypeWithProperties.StaticAutoPropGetSet <- 3 FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test Project24 whole project errors`` () = @@ -3682,12 +3681,12 @@ let _ = XmlProvider<"13">.GetSample() FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = - [| yield! mkProjectCommandLineArgs (dllName, fileNames) + [| yield! mkProjectCommandLineArgs (dllName, []) yield @"-r:" + Path.Combine(__SOURCE_DIRECTORY__, Path.Combine("data", "FSharp.Data.dll")) yield @"-r:" + sysLib "System.Xml.Linq" |] - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] #if NETCOREAPP @@ -3822,9 +3821,9 @@ type Class() = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -3912,9 +3911,9 @@ type CFooImpl() = """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test project27 whole project errors`` () = @@ -3977,9 +3976,9 @@ type Use() = """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } #if !NO_TYPEPROVIDERS [] let ``Test project28 all symbols in signature`` () = @@ -4055,9 +4054,9 @@ let f (x: INotifyPropertyChanged) = failwith "" """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -4114,9 +4113,9 @@ type T() = """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let ``Test project30 whole project errors`` () = @@ -4174,10 +4173,9 @@ let g = Console.ReadKey() """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let ``Test project31 whole project errors`` () = let wholeProjectResults = checker.ParseAndCheckProject(Project31.options) |> Async.RunImmediate @@ -4317,9 +4315,9 @@ val func : int -> int FileSystem.OpenFileForWriteShim(sigFileName1).Write(sigFileSource1) let cleanFileName a = if a = fileName1 then "file1" elif a = sigFileName1 then "sig1" else "??" - let fileNames = [sigFileName1; fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|sigFileName1; fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -4385,9 +4383,9 @@ type System.Int32 with FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test Project33 whole project errors`` () = @@ -4424,17 +4422,17 @@ module internal Project34 = FileSystem.OpenFileForWriteShim(sourceFileName).Write(fileSource) let cleanFileName a = if a = sourceFileName then "file1" else "??" - let fileNames = [sourceFileName] + let fileNames = [|sourceFileName|] let args = [| - yield! mkProjectCommandLineArgs (dllName, fileNames) + yield! mkProjectCommandLineArgs (dllName, []) // We use .NET-built version of System.Data.dll since the tests depend on implementation details // i.e. the private type System.Data.Listeners may not be available on Mono. yield @"-r:" + Path.Combine(__SOURCE_DIRECTORY__, Path.Combine("data", "System.Data.dll")) |] |> Array.filter(fun arg -> not((arg.Contains("System.Data")) && not (arg.Contains(@"service\data\System.Data.dll")))) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test Project34 whole project errors`` () = @@ -4502,9 +4500,9 @@ type Test = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -4574,13 +4572,13 @@ module internal Project35b = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] #if NETCOREAPP let projPath = Path.ChangeExtension(fileName1, ".fsproj") let dllPath = Path.ChangeExtension(fileName1, ".dll") let args = mkProjectCommandLineArgs(dllPath, fileNames) let args2 = Array.append args [| "-r:notexist.dll" |] - let options = checker.GetProjectOptionsFromCommandLineArgs (projPath, args2) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projPath, args2) with SourceFiles = fileNames } #else let options = checker.GetProjectOptionsFromScript(fileName1, fileSource1) |> Async.RunImmediate |> fst #endif @@ -4641,13 +4639,15 @@ let callToOverload = B(5).Overload(4) FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) [] -let ``Test project36 FSharpMemberOrFunctionOrValue.IsBaseValue`` () = - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) +[] +[] +let ``Test project36 FSharpMemberOrFunctionOrValue.IsBaseValue`` useTransparentCompiler = + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) with SourceFiles = Project36.fileNames } let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -4660,9 +4660,11 @@ let ``Test project36 FSharpMemberOrFunctionOrValue.IsBaseValue`` () = |> fun baseSymbol -> shouldEqual true baseSymbol.IsBaseValue [] -let ``Test project36 FSharpMemberOrFunctionOrValue.IsConstructorThisValue & IsMemberThisValue`` () = - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) +[] +[] +let ``Test project36 FSharpMemberOrFunctionOrValue.IsConstructorThisValue & IsMemberThisValue`` useTransparentCompiler = + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) with SourceFiles = Project36.fileNames } let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(options) |> Async.RunImmediate let declarations = let checkedFile = wholeProjectResults.AssemblyContents.ImplementationFiles[0] @@ -4697,9 +4699,11 @@ let ``Test project36 FSharpMemberOrFunctionOrValue.IsConstructorThisValue & IsMe |> shouldEqual true [] -let ``Test project36 FSharpMemberOrFunctionOrValue.LiteralValue`` () = - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) +[] +[] +let ``Test project36 FSharpMemberOrFunctionOrValue.LiteralValue`` useTransparentCompiler = + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) with SourceFiles = Project36.fileNames } let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(options) |> Async.RunImmediate let project36Module = wholeProjectResults.AssemblySignature.Entities[0] let lit = project36Module.MembersFunctionsAndValues[0] @@ -4760,9 +4764,9 @@ namespace AttrTests do () """ FileSystem.OpenFileForWriteShim(fileName2).Write(fileSource2) - let fileNames = [fileName1; fileName2] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1; fileName2|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test project37 typeof and arrays in attribute constructor arguments`` () = @@ -4902,9 +4906,9 @@ type A<'XX, 'YY>() = member this.Property = 1 """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test project38 abstract slot information`` () = @@ -4988,9 +4992,9 @@ let uses () = C().CurriedMemberWithIncompleteSignature (failwith "x1") (failwith "x2") (failwith "x3", failwith "x4") """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let cleanFileName a = if a = fileName1 then "file1" else "??" [] @@ -5063,9 +5067,9 @@ let g (x: C) = x.IsItAnA,x.IsItAnAMethod() """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let cleanFileName a = if a = fileName1 then "file1" else "??" [] @@ -5134,9 +5138,9 @@ module M if true then Foo else Bar """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let cleanFileName a = if a = fileName1 then "file1" else "??" [] @@ -5226,9 +5230,9 @@ open File1 let test2() = test() """ FileSystem.OpenFileForWriteShim(fileName2).Write(fileSource2) - let fileNames = [fileName1;fileName2] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1;fileName2|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test project42 to ensure cached checked results are invalidated`` () = @@ -5261,21 +5265,21 @@ module internal ProjectBig = let fileSources2 = [ for i,f in fileSources -> SourceText.ofString f ] let fileNames = [ for _,f in fileNamesI -> f ] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) - let parsingOptions, _ = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) - + let parsingOptions', _ = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) + let parsingOptions = { parsingOptions' with SourceFiles = fileNames |> List.toArray } [] // Simplified repro for https://github.com/dotnet/fsharp/issues/2679 let ``add files with same name from different folders`` () = let fileNames = - [ __SOURCE_DIRECTORY__ + "/data/samename/folder1/a.fs" - __SOURCE_DIRECTORY__ + "/data/samename/folder2/a.fs" ] + [| __SOURCE_DIRECTORY__ + "/data/samename/folder1/a.fs" + __SOURCE_DIRECTORY__ + "/data/samename/folder2/a.fs" |] let projFileName = __SOURCE_DIRECTORY__ + "/data/samename/tempet.fsproj" let args = mkProjectCommandLineArgs ("test.dll", fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let wholeProjectResults = checker.ParseAndCheckProject(options) |> Async.RunImmediate let errors = wholeProjectResults.Diagnostics @@ -5308,13 +5312,15 @@ let foo (a: Foo): bool = """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] -let ``Test typed AST for struct unions`` () = // See https://github.com/fsharp/FSharp.Compiler.Service/issues/756 - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) +[] +[] +let ``Test typed AST for struct unions`` useTransparentCompiler = // See https://github.com/fsharp/FSharp.Compiler.Service/issues/756 + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(ProjectStructUnions.options) |> Async.RunImmediate let declarations = @@ -5350,9 +5356,9 @@ let x = (1 = 3.0) """ let fileSource1 = SourceText.ofString fileSource1Text FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [| fileName1 |] + let args = mkProjectCommandLineArgs (dllName, []) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test diagnostics with line directives active`` () = @@ -5401,7 +5407,9 @@ let ``Test diagnostics with line directives ignored`` () = //------------------------------------------------------ [] -let ``ParseAndCheckFileResults contains ImplFile list if FSharpChecker is created with keepAssemblyContent flag set to true``() = +[] +[] +let ``ParseAndCheckFileResults contains ImplFile list if FSharpChecker is created with keepAssemblyContent flag set to true`` useTransparentCompiler = let fileName1 = Path.ChangeExtension(tryCreateTemporaryFileName (), ".fs") let base2 = tryCreateTemporaryFileName () @@ -5414,10 +5422,10 @@ type A(i:int) = let fileSource1 = SourceText.ofString fileSource1Text FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let fileCheckResults = keepAssemblyContentsChecker.ParseAndCheckFileInProject(fileName1, 0, fileSource1, options) |> Async.RunImmediate @@ -5459,7 +5467,9 @@ let ``#4030, Incremental builder creation warnings`` (args, errorSeverities) = //------------------------------------------------------ [] -let ``Unused opens in rec module smoke test 1``() = +[] +[] +let ``Unused opens in rec module smoke test 1`` useTransparentCompiler = let fileName1 = Path.ChangeExtension(tryCreateTemporaryFileName (), ".fs") let base2 = tryCreateTemporaryFileName () @@ -5505,10 +5515,10 @@ type UseTheThings(i:int) = let fileSource1 = SourceText.ofString fileSource1Text FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let fileCheckResults = keepAssemblyContentsChecker.ParseAndCheckFileInProject(fileName1, 0, fileSource1, options) |> Async.RunImmediate @@ -5532,7 +5542,9 @@ type UseTheThings(i:int) = unusedOpensData |> shouldEqual expected [] -let ``Unused opens in non rec module smoke test 1``() = +[] +[] +let ``Unused opens in non rec module smoke test 1`` useTransparentCompiler = let fileName1 = Path.ChangeExtension(tryCreateTemporaryFileName (), ".fs") let base2 = tryCreateTemporaryFileName () @@ -5578,10 +5590,10 @@ type UseTheThings(i:int) = let fileSource1 = SourceText.ofString fileSource1Text FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let fileCheckResults = keepAssemblyContentsChecker.ParseAndCheckFileInProject(fileName1, 0, fileSource1, options) |> Async.RunImmediate @@ -5605,7 +5617,9 @@ type UseTheThings(i:int) = unusedOpensData |> shouldEqual expected [] -let ``Unused opens smoke test auto open``() = +[] +[] +let ``Unused opens smoke test auto open`` useTransparentCompiler = let fileName1 = Path.ChangeExtension(tryCreateTemporaryFileName (), ".fs") let base2 = tryCreateTemporaryFileName () @@ -5659,10 +5673,10 @@ module M2 = let fileSource1 = SourceText.ofString fileSource1Text FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let fileCheckResults = keepAssemblyContentsChecker.ParseAndCheckFileInProject(fileName1, 0, fileSource1, options) |> Async.RunImmediate diff --git a/tests/service/ServiceUntypedParseTests.fs b/tests/service/ServiceUntypedParseTests.fs index 7255773d2e0..25417a31d96 100644 --- a/tests/service/ServiceUntypedParseTests.fs +++ b/tests/service/ServiceUntypedParseTests.fs @@ -839,7 +839,7 @@ add2 1 2 | Some range -> range |> tups - |> shouldEqual ((3, 18), (3, 18)) + |> shouldEqual ((3, 17), (3, 18)) [] let ``TryRangeOfFunctionOrMethodBeingApplied - inside CE``() = diff --git a/tests/service/Symbols.fs b/tests/service/Symbols.fs index acbc02f6c98..42427a3a89b 100644 --- a/tests/service/Symbols.fs +++ b/tests/service/Symbols.fs @@ -326,6 +326,60 @@ open System """ findSymbolUseByName "IDisposable" checkResults |> ignore + + [] + let ``Interface 04 - Type arg`` () = + let _, checkResults = getParseAndCheckResults """ +open System.Collections.Generic + +IList +""" + let symbolUse = findSymbolUseByName "IList`1" checkResults + let symbol = symbolUse.Symbol :?> FSharpEntity + let typeArg = symbol.GenericArguments[0] + typeArg.Format(symbolUse.DisplayContext) |> shouldEqual "int" + + [] + let ``Interface 05 - Type arg`` () = + let _, checkResults = getParseAndCheckResults """ +type I<'T> = + abstract M: 'T -> unit + +{ new I<_> with + member this.M(i: int) = () } +""" + let symbolUse = + getSymbolUses checkResults + |> Seq.findBack (fun symbolUse -> symbolUse.Symbol.DisplayName = "I") + + let symbol = symbolUse.Symbol :?> FSharpEntity + let typeArg = symbol.GenericArguments[0] + typeArg.Format(symbolUse.DisplayContext) |> shouldEqual "int" + + [] + let ``Interface 06 - Type arg`` () = + let _, checkResults = getParseAndCheckResults """ +type I<'T> = + abstract M: 'T -> unit + +{ new I with + member this.M _ = () } +""" + let symbolUse = + getSymbolUses checkResults + |> Seq.findBack (fun symbolUse -> symbolUse.Symbol.DisplayName = "I") + + let symbol = symbolUse.Symbol :?> FSharpEntity + let typeArg = symbol.GenericArguments[0] + typeArg.Format(symbolUse.DisplayContext) |> shouldEqual "int" + + [] + let ``Operator 01 - Type arg`` () = + let _, checkResults = getParseAndCheckResults """ +[1] |> ignore +""" + let symbolUses = checkResults.GetAllUsesOfAllSymbolsInFile() + () [] let ``FSharpType.Format can use prefix representations`` () = diff --git a/tests/service/data/SyntaxTree/Binding/RangeOfAttributeShouldBeIncludedInSecondaryConstructor.fs.bsl b/tests/service/data/SyntaxTree/Binding/RangeOfAttributeShouldBeIncludedInSecondaryConstructor.fs.bsl index 2b1943b2d20..ceff4f8e244 100644 --- a/tests/service/data/SyntaxTree/Binding/RangeOfAttributeShouldBeIncludedInSecondaryConstructor.fs.bsl +++ b/tests/service/data/SyntaxTree/Binding/RangeOfAttributeShouldBeIncludedInSecondaryConstructor.fs.bsl @@ -16,7 +16,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None }); Member @@ -109,7 +109,7 @@ ImplFile (3,4--11,12)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None })), (2,5--11,12), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInMemberBinding.fs.bsl b/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInMemberBinding.fs.bsl index e76316e2210..9e0bd27f359 100644 --- a/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInMemberBinding.fs.bsl +++ b/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInMemberBinding.fs.bsl @@ -13,7 +13,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None }); Member @@ -41,7 +41,7 @@ ImplFile (3,4--3,21)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None })), (2,5--3,21), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInMemberBindingWithParameters.fs.bsl b/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInMemberBindingWithParameters.fs.bsl index b9aa74e11b9..463fcef24ba 100644 --- a/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInMemberBindingWithParameters.fs.bsl +++ b/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInMemberBindingWithParameters.fs.bsl @@ -16,7 +16,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None }); Member @@ -48,7 +48,7 @@ ImplFile (3,4--3,24)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None })), (2,5--3,24), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInMemberBindingWithReturnType.fs.bsl b/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInMemberBindingWithReturnType.fs.bsl index 2458900141d..49eeec4d346 100644 --- a/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInMemberBindingWithReturnType.fs.bsl +++ b/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInMemberBindingWithReturnType.fs.bsl @@ -16,7 +16,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None }); Member @@ -57,7 +57,7 @@ ImplFile (3,4--3,33)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None })), (2,5--3,33), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInProperty.fs.bsl b/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInProperty.fs.bsl index 92929b73a08..32f9f50c83a 100644 --- a/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInProperty.fs.bsl +++ b/tests/service/data/SyntaxTree/Binding/RangeOfEqualSignShouldBePresentInProperty.fs.bsl @@ -13,7 +13,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None }); GetSetMember @@ -88,7 +88,7 @@ ImplFile (3,4--5,50)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None })), (2,5--5,50), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/Expression/SynExprObjWithSetter.fs.bsl b/tests/service/data/SyntaxTree/Expression/SynExprObjWithSetter.fs.bsl index 717ef3af2f2..8e4beda60ed 100644 --- a/tests/service/data/SyntaxTree/Expression/SynExprObjWithSetter.fs.bsl +++ b/tests/service/data/SyntaxTree/Expression/SynExprObjWithSetter.fs.bsl @@ -20,7 +20,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (3,9--3,11)), None, + (None, [], Const (Unit, (3,9--3,11)), None, PreXmlDoc ((3,9), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,9), { AsKeyword = None }); AbstractSlot @@ -46,7 +46,7 @@ ImplFile (4,4--4,54)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,9--3,11)), None, + (None, [], Const (Unit, (3,9--3,11)), None, PreXmlDoc ((3,9), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,9), { AsKeyword = None })), (2,0--4,54), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Expression/Unfinished escaped ident 02.fs.bsl b/tests/service/data/SyntaxTree/Expression/Unfinished escaped ident 02.fs.bsl index 669efb602f1..d85ff5016bd 100644 --- a/tests/service/data/SyntaxTree/Expression/Unfinished escaped ident 02.fs.bsl +++ b/tests/service/data/SyntaxTree/Expression/Unfinished escaped ident 02.fs.bsl @@ -13,7 +13,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None }); Member @@ -73,7 +73,7 @@ ImplFile (4,4--6,27)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--6,27), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/LeadingKeyword/NewKeyword.fs.bsl b/tests/service/data/SyntaxTree/LeadingKeyword/NewKeyword.fs.bsl index bc62b3fc078..1e408d27f4b 100644 --- a/tests/service/data/SyntaxTree/LeadingKeyword/NewKeyword.fs.bsl +++ b/tests/service/data/SyntaxTree/LeadingKeyword/NewKeyword.fs.bsl @@ -13,7 +13,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None }); Member @@ -53,7 +53,7 @@ ImplFile (3,4--3,30)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None })), (2,5--3,30), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/Member/Auto property 07.fs.bsl b/tests/service/data/SyntaxTree/Member/Auto property 07.fs.bsl index f2be39aed6e..492124d31f0 100644 --- a/tests/service/data/SyntaxTree/Member/Auto property 07.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Auto property 07.fs.bsl @@ -12,7 +12,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None }); AutoProperty @@ -42,7 +42,7 @@ ImplFile (4,4--4,38)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--4,38), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/GetSetMember 01.fs.bsl b/tests/service/data/SyntaxTree/Member/GetSetMember 01.fs.bsl index 3d88c0ff50d..55fb27c87bd 100644 --- a/tests/service/data/SyntaxTree/Member/GetSetMember 01.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/GetSetMember 01.fs.bsl @@ -12,7 +12,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (3,8--3,10)), None, + (None, [], Const (Unit, (3,8--3,10)), None, PreXmlDoc ((3,8), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,8), { AsKeyword = None }); GetSetMember @@ -194,7 +194,7 @@ ImplFile (4,4--5,92)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,8--3,10)), None, + (None, [], Const (Unit, (3,8--3,10)), None, PreXmlDoc ((3,8), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,8), { AsKeyword = None })), (3,5--5,92), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Missing type 01.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Missing type 01.fs.bsl index 028662608dc..d603121ea71 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Missing type 01.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Missing type 01.fs.bsl @@ -14,20 +14,22 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - FromParseError (3,9--3,9), (3,7--3,9))], [], + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + FromParseError (3,9--3,9), (3,7--3,9)), (3,6--3,10)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], (3,13--3,22)), [], Some (ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - FromParseError (3,9--3,9), (3,7--3,9))], [], + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + FromParseError (3,9--3,9), (3,7--3,9)), (3,6--3,10)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,22), @@ -39,4 +41,4 @@ ImplFile { ConditionalDirectives = [] CodeComments = [] }, set [])) -(3,9)-(3,10) parse error Unexpected symbol ')' in type definition +(3,9)-(3,10) parse error Unexpected symbol ')' in pattern diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Missing type 02.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Missing type 02.fs.bsl index a6f09e6b1ff..d963c4115e8 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Missing type 02.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Missing type 02.fs.bsl @@ -14,23 +14,32 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - FromParseError (3,9--3,9), (3,7--3,9)); - Id (j, None, false, false, false, (3,11--3,12))], - [(3,9--3,10)], (3,6--3,13)), None, + Paren + (Tuple + (false, + [Typed + (Named + (SynIdent (i, None), false, None, + (3,7--3,8)), FromParseError (3,9--3,9), + (3,7--3,9)); + Named + (SynIdent (j, None), false, None, (3,11--3,12))], + [(3,9--3,10)], (3,7--3,12)), (3,6--3,13)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], (3,16--3,25)), [], Some (ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - FromParseError (3,9--3,9), (3,7--3,9)); - Id (j, None, false, false, false, (3,11--3,12))], - [(3,9--3,10)], (3,6--3,13)), None, + Paren + (Tuple + (false, + [Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + FromParseError (3,9--3,9), (3,7--3,9)); + Named + (SynIdent (j, None), false, None, (3,11--3,12))], + [(3,9--3,10)], (3,7--3,12)), (3,6--3,13)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,25), { LeadingKeyword = Type (3,0--3,4) @@ -41,4 +50,4 @@ ImplFile { ConditionalDirectives = [] CodeComments = [] }, set [])) -(3,9)-(3,10) parse error Unexpected symbol ',' in type definition +(3,9)-(3,10) parse error Unexpected symbol ',' in pattern diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Pat - Tuple 01.fs b/tests/service/data/SyntaxTree/Member/Implicit ctor - Pat - Tuple 01.fs new file mode 100644 index 00000000000..29b287b3212 --- /dev/null +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Pat - Tuple 01.fs @@ -0,0 +1,3 @@ +module Module + +type T(a, (b, c: string), d) = class end diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Pat - Tuple 01.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Pat - Tuple 01.fs.bsl new file mode 100644 index 00000000000..2bc4b5f488f --- /dev/null +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Pat - Tuple 01.fs.bsl @@ -0,0 +1,76 @@ +ImplFile + (ParsedImplFileInput + ("/root/Member/Implicit ctor - Pat - Tuple 01.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Types + ([SynTypeDefn + (SynComponentInfo + ([], None, [], [T], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), + false, None, (3,5--3,6)), + ObjectModel + (Class, + [ImplicitCtor + (None, [], + Paren + (Tuple + (false, + [Named + (SynIdent (a, None), false, None, (3,7--3,8)); + Paren + (Tuple + (false, + [Named + (SynIdent (b, None), false, None, + (3,11--3,12)); + Typed + (Named + (SynIdent (c, None), false, None, + (3,14--3,15)), + LongIdent + (SynLongIdent ([string], [], [None])), + (3,14--3,23))], [(3,12--3,13)], + (3,11--3,23)), (3,10--3,24)); + Named + (SynIdent (d, None), false, None, (3,26--3,27))], + [(3,8--3,9); (3,24--3,25)], (3,7--3,27)), + (3,6--3,28)), None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })], (3,31--3,40)), [], + Some + (ImplicitCtor + (None, [], + Paren + (Tuple + (false, + [Named + (SynIdent (a, None), false, None, (3,7--3,8)); + Paren + (Tuple + (false, + [Named + (SynIdent (b, None), false, None, + (3,11--3,12)); + Typed + (Named + (SynIdent (c, None), false, None, + (3,14--3,15)), + LongIdent + (SynLongIdent ([string], [], [None])), + (3,14--3,23))], [(3,12--3,13)], + (3,11--3,23)), (3,10--3,24)); + Named + (SynIdent (d, None), false, None, (3,26--3,27))], + [(3,8--3,9); (3,24--3,25)], (3,7--3,27)), + (3,6--3,28)), None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })), (3,5--3,40), + { LeadingKeyword = Type (3,0--3,4) + EqualsRange = Some (3,29--3,30) + WithKeyword = None })], (3,0--3,40))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--3,40), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Pat - Tuple 02.fs b/tests/service/data/SyntaxTree/Member/Implicit ctor - Pat - Tuple 02.fs new file mode 100644 index 00000000000..058d241942c --- /dev/null +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Pat - Tuple 02.fs @@ -0,0 +1,3 @@ +module Module + +type T(a, , c) = class end diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Pat - Tuple 02.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Pat - Tuple 02.fs.bsl new file mode 100644 index 00000000000..35502745762 --- /dev/null +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Pat - Tuple 02.fs.bsl @@ -0,0 +1,52 @@ +ImplFile + (ParsedImplFileInput + ("/root/Member/Implicit ctor - Pat - Tuple 02.fs", false, + QualifiedNameOfFile Module, [], [], + [SynModuleOrNamespace + ([Module], false, NamedModule, + [Types + ([SynTypeDefn + (SynComponentInfo + ([], None, [], [T], + PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), + false, None, (3,5--3,6)), + ObjectModel + (Class, + [ImplicitCtor + (None, [], + Paren + (Tuple + (false, + [Named + (SynIdent (a, None), false, None, (3,7--3,8)); + Wild (3,10--3,10); + Named + (SynIdent (c, None), false, None, (3,12--3,13))], + [(3,8--3,9); (3,10--3,11)], (3,7--3,13)), + (3,6--3,14)), None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })], (3,17--3,26)), [], + Some + (ImplicitCtor + (None, [], + Paren + (Tuple + (false, + [Named + (SynIdent (a, None), false, None, (3,7--3,8)); + Wild (3,10--3,10); + Named + (SynIdent (c, None), false, None, (3,12--3,13))], + [(3,8--3,9); (3,10--3,11)], (3,7--3,13)), + (3,6--3,14)), None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })), (3,5--3,26), + { LeadingKeyword = Type (3,0--3,4) + EqualsRange = Some (3,15--3,16) + WithKeyword = None })], (3,0--3,26))], + PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None, + (1,0--3,26), { LeadingKeyword = Module (1,0--1,6) })], (true, true), + { ConditionalDirectives = [] + CodeComments = [] }, set [])) + +(3,10)-(3,11) parse error Expecting pattern diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 01.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 01.fs.bsl index 055f0279f10..424a3619305 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 01.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 01.fs.bsl @@ -14,27 +14,29 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Fun - (LongIdent (SynLongIdent ([a], [], [None])), - LongIdent (SynLongIdent ([b], [], [None])), - (3,10--3,16), { ArrowRange = (3,12--3,14) }), - (3,7--3,16))], [], (3,6--3,17)), None, + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Fun + (LongIdent (SynLongIdent ([a], [], [None])), + LongIdent (SynLongIdent ([b], [], [None])), + (3,10--3,16), { ArrowRange = (3,12--3,14) }), + (3,7--3,16)), (3,6--3,17)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], (3,20--3,29)), [], Some (ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Fun - (LongIdent (SynLongIdent ([a], [], [None])), - LongIdent (SynLongIdent ([b], [], [None])), - (3,10--3,16), { ArrowRange = (3,12--3,14) }), - (3,7--3,16))], [], (3,6--3,17)), None, + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Fun + (LongIdent (SynLongIdent ([a], [], [None])), + LongIdent (SynLongIdent ([b], [], [None])), + (3,10--3,16), { ArrowRange = (3,12--3,14) }), + (3,7--3,16)), (3,6--3,17)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,29), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 02.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 02.fs.bsl index d8fda81fe3e..0146ad5a153 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 02.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 02.fs.bsl @@ -14,26 +14,10 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Fun - (LongIdent (SynLongIdent ([a], [], [None])), - Fun - (LongIdent (SynLongIdent ([b], [], [None])), - LongIdent (SynLongIdent ([c], [], [None])), - (3,15--3,21), - { ArrowRange = (3,17--3,19) }), - (3,10--3,21), { ArrowRange = (3,12--3,14) }), - (3,7--3,21))], [], (3,6--3,22)), None, - PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), - (3,5--3,6), { AsKeyword = None })], (3,25--3,34)), [], - Some - (ImplicitCtor - (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), Fun (LongIdent (SynLongIdent ([a], [], [None])), Fun @@ -41,7 +25,24 @@ ImplFile LongIdent (SynLongIdent ([c], [], [None])), (3,15--3,21), { ArrowRange = (3,17--3,19) }), (3,10--3,21), { ArrowRange = (3,12--3,14) }), - (3,7--3,21))], [], (3,6--3,22)), None, + (3,7--3,21)), (3,6--3,22)), None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })], (3,25--3,34)), [], + Some + (ImplicitCtor + (None, [], + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Fun + (LongIdent (SynLongIdent ([a], [], [None])), + Fun + (LongIdent (SynLongIdent ([b], [], [None])), + LongIdent (SynLongIdent ([c], [], [None])), + (3,15--3,21), { ArrowRange = (3,17--3,19) }), + (3,10--3,21), { ArrowRange = (3,12--3,14) }), + (3,7--3,21)), (3,6--3,22)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,34), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 03.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 03.fs.bsl index 5dc98d09cc2..61b3b7f3776 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 03.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 03.fs.bsl @@ -14,26 +14,10 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Fun - (LongIdent (SynLongIdent ([a], [], [None])), - Fun - (LongIdent (SynLongIdent ([b], [], [None])), - LongIdent (SynLongIdent ([c], [], [None])), - (3,15--3,21), - { ArrowRange = (3,17--3,19) }), - (3,10--3,21), { ArrowRange = (3,12--3,14) }), - (3,7--3,21))], [], (3,6--3,22)), None, - PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), - (3,5--3,6), { AsKeyword = None })], (3,25--3,34)), [], - Some - (ImplicitCtor - (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), Fun (LongIdent (SynLongIdent ([a], [], [None])), Fun @@ -41,7 +25,24 @@ ImplFile LongIdent (SynLongIdent ([c], [], [None])), (3,15--3,21), { ArrowRange = (3,17--3,19) }), (3,10--3,21), { ArrowRange = (3,12--3,14) }), - (3,7--3,21))], [], (3,6--3,22)), None, + (3,7--3,21)), (3,6--3,22)), None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })], (3,25--3,34)), [], + Some + (ImplicitCtor + (None, [], + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Fun + (LongIdent (SynLongIdent ([a], [], [None])), + Fun + (LongIdent (SynLongIdent ([b], [], [None])), + LongIdent (SynLongIdent ([c], [], [None])), + (3,15--3,21), { ArrowRange = (3,17--3,19) }), + (3,10--3,21), { ArrowRange = (3,12--3,14) }), + (3,7--3,21)), (3,6--3,22)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,34), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 04.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 04.fs.bsl index b230c775e10..ca30a8c2327 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 04.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 04.fs.bsl @@ -14,27 +14,29 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Fun - (LongIdent (SynLongIdent ([a], [], [None])), - FromParseError (3,14--3,14), (3,10--3,14), - { ArrowRange = (3,12--3,14) }), (3,7--3,16))], - [], (3,6--3,16)), None, + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Fun + (LongIdent (SynLongIdent ([a], [], [None])), + FromParseError (3,14--3,14), (3,10--3,14), + { ArrowRange = (3,12--3,14) }), (3,7--3,16)), + (3,6--3,16)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], (3,19--3,28)), [], Some (ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Fun - (LongIdent (SynLongIdent ([a], [], [None])), - FromParseError (3,14--3,14), (3,10--3,14), - { ArrowRange = (3,12--3,14) }), (3,7--3,16))], - [], (3,6--3,16)), None, + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Fun + (LongIdent (SynLongIdent ([a], [], [None])), + FromParseError (3,14--3,14), (3,10--3,14), + { ArrowRange = (3,12--3,14) }), (3,7--3,16)), + (3,6--3,16)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,28), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 05.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 05.fs.bsl index 00c2967df77..bd89bbaf94e 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 05.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 05.fs.bsl @@ -14,26 +14,10 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Fun - (LongIdent (SynLongIdent ([a], [], [None])), - Fun - (FromParseError (3,15--3,15), - LongIdent (SynLongIdent ([c], [], [None])), - (3,15--3,19), - { ArrowRange = (3,15--3,17) }), - (3,10--3,19), { ArrowRange = (3,12--3,14) }), - (3,7--3,19))], [], (3,6--3,20)), None, - PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), - (3,5--3,6), { AsKeyword = None })], (3,23--3,32)), [], - Some - (ImplicitCtor - (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), Fun (LongIdent (SynLongIdent ([a], [], [None])), Fun @@ -41,7 +25,24 @@ ImplFile LongIdent (SynLongIdent ([c], [], [None])), (3,15--3,19), { ArrowRange = (3,15--3,17) }), (3,10--3,19), { ArrowRange = (3,12--3,14) }), - (3,7--3,19))], [], (3,6--3,20)), None, + (3,7--3,19)), (3,6--3,20)), None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })], (3,23--3,32)), [], + Some + (ImplicitCtor + (None, [], + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Fun + (LongIdent (SynLongIdent ([a], [], [None])), + Fun + (FromParseError (3,15--3,15), + LongIdent (SynLongIdent ([c], [], [None])), + (3,15--3,19), { ArrowRange = (3,15--3,17) }), + (3,10--3,19), { ArrowRange = (3,12--3,14) }), + (3,7--3,19)), (3,6--3,20)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,32), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 06.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 06.fs.bsl index 781136aa263..ac58a094acb 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 06.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Fun 06.fs.bsl @@ -14,32 +14,10 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Fun - (LongIdent (SynLongIdent ([a], [], [None])), - Fun - (FromParseError (3,15--3,15), - Tuple - (false, - [Type - (LongIdent - (SynLongIdent ([c], [], [None]))); - Star (3,20--3,21); - Type (FromParseError (3,21--3,21))], - (3,18--3,21)), (3,15--3,21), - { ArrowRange = (3,15--3,17) }), - (3,10--3,21), { ArrowRange = (3,12--3,14) }), - (3,7--3,23))], [], (3,6--3,23)), None, - PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), - (3,5--3,6), { AsKeyword = None })], (3,26--3,35)), [], - Some - (ImplicitCtor - (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), Fun (LongIdent (SynLongIdent ([a], [], [None])), Fun @@ -54,7 +32,31 @@ ImplFile (3,18--3,21)), (3,15--3,21), { ArrowRange = (3,15--3,17) }), (3,10--3,21), { ArrowRange = (3,12--3,14) }), - (3,7--3,23))], [], (3,6--3,23)), None, + (3,7--3,23)), (3,6--3,23)), None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })], (3,26--3,35)), [], + Some + (ImplicitCtor + (None, [], + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Fun + (LongIdent (SynLongIdent ([a], [], [None])), + Fun + (FromParseError (3,15--3,15), + Tuple + (false, + [Type + (LongIdent + (SynLongIdent ([c], [], [None]))); + Star (3,20--3,21); + Type (FromParseError (3,21--3,21))], + (3,18--3,21)), (3,15--3,21), + { ArrowRange = (3,15--3,17) }), (3,10--3,21), + { ArrowRange = (3,12--3,14) }), (3,7--3,23)), + (3,6--3,23)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,35), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 01.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 01.fs.bsl index b8315229b80..473bd768b3c 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 01.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 01.fs.bsl @@ -14,28 +14,10 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Tuple - (false, - [Type - (LongIdent - (SynLongIdent ([a], [], [None]))); - Star (3,12--3,13); - Type - (LongIdent - (SynLongIdent ([b], [], [None])))], - (3,10--3,15)), (3,7--3,15))], [], (3,6--3,16)), - None, - PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), - (3,5--3,6), { AsKeyword = None })], (3,19--3,28)), [], - Some - (ImplicitCtor - (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), Tuple (false, [Type @@ -43,8 +25,25 @@ ImplFile Star (3,12--3,13); Type (LongIdent (SynLongIdent ([b], [], [None])))], - (3,10--3,15)), (3,7--3,15))], [], (3,6--3,16)), - None, + (3,10--3,15)), (3,7--3,15)), (3,6--3,16)), + None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })], (3,19--3,28)), [], + Some + (ImplicitCtor + (None, [], + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Tuple + (false, + [Type + (LongIdent (SynLongIdent ([a], [], [None]))); + Star (3,12--3,13); + Type + (LongIdent (SynLongIdent ([b], [], [None])))], + (3,10--3,15)), (3,7--3,15)), (3,6--3,16)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,28), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 02.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 02.fs.bsl index 80b2ad46a8d..57b5279161a 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 02.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 02.fs.bsl @@ -14,32 +14,10 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Tuple - (false, - [Type - (LongIdent - (SynLongIdent ([a], [], [None]))); - Star (3,12--3,13); - Type - (LongIdent - (SynLongIdent ([b], [], [None]))); - Star (3,16--3,17); - Type - (LongIdent - (SynLongIdent ([c], [], [None])))], - (3,10--3,19)), (3,7--3,19))], [], (3,6--3,20)), - None, - PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), - (3,5--3,6), { AsKeyword = None })], (3,23--3,32)), [], - Some - (ImplicitCtor - (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), Tuple (false, [Type @@ -50,8 +28,28 @@ ImplFile Star (3,16--3,17); Type (LongIdent (SynLongIdent ([c], [], [None])))], - (3,10--3,19)), (3,7--3,19))], [], (3,6--3,20)), - None, + (3,10--3,19)), (3,7--3,19)), (3,6--3,20)), + None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })], (3,23--3,32)), [], + Some + (ImplicitCtor + (None, [], + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Tuple + (false, + [Type + (LongIdent (SynLongIdent ([a], [], [None]))); + Star (3,12--3,13); + Type + (LongIdent (SynLongIdent ([b], [], [None]))); + Star (3,16--3,17); + Type + (LongIdent (SynLongIdent ([c], [], [None])))], + (3,10--3,19)), (3,7--3,19)), (3,6--3,20)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,32), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 03.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 03.fs.bsl index ada1502d243..92b69f0c2b2 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 03.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 03.fs.bsl @@ -14,34 +14,34 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Tuple - (false, - [Type - (LongIdent - (SynLongIdent ([a], [], [None]))); - Star (3,12--3,13); - Type (FromParseError (3,13--3,13))], - (3,10--3,13)), (3,7--3,15))], [], (3,6--3,15)), - None, - PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), - (3,5--3,6), { AsKeyword = None })], (3,18--3,27)), [], - Some - (ImplicitCtor - (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), Tuple (false, [Type (LongIdent (SynLongIdent ([a], [], [None]))); Star (3,12--3,13); Type (FromParseError (3,13--3,13))], - (3,10--3,13)), (3,7--3,15))], [], (3,6--3,15)), - None, + (3,10--3,13)), (3,7--3,15)), (3,6--3,15)), + None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })], (3,18--3,27)), [], + Some + (ImplicitCtor + (None, [], + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Tuple + (false, + [Type + (LongIdent (SynLongIdent ([a], [], [None]))); + Star (3,12--3,13); + Type (FromParseError (3,13--3,13))], + (3,10--3,13)), (3,7--3,15)), (3,6--3,15)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,27), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 04.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 04.fs.bsl index e6bbfcbcb71..5ff10cae81b 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 04.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 04.fs.bsl @@ -14,30 +14,10 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Tuple - (false, - [Type - (LongIdent - (SynLongIdent ([a], [], [None]))); - Star (3,12--3,13); - Type - (LongIdent - (SynLongIdent ([b], [], [None]))); - Star (3,16--3,17); - Type (FromParseError (3,17--3,17))], - (3,10--3,17)), (3,7--3,19))], [], (3,6--3,19)), - None, - PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), - (3,5--3,6), { AsKeyword = None })], (3,22--3,31)), [], - Some - (ImplicitCtor - (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), Tuple (false, [Type @@ -47,8 +27,27 @@ ImplFile (LongIdent (SynLongIdent ([b], [], [None]))); Star (3,16--3,17); Type (FromParseError (3,17--3,17))], - (3,10--3,17)), (3,7--3,19))], [], (3,6--3,19)), - None, + (3,10--3,17)), (3,7--3,19)), (3,6--3,19)), + None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })], (3,22--3,31)), [], + Some + (ImplicitCtor + (None, [], + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Tuple + (false, + [Type + (LongIdent (SynLongIdent ([a], [], [None]))); + Star (3,12--3,13); + Type + (LongIdent (SynLongIdent ([b], [], [None]))); + Star (3,16--3,17); + Type (FromParseError (3,17--3,17))], + (3,10--3,17)), (3,7--3,19)), (3,6--3,19)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,31), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 05.fs.bsl b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 05.fs.bsl index dfcd368eb5c..a39da9cc29e 100644 --- a/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 05.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Implicit ctor - Type - Tuple 05.fs.bsl @@ -14,30 +14,10 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - Tuple - (false, - [Type - (LongIdent - (SynLongIdent ([a], [], [None]))); - Star (3,12--3,13); - Type (FromParseError (3,15--3,15)); - Star (3,14--3,15); - Type - (LongIdent - (SynLongIdent ([c], [], [None])))], - (3,10--3,17)), (3,7--3,17))], [], (3,6--3,19)), - None, - PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), - (3,5--3,6), { AsKeyword = None })], (3,22--3,31)), [], - Some - (ImplicitCtor - (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), Tuple (false, [Type @@ -47,8 +27,27 @@ ImplFile Star (3,14--3,15); Type (LongIdent (SynLongIdent ([c], [], [None])))], - (3,10--3,17)), (3,7--3,17))], [], (3,6--3,19)), - None, + (3,10--3,17)), (3,7--3,17)), (3,6--3,19)), + None, + PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), + (3,5--3,6), { AsKeyword = None })], (3,22--3,31)), [], + Some + (ImplicitCtor + (None, [], + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + Tuple + (false, + [Type + (LongIdent (SynLongIdent ([a], [], [None]))); + Star (3,12--3,13); + Type (FromParseError (3,15--3,15)); + Star (3,14--3,15); + Type + (LongIdent (SynLongIdent ([c], [], [None])))], + (3,10--3,17)), (3,7--3,17)), (3,6--3,19)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,31), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/ImplicitCtorWithAsKeyword.fs.bsl b/tests/service/data/SyntaxTree/Member/ImplicitCtorWithAsKeyword.fs.bsl index 7f97fd1c420..61f182611f8 100644 --- a/tests/service/data/SyntaxTree/Member/ImplicitCtorWithAsKeyword.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/ImplicitCtorWithAsKeyword.fs.bsl @@ -14,58 +14,64 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id - (readAllBytes, None, false, false, false, - (2,33--2,45)), - Fun - (LongIdent - (SynLongIdent ([string], [], [None])), - Array - (1, - LongIdent - (SynLongIdent ([byte], [], [None])), - (2,57--2,63)), (2,47--2,63), - { ArrowRange = (2,54--2,56) }), (2,33--2,63)); - Typed - (Id - (projectOptions, None, false, false, false, - (2,65--2,79)), - LongIdent - (SynLongIdent - ([FSharpProjectOptions], [], [None])), - (2,65--2,101))], [(2,63--2,64)], (2,32--2,102)), - Some this, + Paren + (Tuple + (false, + [Typed + (Named + (SynIdent (readAllBytes, None), false, None, + (2,33--2,45)), + Fun + (LongIdent + (SynLongIdent ([string], [], [None])), + Array + (1, + LongIdent + (SynLongIdent ([byte], [], [None])), + (2,57--2,63)), (2,47--2,63), + { ArrowRange = (2,54--2,56) }), + (2,33--2,63)); + Typed + (Named + (SynIdent (projectOptions, None), false, + None, (2,65--2,79)), + LongIdent + (SynLongIdent + ([FSharpProjectOptions], [], [None])), + (2,65--2,101))], [(2,63--2,64)], + (2,33--2,101)), (2,32--2,102)), Some this, PreXmlDoc ((2,32), FSharp.Compiler.Xml.XmlDocCollector), (2,14--2,32), { AsKeyword = Some (4,4--4,6) })], (8,4--8,13)), [], Some (ImplicitCtor (None, [], - SimplePats - ([Typed - (Id - (readAllBytes, None, false, false, false, - (2,33--2,45)), - Fun - (LongIdent - (SynLongIdent ([string], [], [None])), - Array - (1, - LongIdent - (SynLongIdent ([byte], [], [None])), - (2,57--2,63)), (2,47--2,63), - { ArrowRange = (2,54--2,56) }), (2,33--2,63)); - Typed - (Id - (projectOptions, None, false, false, false, - (2,65--2,79)), - LongIdent - (SynLongIdent - ([FSharpProjectOptions], [], [None])), - (2,65--2,101))], [(2,63--2,64)], (2,32--2,102)), - Some this, + Paren + (Tuple + (false, + [Typed + (Named + (SynIdent (readAllBytes, None), false, None, + (2,33--2,45)), + Fun + (LongIdent + (SynLongIdent ([string], [], [None])), + Array + (1, + LongIdent + (SynLongIdent ([byte], [], [None])), + (2,57--2,63)), (2,47--2,63), + { ArrowRange = (2,54--2,56) }), + (2,33--2,63)); + Typed + (Named + (SynIdent (projectOptions, None), false, + None, (2,65--2,79)), + LongIdent + (SynLongIdent + ([FSharpProjectOptions], [], [None])), + (2,65--2,101))], [(2,63--2,64)], (2,33--2,101)), + (2,32--2,102)), Some this, PreXmlDoc ((2,32), FSharp.Compiler.Xml.XmlDocCollector), (2,14--2,32), { AsKeyword = Some (4,4--4,6) })), (2,5--8,13), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/Member/Read-onlyPropertyInSynMemberDefnMemberContainsTheRangeOfTheWithKeyword.fs.bsl b/tests/service/data/SyntaxTree/Member/Read-onlyPropertyInSynMemberDefnMemberContainsTheRangeOfTheWithKeyword.fs.bsl index 780fb23542d..6692c520246 100644 --- a/tests/service/data/SyntaxTree/Member/Read-onlyPropertyInSynMemberDefnMemberContainsTheRangeOfTheWithKeyword.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Read-onlyPropertyInSynMemberDefnMemberContainsTheRangeOfTheWithKeyword.fs.bsl @@ -16,7 +16,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (3,8--3,10)), None, + (None, [], Const (Unit, (3,8--3,10)), None, PreXmlDoc ((3,8), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,8), { AsKeyword = None }); GetSetMember @@ -56,7 +56,7 @@ ImplFile SetKeyword = None })], (5,4--5,60)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,8--3,10)), None, + (None, [], Const (Unit, (3,8--3,10)), None, PreXmlDoc ((3,8), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,8), { AsKeyword = None })), (3,5--5,60), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/ReadwritePropertyInSynMemberDefnMemberContainsTheRangeOfTheWithKeyword.fs.bsl b/tests/service/data/SyntaxTree/Member/ReadwritePropertyInSynMemberDefnMemberContainsTheRangeOfTheWithKeyword.fs.bsl index a4240a8a27e..8319d790680 100644 --- a/tests/service/data/SyntaxTree/Member/ReadwritePropertyInSynMemberDefnMemberContainsTheRangeOfTheWithKeyword.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/ReadwritePropertyInSynMemberDefnMemberContainsTheRangeOfTheWithKeyword.fs.bsl @@ -17,7 +17,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,8--2,10)), None, + (None, [], Const (Unit, (2,8--2,10)), None, PreXmlDoc ((2,8), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,8), { AsKeyword = None }); GetSetMember @@ -92,7 +92,7 @@ ImplFile (4,4--6,50)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,8--2,10)), None, + (None, [], Const (Unit, (2,8--2,10)), None, PreXmlDoc ((2,8), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,8), { AsKeyword = None })), (2,5--6,50), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/Member/SynTypeDefnWithAbstractSlotContainsTheRangeOfTheWithKeyword.fs.bsl b/tests/service/data/SyntaxTree/Member/SynTypeDefnWithAbstractSlotContainsTheRangeOfTheWithKeyword.fs.bsl index ea8c1ef757c..f3b24278efa 100644 --- a/tests/service/data/SyntaxTree/Member/SynTypeDefnWithAbstractSlotContainsTheRangeOfTheWithKeyword.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/SynTypeDefnWithAbstractSlotContainsTheRangeOfTheWithKeyword.fs.bsl @@ -16,7 +16,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,8--2,10)), None, + (None, [], Const (Unit, (2,8--2,10)), None, PreXmlDoc ((2,8), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,8), { AsKeyword = None }); AbstractSlot @@ -44,7 +44,7 @@ ImplFile (3,4--3,42)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,8--2,10)), None, + (None, [], Const (Unit, (2,8--2,10)), None, PreXmlDoc ((2,8), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,8), { AsKeyword = None })), (2,5--3,42), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/Member/SynTypeDefnWithAutoPropertyContainsTheRangeOfTheEqualsSign.fs.bsl b/tests/service/data/SyntaxTree/Member/SynTypeDefnWithAutoPropertyContainsTheRangeOfTheEqualsSign.fs.bsl index e9d5adcfd5b..7cfa4c283eb 100644 --- a/tests/service/data/SyntaxTree/Member/SynTypeDefnWithAutoPropertyContainsTheRangeOfTheEqualsSign.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/SynTypeDefnWithAutoPropertyContainsTheRangeOfTheEqualsSign.fs.bsl @@ -17,18 +17,23 @@ ImplFile (Unspecified, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id - (name, None, false, false, false, (3,12--3,16)), - LongIdent (SynLongIdent ([string], [], [None])), - (3,12--3,25)); - Typed - (Id - (age, None, false, false, false, (3,27--3,30)), - LongIdent (SynLongIdent ([int], [], [None])), - (3,27--3,36))], [(3,25--3,26)], (3,11--3,37)), - None, + Paren + (Tuple + (false, + [Typed + (Named + (SynIdent (name, None), false, None, + (3,12--3,16)), + LongIdent + (SynLongIdent ([string], [], [None])), + (3,12--3,25)); + Typed + (Named + (SynIdent (age, None), false, None, + (3,27--3,30)), + LongIdent (SynLongIdent ([int], [], [None])), + (3,27--3,36))], [(3,25--3,26)], (3,12--3,36)), + (3,11--3,37)), None, PreXmlDoc ((3,11), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,11), { AsKeyword = None }); AutoProperty @@ -57,17 +62,23 @@ ImplFile Some (ImplicitCtor (None, [], - SimplePats - ([Typed - (Id - (name, None, false, false, false, (3,12--3,16)), - LongIdent (SynLongIdent ([string], [], [None])), - (3,12--3,25)); - Typed - (Id (age, None, false, false, false, (3,27--3,30)), - LongIdent (SynLongIdent ([int], [], [None])), - (3,27--3,36))], [(3,25--3,26)], (3,11--3,37)), - None, + Paren + (Tuple + (false, + [Typed + (Named + (SynIdent (name, None), false, None, + (3,12--3,16)), + LongIdent + (SynLongIdent ([string], [], [None])), + (3,12--3,25)); + Typed + (Named + (SynIdent (age, None), false, None, + (3,27--3,30)), + LongIdent (SynLongIdent ([int], [], [None])), + (3,27--3,36))], [(3,25--3,26)], (3,12--3,36)), + (3,11--3,37)), None, PreXmlDoc ((3,11), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,11), { AsKeyword = None })), (2,0--5,40), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Member/SynTypeDefnWithAutoPropertyContainsTheRangeOfTheWithKeyword.fs.bsl b/tests/service/data/SyntaxTree/Member/SynTypeDefnWithAutoPropertyContainsTheRangeOfTheWithKeyword.fs.bsl index 2cbc0581a65..0bef3c32697 100644 --- a/tests/service/data/SyntaxTree/Member/SynTypeDefnWithAutoPropertyContainsTheRangeOfTheWithKeyword.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/SynTypeDefnWithAutoPropertyContainsTheRangeOfTheWithKeyword.fs.bsl @@ -16,7 +16,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,8--2,10)), None, + (None, [], Const (Unit, (2,8--2,10)), None, PreXmlDoc ((2,8), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,8), { AsKeyword = None }); AutoProperty @@ -64,7 +64,7 @@ ImplFile GetSetKeywords = None })], (3,4--4,39)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,8--2,10)), None, + (None, [], Const (Unit, (2,8--2,10)), None, PreXmlDoc ((2,8), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,8), { AsKeyword = None })), (2,5--4,39), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/Member/SynTypeDefnWithMemberWithSetget.fs.bsl b/tests/service/data/SyntaxTree/Member/SynTypeDefnWithMemberWithSetget.fs.bsl index 85520b1de87..e3fe9618a23 100644 --- a/tests/service/data/SyntaxTree/Member/SynTypeDefnWithMemberWithSetget.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/SynTypeDefnWithMemberWithSetget.fs.bsl @@ -13,7 +13,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None }); GetSetMember @@ -104,7 +104,7 @@ ImplFile (3,4--3,62)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None })), (2,5--3,62), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/Member/Write-onlyPropertyInSynMemberDefnMemberContainsTheRangeOfTheWithKeyword.fs.bsl b/tests/service/data/SyntaxTree/Member/Write-onlyPropertyInSynMemberDefnMemberContainsTheRangeOfTheWithKeyword.fs.bsl index 9dad153789c..79a865c5eed 100644 --- a/tests/service/data/SyntaxTree/Member/Write-onlyPropertyInSynMemberDefnMemberContainsTheRangeOfTheWithKeyword.fs.bsl +++ b/tests/service/data/SyntaxTree/Member/Write-onlyPropertyInSynMemberDefnMemberContainsTheRangeOfTheWithKeyword.fs.bsl @@ -16,7 +16,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (3,8--3,10)), None, + (None, [], Const (Unit, (3,8--3,10)), None, PreXmlDoc ((3,8), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,8), { AsKeyword = None }); GetSetMember @@ -63,7 +63,7 @@ ImplFile (5,4--5,79)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,8--3,10)), None, + (None, [], Const (Unit, (3,8--3,10)), None, PreXmlDoc ((3,8), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,8), { AsKeyword = None })), (3,5--5,79), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/OperatorName/ActivePatternIdentifierInPrivateMember.fs.bsl b/tests/service/data/SyntaxTree/OperatorName/ActivePatternIdentifierInPrivateMember.fs.bsl index f78ea9234b9..a91a973e4e5 100644 --- a/tests/service/data/SyntaxTree/OperatorName/ActivePatternIdentifierInPrivateMember.fs.bsl +++ b/tests/service/data/SyntaxTree/OperatorName/ActivePatternIdentifierInPrivateMember.fs.bsl @@ -13,7 +13,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None }); Member @@ -51,7 +51,7 @@ ImplFile (3,4--7,6)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None })), (2,5--7,6), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/OperatorName/ObjectModelWithTwoMembers.fs.bsl b/tests/service/data/SyntaxTree/OperatorName/ObjectModelWithTwoMembers.fs.bsl index 2679d87a302..760c3f64ca3 100644 --- a/tests/service/data/SyntaxTree/OperatorName/ObjectModelWithTwoMembers.fs.bsl +++ b/tests/service/data/SyntaxTree/OperatorName/ObjectModelWithTwoMembers.fs.bsl @@ -13,7 +13,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None }); LetBindings @@ -103,7 +103,7 @@ ImplFile (3,4--4,79)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,6--2,8)), None, + (None, [], Const (Unit, (2,6--2,8)), None, PreXmlDoc ((2,6), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,6), { AsKeyword = None })), (2,5--4,79), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/SimplePats/SimplePats 02.fs.bsl b/tests/service/data/SyntaxTree/SimplePats/SimplePats 02.fs.bsl index 4a993df804c..fc92f2e4767 100644 --- a/tests/service/data/SyntaxTree/SimplePats/SimplePats 02.fs.bsl +++ b/tests/service/data/SyntaxTree/SimplePats/SimplePats 02.fs.bsl @@ -14,21 +14,23 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - LongIdent (SynLongIdent ([int], [], [None])), - (3,7--3,13))], [], (3,6--3,14)), None, + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + LongIdent (SynLongIdent ([int], [], [None])), + (3,7--3,13)), (3,6--3,14)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], (3,17--3,26)), [], Some (ImplicitCtor (None, [], - SimplePats - ([Typed - (Id (i, None, false, false, false, (3,7--3,8)), - LongIdent (SynLongIdent ([int], [], [None])), - (3,7--3,13))], [], (3,6--3,14)), None, + Paren + (Typed + (Named + (SynIdent (i, None), false, None, (3,7--3,8)), + LongIdent (SynLongIdent ([int], [], [None])), + (3,7--3,13)), (3,6--3,14)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,26), { LeadingKeyword = Type (3,0--3,4) @@ -44,19 +46,27 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Id (a, None, false, false, false, (4,7--4,8)); - Id (b, None, false, false, false, (4,9--4,10))], - [(4,8--4,9)], (4,6--4,11)), None, + Paren + (Tuple + (false, + [Named + (SynIdent (a, None), false, None, (4,7--4,8)); + Named + (SynIdent (b, None), false, None, (4,9--4,10))], + [(4,8--4,9)], (4,7--4,10)), (4,6--4,11)), None, PreXmlDoc ((4,6), FSharp.Compiler.Xml.XmlDocCollector), (4,5--4,6), { AsKeyword = None })], (4,14--4,23)), [], Some (ImplicitCtor (None, [], - SimplePats - ([Id (a, None, false, false, false, (4,7--4,8)); - Id (b, None, false, false, false, (4,9--4,10))], - [(4,8--4,9)], (4,6--4,11)), None, + Paren + (Tuple + (false, + [Named + (SynIdent (a, None), false, None, (4,7--4,8)); + Named + (SynIdent (b, None), false, None, (4,9--4,10))], + [(4,8--4,9)], (4,7--4,10)), (4,6--4,11)), None, PreXmlDoc ((4,6), FSharp.Compiler.Xml.XmlDocCollector), (4,5--4,6), { AsKeyword = None })), (4,5--4,23), { LeadingKeyword = Type (4,0--4,4) @@ -72,64 +82,73 @@ ImplFile (Class, [ImplicitCtor (None, [], - SimplePats - ([Attrib - (Id - (bar, None, false, false, false, (5,15--5,18)), - [{ Attributes = - [{ TypeName = - SynLongIdent ([Foo], [], [None]) - ArgExpr = Const (Unit, (5,9--5,12)) - Target = None - AppliesToGetterAndSetter = false - Range = (5,9--5,12) }] - Range = (5,7--5,14) }], (5,7--5,18)); - Attrib - (Typed - (Id - (v, None, false, false, false, (5,28--5,29)), - LongIdent (SynLongIdent ([V], [], [None])), - (5,28--5,32)), - [{ Attributes = - [{ TypeName = - SynLongIdent ([Foo], [], [None]) - ArgExpr = Const (Unit, (5,22--5,25)) - Target = None - AppliesToGetterAndSetter = false - Range = (5,22--5,25) }] - Range = (5,20--5,27) }], (5,20--5,32))], - [(5,18--5,19)], (5,6--5,33)), None, + Paren + (Tuple + (false, + [Attrib + (Named + (SynIdent (bar, None), false, None, + (5,15--5,18)), + [{ Attributes = + [{ TypeName = + SynLongIdent ([Foo], [], [None]) + ArgExpr = Const (Unit, (5,9--5,12)) + Target = None + AppliesToGetterAndSetter = false + Range = (5,9--5,12) }] + Range = (5,7--5,14) }], (5,7--5,18)); + Attrib + (Typed + (Named + (SynIdent (v, None), false, None, + (5,28--5,29)), + LongIdent (SynLongIdent ([V], [], [None])), + (5,28--5,32)), + [{ Attributes = + [{ TypeName = + SynLongIdent ([Foo], [], [None]) + ArgExpr = Const (Unit, (5,22--5,25)) + Target = None + AppliesToGetterAndSetter = false + Range = (5,22--5,25) }] + Range = (5,20--5,27) }], (5,20--5,32))], + [(5,18--5,19)], (5,7--5,32)), (5,6--5,33)), None, PreXmlDoc ((5,6), FSharp.Compiler.Xml.XmlDocCollector), (5,5--5,6), { AsKeyword = None })], (5,36--5,45)), [], Some (ImplicitCtor (None, [], - SimplePats - ([Attrib - (Id (bar, None, false, false, false, (5,15--5,18)), - [{ Attributes = - [{ TypeName = - SynLongIdent ([Foo], [], [None]) - ArgExpr = Const (Unit, (5,9--5,12)) - Target = None - AppliesToGetterAndSetter = false - Range = (5,9--5,12) }] - Range = (5,7--5,14) }], (5,7--5,18)); - Attrib - (Typed - (Id - (v, None, false, false, false, (5,28--5,29)), - LongIdent (SynLongIdent ([V], [], [None])), - (5,28--5,32)), - [{ Attributes = - [{ TypeName = - SynLongIdent ([Foo], [], [None]) - ArgExpr = Const (Unit, (5,22--5,25)) - Target = None - AppliesToGetterAndSetter = false - Range = (5,22--5,25) }] - Range = (5,20--5,27) }], (5,20--5,32))], - [(5,18--5,19)], (5,6--5,33)), None, + Paren + (Tuple + (false, + [Attrib + (Named + (SynIdent (bar, None), false, None, + (5,15--5,18)), + [{ Attributes = + [{ TypeName = + SynLongIdent ([Foo], [], [None]) + ArgExpr = Const (Unit, (5,9--5,12)) + Target = None + AppliesToGetterAndSetter = false + Range = (5,9--5,12) }] + Range = (5,7--5,14) }], (5,7--5,18)); + Attrib + (Typed + (Named + (SynIdent (v, None), false, None, + (5,28--5,29)), + LongIdent (SynLongIdent ([V], [], [None])), + (5,28--5,32)), + [{ Attributes = + [{ TypeName = + SynLongIdent ([Foo], [], [None]) + ArgExpr = Const (Unit, (5,22--5,25)) + Target = None + AppliesToGetterAndSetter = false + Range = (5,22--5,25) }] + Range = (5,20--5,27) }], (5,20--5,32))], + [(5,18--5,19)], (5,7--5,32)), (5,6--5,33)), None, PreXmlDoc ((5,6), FSharp.Compiler.Xml.XmlDocCollector), (5,5--5,6), { AsKeyword = None })), (5,5--5,45), { LeadingKeyword = Type (5,0--5,4) @@ -144,12 +163,12 @@ ImplFile ObjectModel (Class, [ImplicitCtor - (None, [], SimplePats ([], [], (6,9--6,11)), None, + (None, [], Const (Unit, (6,9--6,11)), None, PreXmlDoc ((6,9), FSharp.Compiler.Xml.XmlDocCollector), (6,5--6,9), { AsKeyword = None })], (6,14--6,23)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (6,9--6,11)), None, + (None, [], Const (Unit, (6,9--6,11)), None, PreXmlDoc ((6,9), FSharp.Compiler.Xml.XmlDocCollector), (6,5--6,9), { AsKeyword = None })), (6,5--6,23), { LeadingKeyword = Type (6,0--6,4) diff --git a/tests/service/data/SyntaxTree/Type/As 01.fs.bsl b/tests/service/data/SyntaxTree/Type/As 01.fs.bsl index 84f8bb3c3a7..bec2d3a7121 100644 --- a/tests/service/data/SyntaxTree/Type/As 01.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/As 01.fs.bsl @@ -12,13 +12,13 @@ ImplFile ObjectModel (Class, [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), Some this, + (None, [], Const (Unit, (3,6--3,8)), Some this, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = Some (3,9--3,11) })], (3,19--3,28)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), Some this, + (None, [], Const (Unit, (3,6--3,8)), Some this, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = Some (3,9--3,11) })), (3,5--3,28), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/As 02.fs.bsl b/tests/service/data/SyntaxTree/Type/As 02.fs.bsl index b1a6a433c9c..f40ff1a13d0 100644 --- a/tests/service/data/SyntaxTree/Type/As 02.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/As 02.fs.bsl @@ -12,13 +12,13 @@ ImplFile ObjectModel (Class, [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = Some (3,9--3,11) })], (3,14--3,23)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = Some (3,9--3,11) })), (3,5--3,23), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/As 05.fs.bsl b/tests/service/data/SyntaxTree/Type/As 05.fs.bsl index 8c216721fbb..52ea9524fef 100644 --- a/tests/service/data/SyntaxTree/Type/As 05.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/As 05.fs.bsl @@ -11,7 +11,7 @@ ImplFile false, None, (3,5--3,6)), Simple (None (3,5--3,6), (3,5--3,6)), [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), Some this, + (None, [], Const (Unit, (3,6--3,8)), Some this, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = Some (3,9--3,11) })], None, (3,5--3,6), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/As 06.fs.bsl b/tests/service/data/SyntaxTree/Type/As 06.fs.bsl index f7f827efbb1..ed933552272 100644 --- a/tests/service/data/SyntaxTree/Type/As 06.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/As 06.fs.bsl @@ -11,7 +11,7 @@ ImplFile false, None, (3,5--3,6)), Simple (None (3,5--3,6), (3,5--3,6)), [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = Some (3,9--3,11) })], None, (3,5--3,6), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/As 07.fs.bsl b/tests/service/data/SyntaxTree/Type/As 07.fs.bsl index 59cc29788eb..a491491159b 100644 --- a/tests/service/data/SyntaxTree/Type/As 07.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/As 07.fs.bsl @@ -11,7 +11,7 @@ ImplFile false, None, (3,5--3,6)), Simple (None (3,5--3,6), (3,5--3,6)), [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), Some this, + (None, [], Const (Unit, (3,6--3,8)), Some this, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = Some (3,9--3,11) })], None, (3,5--3,6), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/As 08.fs.bsl b/tests/service/data/SyntaxTree/Type/As 08.fs.bsl index 3894e1a4c18..24254ea6064 100644 --- a/tests/service/data/SyntaxTree/Type/As 08.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/As 08.fs.bsl @@ -11,7 +11,7 @@ ImplFile false, None, (3,5--3,6)), Simple (None (3,5--3,6), (3,5--3,6)), [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = Some (3,9--3,11) })], None, (3,5--3,6), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/Class 03.fs.bsl b/tests/service/data/SyntaxTree/Type/Class 03.fs.bsl index e3e58a8b719..77f8ca8cea0 100644 --- a/tests/service/data/SyntaxTree/Type/Class 03.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/Class 03.fs.bsl @@ -12,12 +12,12 @@ ImplFile ObjectModel (Class, [ImplicitCtor - (None, [], SimplePats ([], [], (3,7--3,9)), None, + (None, [], Const (Unit, (3,7--3,9)), None, PreXmlDoc ((3,7), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], (4,4--5,7)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,7--3,9)), None, + (None, [], Const (Unit, (3,7--3,9)), None, PreXmlDoc ((3,7), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--5,7), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/Class 04.fs.bsl b/tests/service/data/SyntaxTree/Type/Class 04.fs.bsl index e137aa4ebfc..80c75b7d5da 100644 --- a/tests/service/data/SyntaxTree/Type/Class 04.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/Class 04.fs.bsl @@ -13,13 +13,13 @@ ImplFile (Class, [ImplicitCtor (Some (Private (3,7--3,14)), [], - SimplePats ([], [], (3,15--3,17)), None, + Const (Unit, (3,15--3,17)), None, PreXmlDoc ((3,7), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], (4,4--5,7)), [], Some (ImplicitCtor (Some (Private (3,7--3,14)), [], - SimplePats ([], [], (3,15--3,17)), None, + Const (Unit, (3,15--3,17)), None, PreXmlDoc ((3,7), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--5,7), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/Class 05.fs.bsl b/tests/service/data/SyntaxTree/Type/Class 05.fs.bsl index 903fa37b286..6516bdc8320 100644 --- a/tests/service/data/SyntaxTree/Type/Class 05.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/Class 05.fs.bsl @@ -19,8 +19,8 @@ ImplFile Target = None AppliesToGetterAndSetter = false Range = (3,9--3,10) }] - Range = (3,7--3,12) }], - SimplePats ([], [], (3,13--3,15)), None, + Range = (3,7--3,12) }], Const (Unit, (3,13--3,15)), + None, PreXmlDoc ((3,7), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], (4,4--5,7)), [], Some @@ -32,8 +32,8 @@ ImplFile Target = None AppliesToGetterAndSetter = false Range = (3,9--3,10) }] - Range = (3,7--3,12) }], - SimplePats ([], [], (3,13--3,15)), None, + Range = (3,7--3,12) }], Const (Unit, (3,13--3,15)), + None, PreXmlDoc ((3,7), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--5,7), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/Primary ctor 01.fs.bsl b/tests/service/data/SyntaxTree/Type/Primary ctor 01.fs.bsl index 24fbd4e7c8f..6717cb55b41 100644 --- a/tests/service/data/SyntaxTree/Type/Primary ctor 01.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/Primary ctor 01.fs.bsl @@ -12,12 +12,12 @@ ImplFile ObjectModel (Class, [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], (3,11--3,20)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,20), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/Primary ctor 02.fs.bsl b/tests/service/data/SyntaxTree/Type/Primary ctor 02.fs.bsl index a770ed3442e..fbb48e2fd0a 100644 --- a/tests/service/data/SyntaxTree/Type/Primary ctor 02.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/Primary ctor 02.fs.bsl @@ -12,12 +12,12 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], (3,5--3,10)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,10), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/Primary ctor 03.fs.bsl b/tests/service/data/SyntaxTree/Type/Primary ctor 03.fs.bsl index 882320fe29f..7bf4dedee58 100644 --- a/tests/service/data/SyntaxTree/Type/Primary ctor 03.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/Primary ctor 03.fs.bsl @@ -11,7 +11,7 @@ ImplFile false, None, (3,5--3,6)), Simple (None (3,5--3,6), (3,5--3,6)), [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], None, (3,5--3,6), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/Primary ctor 04.fs.bsl b/tests/service/data/SyntaxTree/Type/Primary ctor 04.fs.bsl index 6505497c378..a46c198cdfe 100644 --- a/tests/service/data/SyntaxTree/Type/Primary ctor 04.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/Primary ctor 04.fs.bsl @@ -12,12 +12,12 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], (3,5--3,10)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,10), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/Primary ctor 05.fs.bsl b/tests/service/data/SyntaxTree/Type/Primary ctor 05.fs.bsl index 352346a2396..17215f552fc 100644 --- a/tests/service/data/SyntaxTree/Type/Primary ctor 05.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/Primary ctor 05.fs.bsl @@ -11,7 +11,7 @@ ImplFile false, None, (3,5--3,6)), Simple (None (3,5--3,6), (3,5--3,6)), [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], None, (3,5--3,6), { LeadingKeyword = Type (3,0--3,4) diff --git a/tests/service/data/SyntaxTree/Type/SynMemberDefnInterfaceContainsTheRangeOfTheWithKeyword.fs.bsl b/tests/service/data/SyntaxTree/Type/SynMemberDefnInterfaceContainsTheRangeOfTheWithKeyword.fs.bsl index ed20d359009..cc2e1a3997f 100644 --- a/tests/service/data/SyntaxTree/Type/SynMemberDefnInterfaceContainsTheRangeOfTheWithKeyword.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/SynMemberDefnInterfaceContainsTheRangeOfTheWithKeyword.fs.bsl @@ -16,7 +16,7 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (2,8--2,10)), None, + (None, [], Const (Unit, (2,8--2,10)), None, PreXmlDoc ((2,8), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,8), { AsKeyword = None }); Interface @@ -57,7 +57,7 @@ ImplFile None, (5,4--5,19))], (3,4--5,19)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (2,8--2,10)), None, + (None, [], Const (Unit, (2,8--2,10)), None, PreXmlDoc ((2,8), FSharp.Compiler.Xml.XmlDocCollector), (2,5--2,8), { AsKeyword = None })), (2,5--5,19), { LeadingKeyword = Type (2,0--2,4) diff --git a/tests/service/data/SyntaxTree/Type/Type 11.fs.bsl b/tests/service/data/SyntaxTree/Type/Type 11.fs.bsl index fb9f7a3d57e..074cead59e8 100644 --- a/tests/service/data/SyntaxTree/Type/Type 11.fs.bsl +++ b/tests/service/data/SyntaxTree/Type/Type 11.fs.bsl @@ -12,12 +12,12 @@ ImplFile ObjectModel (Unspecified, [ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })], (3,5--3,10)), [], Some (ImplicitCtor - (None, [], SimplePats ([], [], (3,6--3,8)), None, + (None, [], Const (Unit, (3,6--3,8)), None, PreXmlDoc ((3,6), FSharp.Compiler.Xml.XmlDocCollector), (3,5--3,6), { AsKeyword = None })), (3,5--3,10), { LeadingKeyword = Type (3,0--3,4) diff --git a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs index 1cd71d4228e..0326688f987 100644 --- a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs +++ b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs @@ -1107,6 +1107,16 @@ module CancellableTasks = return! Task.WhenAll (tasks) } + let inline sequential (tasks: CancellableTask<'a> seq) = + cancellableTask { + let! ct = getCancellationToken () + let results = ResizeArray() + for task in tasks do + let! result = start ct task + results.Add(result) + return results + } + let inline ignore ([] ctask: CancellableTask<_>) = toUnit ctask /// diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index eb2f61c4147..b0eb7305713 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -87,7 +87,7 @@ module private SourceText = let combineValues (values: seq<'T>) = (0, values) ||> Seq.fold (fun hash value -> combine (value.GetHashCode()) hash) - let weakTable = ConditionalWeakTable() + let weakTable = ConditionalWeakTable() let create (sourceText: SourceText) = let sourceText = @@ -111,7 +111,9 @@ module private SourceText = |> Hash.combine encodingHash |> Hash.combine contentsHash |> Hash.combine sourceText.Length - interface ISourceText with + + override _.ToString() = sourceText.ToString() + interface ISourceTextNew with member _.Item with get index = sourceText.[index] @@ -197,6 +199,8 @@ module private SourceText = let lastLine = this.GetLineString(range.EndLine - 1) sb.Append(lastLine.Substring(0, range.EndColumn)).ToString() + + member _.GetChecksum() = sourceText.GetChecksum() } sourceText diff --git a/vsintegration/src/FSharp.Editor/Common/Logging.fs b/vsintegration/src/FSharp.Editor/Common/Logging.fs index 0ba681dc589..a69779e9acc 100644 --- a/vsintegration/src/FSharp.Editor/Common/Logging.fs +++ b/vsintegration/src/FSharp.Editor/Common/Logging.fs @@ -93,7 +93,10 @@ module Logging = let inline debug msg = Printf.kprintf Debug.WriteLine msg let private logger = lazy Logger(Logger.GlobalServiceProvider) - let private log logType msg = logger.Value.Log(logType, msg) + + let private log logType msg = + logger.Value.Log(logType, msg) + System.Diagnostics.Trace.TraceInformation(msg) let logMsg msg = log LogType.Message msg let logInfo msg = log LogType.Info msg diff --git a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs index a6d9a50dd60..f00deaa9250 100644 --- a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs +++ b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs @@ -629,14 +629,18 @@ type internal FSharpSignatureHelpProvider [] (serviceProvi let caretLineColumn = caretLinePos.Character let adjustedColumnInSource = - - let rec loop ch pos = - if Char.IsWhiteSpace(ch) then - loop sourceText.[pos - 1] (pos - 1) - else + let rec loop pos = + if pos = 0 then pos + else + let nextPos = pos - 1 + + if not (Char.IsWhiteSpace sourceText[nextPos]) then + pos + else + loop nextPos - loop sourceText.[caretPosition - 1] (caretPosition - 1) + loop (caretPosition - 1) let adjustedColumnChar = sourceText.[adjustedColumnInSource] diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnnecessaryParenthesesDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnnecessaryParenthesesDiagnosticAnalyzer.fs index bc433015ec9..07201f8a0a3 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnnecessaryParenthesesDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnnecessaryParenthesesDiagnosticAnalyzer.fs @@ -3,11 +3,12 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition +open System.Collections.Generic open System.Collections.Immutable open System.Runtime.Caching open System.Threading open System.Threading.Tasks -open FSharp.Compiler.EditorServices +open FSharp.Compiler.Syntax open FSharp.Compiler.Text open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics @@ -70,13 +71,31 @@ type internal UnnecessaryParenthesesDiagnosticAnalyzer [] let getLineString line = sourceText.Lines[Line.toZ line].ToString() - let! unnecessaryParentheses = UnnecessaryParentheses.getUnnecessaryParentheses getLineString parseResults.ParseTree + let unnecessaryParentheses = + (HashSet Range.comparer, parseResults.ParseTree) + ||> ParsedInput.fold (fun ranges path node -> + match node with + | SyntaxNode.SynExpr(SynExpr.Paren(expr = inner; rightParenRange = Some _; range = range)) when + not (SynExpr.shouldBeParenthesizedInContext getLineString path inner) + -> + ignore (ranges.Add range) + ranges + + | SyntaxNode.SynPat(SynPat.Paren(inner, range)) when not (SynPat.shouldBeParenthesizedInContext path inner) -> + ignore (ranges.Add range) + ranges + + | _ -> ranges) let diagnostics = - unnecessaryParentheses - |> Seq.map (fun range -> - Diagnostic.Create(descriptor, RoslynHelpers.RangeToLocation(range, sourceText, document.FilePath))) - |> Seq.toImmutableArray + let builder = ImmutableArray.CreateBuilder unnecessaryParentheses.Count + + for range in unnecessaryParentheses do + builder.Add( + Diagnostic.Create(descriptor, RoslynHelpers.RangeToLocation(range, sourceText, document.FilePath)) + ) + + builder.MoveToImmutable() ignore (cache.Remove key) diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.resx b/vsintegration/src/FSharp.Editor/FSharp.Editor.resx index ef5f05b3d24..6678a2d98e3 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.resx +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.resx @@ -227,6 +227,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 301428f69cb..b492cf1f35f 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -25,6 +25,7 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp open Microsoft.CodeAnalysis.Host.Mef open Microsoft.VisualStudio.FSharp.Editor.Telemetry open CancellableTasks +open FSharp.Compiler.Text #nowarn "9" // NativePtr.toNativeInt #nowarn "57" // Experimental stuff @@ -147,6 +148,8 @@ type internal FSharpWorkspaceServiceFactory [] let enableBackgroundItemKeyStoreAndSemanticClassification = editorOptions.LanguageServicePerformance.EnableBackgroundItemKeyStoreAndSemanticClassification + let useTransparentCompiler = editorOptions.Advanced.UseTransparentCompiler + // Default is false here let solutionCrawler = editorOptions.Advanced.SolutionBackgroundAnalysis @@ -168,6 +171,7 @@ type internal FSharpWorkspaceServiceFactory [] nameof enableBackgroundItemKeyStoreAndSemanticClassification, enableBackgroundItemKeyStoreAndSemanticClassification "captureIdentifiersWhenParsing", enableFastFindReferences + nameof useTransparentCompiler, useTransparentCompiler nameof solutionCrawler, solutionCrawler |], TelemetryThrottlingStrategy.NoThrottling @@ -187,13 +191,19 @@ type internal FSharpWorkspaceServiceFactory [] captureIdentifiersWhenParsing = enableFastFindReferences, documentSource = (if enableLiveBuffers then - DocumentSource.Custom getSource + (DocumentSource.Custom(fun filename -> + async { + match! getSource filename with + | Some source -> return Some(source :> ISourceText) + | None -> return None + })) else DocumentSource.FileSystem), - useSyntaxTreeCache = useSyntaxTreeCache + useSyntaxTreeCache = useSyntaxTreeCache, + useTransparentCompiler = useTransparentCompiler ) - if enableLiveBuffers then + if enableLiveBuffers && not useTransparentCompiler then workspace.WorkspaceChanged.Add(fun args -> if args.DocumentId <> null then cancellableTask { @@ -481,10 +491,10 @@ type internal HackCpsCommandLineChanges else Path.GetFileNameWithoutExtension projectFileName - [] /// This handles commandline change notifications from the Dotnet Project-system /// Prior to VS 15.7 path contained path to project file, post 15.7 contains target binpath /// binpath is more accurate because a project file can have multiple in memory projects based on configuration + [] member _.HandleCommandLineChanges ( path: string, @@ -527,10 +537,10 @@ type internal HackCpsCommandLineChanges let sourcePaths = sources |> Seq.map (fun s -> getFullPath s.Path) |> Seq.toArray - /// Due to an issue in project system, when we close and reopen solution, it sends the CommandLineChanges twice for every project. - /// First time it sends a correct path, sources, references and options. - /// Second time it sends a correct path, empty sources, empty references and empty options, and we rewrite our cache, and fail to colourize the document later. - /// As a workaround, until we have a fix from PS or will move to Roslyn as a source of truth, we will not overwrite the cache in case of empty lists. + // Due to an issue in project system, when we close and reopen solution, it sends the CommandLineChanges twice for every project. + // First time it sends a correct path, sources, references and options. + // Second time it sends a correct path, empty sources, empty references and empty options, and we rewrite our cache, and fail to colourize the document later. + // As a workaround, until we have a fix from PS or will move to Roslyn as a source of truth, we will not overwrite the cache in case of empty lists. if not (sources.IsEmpty && references.IsEmpty && options.IsEmpty) then let workspaceService = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 8a74c5ad795..261c4950ef9 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -3,6 +3,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Collections.Concurrent +open System.Collections.Generic open System.Collections.Immutable open System.Threading.Tasks @@ -80,9 +81,19 @@ module internal SymbolHelpers = // TODO: this needs to be a single event with a duration TelemetryReporter.ReportSingleEvent(TelemetryEvents.GetSymbolUsesInProjectsStarted, props) + let snapshotAccumulator = Dictionary() + + let! projects = + projects + |> Seq.map (fun project -> + project.GetFSharpProjectSnapshot(snapshotAccumulator) + |> CancellableTask.map (fun s -> project, s)) + |> CancellableTask.sequential + do! projects - |> Seq.map (fun project -> project.FindFSharpReferencesAsync(symbol, onFound, "getSymbolUsesInProjects")) + |> Seq.map (fun (project, snapshot) -> + project.FindFSharpReferencesAsync(symbol, snapshot, onFound, "getSymbolUsesInProjects")) |> CancellableTask.whenAll TelemetryReporter.ReportSingleEvent(TelemetryEvents.GetSymbolUsesInProjectsFinished, props) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 891eb960ab3..154517ed6a1 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -9,12 +9,327 @@ open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot open FSharp.Compiler.Symbols -open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks +open FSharp.Compiler.BuildGraph + +open CancellableTasks + +open Internal.Utilities.Collections +open Newtonsoft.Json +open Newtonsoft.Json.Linq +open System.Text.Json.Nodes + +#nowarn "57" // Experimental stuff + +[] +module internal ProjectCache = + + /// This is a cache to maintain FSharpParsingOptions and FSharpProjectOptions per Roslyn Project. + /// The Roslyn Project is held weakly meaning when it is cleaned up by the GC, the FSharParsingOptions and FSharpProjectOptions will be cleaned up by the GC. + /// At some point, this will be the main caching mechanism for FCS projects instead of FCS itself. + let Projects = + ConditionalWeakTable() + +type Solution with + + /// Get the instance of IFSharpWorkspaceService. + member internal this.GetFSharpWorkspaceService() = + this.Workspace.Services.GetRequiredService() + +module internal FSharpProjectSnapshotSerialization = + + let serializeFileSnapshot (snapshot: FSharpFileSnapshot) = + let output = JObject() + output.Add("FileName", snapshot.FileName) + output.Add("Version", snapshot.Version) + output + + let serializeReferenceOnDisk (reference: ReferenceOnDisk) = + let output = JObject() + output.Add("Path", reference.Path) + output.Add("LastModified", reference.LastModified) + output + + let rec serializeReferencedProject (reference: FSharpReferencedProjectSnapshot) = + let output = JObject() + + match reference with + | FSharpReference(projectOutputFile, snapshot) -> + output.Add("projectOutputFile", projectOutputFile) + output.Add("snapshot", serializeSnapshot snapshot) + | _ -> () + + output + + and serializeSnapshot (snapshot: FSharpProjectSnapshot) = + + let output = JObject() + let snapshot = snapshot.ProjectSnapshot + + output.Add("ProjectFileName", snapshot.ProjectFileName) + output.Add("ProjectId", (snapshot.ProjectId |> Option.defaultValue null |> JToken.FromObject)) + output.Add("SourceFiles", snapshot.SourceFiles |> Seq.map serializeFileSnapshot |> JArray) + output.Add("ReferencesOnDisk", snapshot.ReferencesOnDisk |> Seq.map serializeReferenceOnDisk |> JArray) + output.Add("OtherOptions", JArray(snapshot.OtherOptions)) + output.Add("ReferencedProjects", snapshot.ReferencedProjects |> Seq.map serializeReferencedProject |> JArray) + output.Add("IsIncompleteTypeCheckEnvironment", snapshot.IsIncompleteTypeCheckEnvironment) + output.Add("UseScriptResolutionRules", snapshot.UseScriptResolutionRules) + output.Add("LoadTime", snapshot.LoadTime) + // output.Add("UnresolvedReferences", snapshot.UnresolvedReferences) + output.Add( + "OriginalLoadReferences", + snapshot.OriginalLoadReferences + |> Seq.map (fun (r: Text.range, a, b) -> JArray(r.FileName, r.Start, r.End, a, b)) + |> JArray + ) + + output.Add("Stamp", (snapshot.Stamp |> (Option.defaultValue 0) |> JToken.FromObject)) + + output + + let dumpToJson (snapshot) = + + let jObject = serializeSnapshot snapshot + + let json = jObject.ToString(Formatting.Indented) + + json + +open FSharpProjectSnapshotSerialization +open System.Collections.Concurrent [] module private CheckerExtensions = + let snapshotCache = AsyncMemoize(1000, 500, "SnapshotCache") + + let latestSnapshots = ConcurrentDictionary<_, _>() + + let exist xs = xs |> Seq.isEmpty |> not + + let getFSharpOptionsForProject (this: Project) = + if not this.IsFSharp then + raise (OperationCanceledException("Project is not a FSharp project.")) + else + match ProjectCache.Projects.TryGetValue(this) with + | true, result -> CancellableTask.singleton result + | _ -> + cancellableTask { + + let! ct = CancellableTask.getCancellationToken () + + let service = this.Solution.GetFSharpWorkspaceService() + let projectOptionsManager = service.FSharpProjectOptionsManager + + match! projectOptionsManager.TryGetOptionsByProject(this, ct) with + | ValueNone -> return raise (OperationCanceledException("FSharp project options not found.")) + | ValueSome(parsingOptions, projectOptions) -> + let result = + (service.Checker, projectOptionsManager, parsingOptions, projectOptions) + + return ProjectCache.Projects.GetValue(this, ConditionalWeakTable<_, _>.CreateValueCallback(fun _ -> result)) + } + + let documentToSnapshot (document: Document) = + cancellableTask { + let! version = document.GetTextVersionAsync() + + let getSource () = + task { + let! sourceText = document.GetTextAsync() + return sourceText.ToFSharpSourceText() + } + + return FSharpFileSnapshot(FileName = document.FilePath, Version = version.ToString(), GetSource = getSource) + } + + let getReferencedProjectVersions (project: Project) = + project.GetAllProjectsThisProjectDependsOn() + |> Seq.map (fun r ct -> r.GetDependentSemanticVersionAsync(ct)) + |> CancellableTask.whenAll + |> CancellableTask.map (Seq.map (fun x -> x.ToString()) >> Set) + + let getOnDiskReferences (options: FSharpProjectOptions) = + options.OtherOptions + |> Seq.filter (fun x -> x.StartsWith("-r:")) + |> Seq.map (fun x -> + let path = x.Substring(3) + + { + Path = path + LastModified = System.IO.File.GetLastWriteTimeUtc path + }) + |> Seq.toList + + let createProjectSnapshot (snapshotAccumulatorOpt) (project: Project) (options: FSharpProjectOptions option) = + cancellableTask { + + let! options = + match options with + | Some options -> CancellableTask.singleton options + | None -> + cancellableTask { + let! _, _, _, options = getFSharpOptionsForProject project + return options + } + + let! projectVersion = project.GetDependentSemanticVersionAsync() + + let! referenceVersions = getReferencedProjectVersions project + + let updatedSnapshot = + match project.IsTransparentCompilerSnapshotReuseEnabled, latestSnapshots.TryGetValue project.Id with + | true, (true, (_, _, oldReferenceVersions, _, _)) when referenceVersions <> oldReferenceVersions -> + System.Diagnostics.Trace.TraceWarning "Reference versions changed" + None + + | true, (true, (_, _, _, _, oldSnapshot: FSharpProjectSnapshot)) when + oldSnapshot.ProjectSnapshot.ReferencesOnDisk <> (getOnDiskReferences options) + -> + System.Diagnostics.Trace.TraceWarning "References on disk changed" + None + + | true, (true, (_, oldProjectVersion, _, _, oldSnapshot: FSharpProjectSnapshot)) when projectVersion = oldProjectVersion -> + Some(CancellableTask.singleton oldSnapshot) + + | true, (true, (oldProject, _oldProjectVersion, _oldReferencesVersion, oldOptions, oldSnapshot: FSharpProjectSnapshot)) when + FSharpProjectOptions.AreSameForChecking(options, oldOptions) + -> + + let changes = project.GetChanges(oldProject) + + if + changes.GetAddedDocuments() |> exist + || changes.GetRemovedDocuments() |> exist + || changes.GetAddedMetadataReferences() |> exist + || changes.GetRemovedMetadataReferences() |> exist + || changes.GetAddedProjectReferences() |> exist + || changes.GetRemovedProjectReferences() |> exist + then + // if any of that happened, we create it from scratch + System.Diagnostics.Trace.TraceWarning "Project change not covered by options - suspicious" + None + + else + // we build it from the previous one + + let changedDocuments = changes.GetChangedDocuments() |> Seq.toList + + System.Diagnostics.Trace.TraceInformation + $"Incremental update of FSharpProjectSnapshot ({oldSnapshot.Label}) - {changedDocuments.Length} changed documents" + + if changedDocuments.Length = 0 then + // this is suspicious + let _breakpoint = "here" + () + + changedDocuments + |> Seq.map (project.GetDocument >> documentToSnapshot) + |> CancellableTask.whenAll + |> CancellableTask.map (Array.toList >> oldSnapshot.Replace) + |> Some + + | _ -> None + + let! newSnapshot = + + match updatedSnapshot with + | Some snapshot -> snapshot + | _ -> + cancellableTask { + + let solution = project.Solution + + let projects = + solution.Projects + |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) + |> Map + + let getFileSnapshot (options: FSharpProjectOptions) path = + async { + let project = projects.TryFind options.ProjectFileName + + if project.IsNone then + System.Diagnostics.Trace.TraceError( + "Could not find project {0} in solution {1}", + options.ProjectFileName, + solution.FilePath + ) + + let documentOpt = project |> Option.bind (Map.tryFind path) + + let! version, getSource = + match documentOpt with + | Some document -> + async { + + let! version = document.GetTextVersionAsync() |> Async.AwaitTask + + let getSource () = + task { + let! sourceText = document.GetTextAsync() + return sourceText.ToFSharpSourceText() + } + + return version.ToString(), getSource + + } + | None -> + // This happens with files that are read from /obj + + // Fall back to file system + let version = System.IO.File.GetLastWriteTimeUtc(path) + + let getSource () = + task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceTextNew.ofString } + + async.Return(version.ToString(), getSource) + + return FSharpFileSnapshot(FileName = path, Version = version, GetSource = getSource) + } + + let! snapshot = + FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) + + System.Diagnostics.Trace.TraceInformation $"Created new FSharpProjectSnapshot ({snapshot.Label})" + + return snapshot + } + + let latestSnapshotData = + project, projectVersion, referenceVersions, options, newSnapshot + + latestSnapshots.AddOrUpdate(project.Id, latestSnapshotData, (fun _ _ -> latestSnapshotData)) + |> ignore + + return newSnapshot + } + + let getOrCreateSnapshotForProject (project: Project) options snapshotAccumulatorOpt = + + let key = + { new ICacheKey<_, _> with + member _.GetKey() = project.Id + member _.GetVersion() = project + member _.GetLabel() = project.FilePath + } + + snapshotCache.Get( + key, + node { + let! ct = NodeCode.CancellationToken + + return! + createProjectSnapshot snapshotAccumulatorOpt project options ct + |> NodeCode.AwaitTask + } + ) + |> Async.AwaitNodeCode + + let getProjectSnapshotForDocument (document: Document, options: FSharpProjectOptions) = + getOrCreateSnapshotForProject document.Project (Some options) None + type FSharpChecker with /// Parse the source text from the Roslyn document. @@ -26,6 +341,32 @@ module private CheckerExtensions = return! checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName = userOpName) } + member checker.ParseDocumentUsingTransparentCompiler(document: Document, options: FSharpProjectOptions, userOpName: string) = + cancellableTask { + let! projectSnapshot = getProjectSnapshotForDocument (document, options) + return! checker.ParseFile(document.FilePath, projectSnapshot, userOpName = userOpName) + } + + member checker.ParseAndCheckDocumentUsingTransparentCompiler + ( + document: Document, + options: FSharpProjectOptions, + userOpName: string + ) = + cancellableTask { + + checker.TransparentCompiler.SetCacheSizeFactor(document.Project.TransparentCompilerCacheFactor) + + let! projectSnapshot = getProjectSnapshotForDocument (document, options) + + let! (parseResults, checkFileAnswer) = checker.ParseAndCheckFileInProject(document.FilePath, projectSnapshot, userOpName) + + return + match checkFileAnswer with + | FSharpCheckFileAnswer.Aborted -> None + | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> Some(parseResults, checkFileResults) + } + /// Parse and check the source text from the Roslyn document with possible stale results. member checker.ParseAndCheckDocumentWithPossibleStaleResults ( @@ -106,28 +447,18 @@ module private CheckerExtensions = ?allowStaleResults: bool ) = cancellableTask { - let allowStaleResults = - match allowStaleResults with - | Some b -> b - | _ -> document.Project.IsFSharpStaleCompletionResultsEnabled - - return! checker.ParseAndCheckDocumentWithPossibleStaleResults(document, options, allowStaleResults, userOpName = userOpName) - } -[] -module internal ProjectCache = - - /// This is a cache to maintain FSharpParsingOptions and FSharpProjectOptions per Roslyn Project. - /// The Roslyn Project is held weakly meaning when it is cleaned up by the GC, the FSharParsingOptions and FSharpProjectOptions will be cleaned up by the GC. - /// At some point, this will be the main caching mechanism for FCS projects instead of FCS itself. - let Projects = - ConditionalWeakTable() - -type Solution with + if checker.UsesTransparentCompiler then + return! checker.ParseAndCheckDocumentUsingTransparentCompiler(document, options, userOpName) + else + let allowStaleResults = + match allowStaleResults with + | Some b -> b + | _ -> document.Project.IsFSharpStaleCompletionResultsEnabled - /// Get the instance of IFSharpWorkspaceService. - member internal this.GetFSharpWorkspaceService() = - this.Workspace.Services.GetRequiredService() + return! + checker.ParseAndCheckDocumentWithPossibleStaleResults(document, options, allowStaleResults, userOpName = userOpName) + } type Document with @@ -195,8 +526,12 @@ type Document with /// Parses the given F# document. member this.GetFSharpParseResultsAsync(userOpName) = cancellableTask { - let! checker, _, parsingOptions, _ = this.GetFSharpCompilationOptionsAsync(userOpName) - return! checker.ParseDocument(this, parsingOptions, userOpName) + let! checker, _, parsingOptions, options = this.GetFSharpCompilationOptionsAsync(userOpName) + + if this.Project.UseTransparentCompiler then + return! checker.ParseDocumentUsingTransparentCompiler(this, options, userOpName) + else + return! checker.ParseDocument(this, parsingOptions, userOpName) } /// Parses and checks the given F# document. @@ -213,7 +548,15 @@ type Document with member this.GetFSharpSemanticClassificationAsync(userOpName) = cancellableTask { let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync(userOpName) - let! result = checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectOptions) + + let! result = + if this.Project.UseTransparentCompiler then + async { + let! projectSnapshot = getProjectSnapshotForDocument (this, projectOptions) + return! checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectSnapshot) + } + else + checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectOptions) return result @@ -221,18 +564,22 @@ type Document with } /// Find F# references in the given F# document. - member inline this.FindFSharpReferencesAsync(symbol, [] onFound, userOpName) = + member inline this.FindFSharpReferencesAsync(symbol, projectSnapshot: FSharpProjectSnapshot, [] onFound, userOpName) = cancellableTask { let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync(userOpName) let! symbolUses = - checker.FindBackgroundReferencesInFile( - this.FilePath, - projectOptions, - symbol, - canInvalidateProject = false, - fastCheck = this.Project.IsFastFindReferencesEnabled - ) + + if this.Project.UseTransparentCompiler then + checker.FindBackgroundReferencesInFile(this.FilePath, projectSnapshot, symbol) + else + checker.FindBackgroundReferencesInFile( + this.FilePath, + projectOptions, + symbol, + canInvalidateProject = false, + fastCheck = this.Project.IsFastFindReferencesEnabled + ) do! symbolUses @@ -267,7 +614,7 @@ type Document with type Project with /// Find F# references in the given project. - member this.FindFSharpReferencesAsync(symbol: FSharpSymbol, onFound, userOpName) = + member this.FindFSharpReferencesAsync(symbol: FSharpSymbol, projectSnapshot, onFound, userOpName) = cancellableTask { let declarationLocation = @@ -307,32 +654,15 @@ type Project with if this.IsFastFindReferencesEnabled then do! documents - |> Seq.map (fun doc -> doc.FindFSharpReferencesAsync(symbol, (fun range -> onFound doc range), userOpName)) + |> Seq.map (fun doc -> + doc.FindFSharpReferencesAsync(symbol, projectSnapshot, (fun range -> onFound doc range), userOpName)) |> CancellableTask.whenAll else for doc in documents do - do! doc.FindFSharpReferencesAsync(symbol, (fun range -> onFound doc range), userOpName) + do! doc.FindFSharpReferencesAsync(symbol, projectSnapshot, (onFound doc), userOpName) } - member this.GetFSharpCompilationOptionsAsync() = - if not this.IsFSharp then - raise (OperationCanceledException("Project is not a FSharp project.")) - else - match ProjectCache.Projects.TryGetValue(this) with - | true, result -> CancellableTask.singleton result - | _ -> - cancellableTask { - - let! ct = CancellableTask.getCancellationToken () - - let service = this.Solution.GetFSharpWorkspaceService() - let projectOptionsManager = service.FSharpProjectOptionsManager + member this.GetFSharpCompilationOptionsAsync() = this |> getFSharpOptionsForProject - match! projectOptionsManager.TryGetOptionsByProject(this, ct) with - | ValueNone -> return raise (OperationCanceledException("FSharp project options not found.")) - | ValueSome(parsingOptions, projectOptions) -> - let result = - (service.Checker, projectOptionsManager, parsingOptions, projectOptions) - - return ProjectCache.Projects.GetValue(this, ConditionalWeakTable<_, _>.CreateValueCallback(fun _ -> result)) - } + member this.GetFSharpProjectSnapshot(?snapshotAccumulator) = + cancellableTask { return! getOrCreateSnapshotForProject this None snapshotAccumulator } diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 250565319e7..e9b7f60252a 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -83,6 +83,7 @@ type CodeFixesOptions = type LanguageServicePerformanceOptions = { EnableInMemoryCrossProjectReferences: bool + TransparentCompilerCacheFactor: int AllowStaleCompletionResults: bool TimeUntilStaleCompletion: int EnableParallelReferenceResolution: bool @@ -97,6 +98,7 @@ type LanguageServicePerformanceOptions = static member Default = { EnableInMemoryCrossProjectReferences = true + TransparentCompilerCacheFactor = 100 AllowStaleCompletionResults = true TimeUntilStaleCompletion = 2000 // In ms, so this is 2 seconds EnableParallelReferenceResolution = false @@ -117,6 +119,8 @@ type AdvancedOptions = IsInlineParameterNameHintsEnabled: bool IsInlineReturnTypeHintsEnabled: bool IsUseLiveBuffersEnabled: bool + UseTransparentCompiler: bool + TransparentCompilerSnapshotReuse: bool SendAdditionalTelemetry: bool SolutionBackgroundAnalysis: bool } @@ -128,6 +132,8 @@ type AdvancedOptions = IsInlineTypeHintsEnabled = false IsInlineParameterNameHintsEnabled = false IsInlineReturnTypeHintsEnabled = false + UseTransparentCompiler = false + TransparentCompilerSnapshotReuse = false IsUseLiveBuffersEnabled = true SendAdditionalTelemetry = true SolutionBackgroundAnalysis = false @@ -265,3 +271,11 @@ module EditorOptionsExtensions = member this.IsFastFindReferencesEnabled = this.EditorOptions.LanguageServicePerformance.EnableFastFindReferencesAndRename + + member this.UseTransparentCompiler = this.EditorOptions.Advanced.UseTransparentCompiler + + member this.IsTransparentCompilerSnapshotReuseEnabled = + this.EditorOptions.Advanced.TransparentCompilerSnapshotReuse + + member this.TransparentCompilerCacheFactor = + this.EditorOptions.LanguageServicePerformance.TransparentCompilerCacheFactor diff --git a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs index 2a520d72066..b841b7e8cd6 100644 --- a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs +++ b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs @@ -105,9 +105,11 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw TelemetryService.DefaultSession.IsUserMicrosoftInternal else let workspace = componentModel.GetService() + let options = workspace.Services.GetService() TelemetryService.DefaultSession.IsUserMicrosoftInternal - || workspace.Services.GetService().Advanced.SendAdditionalTelemetry) + || options.Advanced.SendAdditionalTelemetry + || options.Advanced.UseTransparentCompiler) static member ReportFault(name, ?severity: FaultSeverity, ?e: exn) = if TelemetryReporter.SendAdditionalTelemetry.Value then diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf index 8cd60083c90..45a6e2c861a 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf @@ -41,18 +41,20 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis - Vodítka struktury bloků; -Zobrazit pokyny ke struktuře pro kód F#; -Osnova; -Zobrazit osnovu a sbalitelné uzly kódu F#; -Vložené tipy; -Zobrazit tipy pro vložený typ (náhled); -Zobrazení tipů pro návratový typ (náhled); -Zobrazit nápovědy k názvům vložených parametrů (náhled); -Živé vyrovnávací paměti; -Použití živých (neuložených) vyrovnávacích pamětí pro analýzu + Block Structure Guides; +Show structure guidelines for F# code; +Outlining; +Show outlining and collapsible nodes for F# code; +Inline hints; +Display inline type hints (preview); +Display return type hints (preview); +Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); +Live Buffers; +Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf index bc05638c6f5..db16e553221 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf @@ -41,18 +41,20 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis - Führungslinien für Blockstruktur; -Strukturrichtlinien für F#-Code anzeigen; -Gliederung; -Gliederungs- und reduzierbare Knoten für F#-Code anzeigen; -Inlinehinweise; -Hinweise zu Inlinetypen anzeigen (Vorschau); -Hinweise zu Rückgabetypen anzeigen (Vorschau); -Hinweise zu Inlineparameternamen anzeigen (Vorschau); -Livepuffer; -Livepuffer (nicht gespeichert) zur Analyse verwenden + Block Structure Guides; +Show structure guidelines for F# code; +Outlining; +Show outlining and collapsible nodes for F# code; +Inline hints; +Display inline type hints (preview); +Display return type hints (preview); +Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); +Live Buffers; +Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf index 4b789c84545..341dea0e663 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf @@ -41,18 +41,20 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis - Guías de estructura de bloques; -Mostrar guías de estructura para código F#; -Esquema; -Mostrar esquema y nodos colapsables para código F#; -Sugerencias insertadas; -Mostrar sugerencias de tipo insertadas (vista previa); -Mostrar sugerencias de tipo de valor devuelto (vista previa); -Mostrar sugerencias de nombres de parámetro insertadas (vista previa) -Búferes activos; -Usar búferes activos (no guardados) para el análisis + Block Structure Guides; +Show structure guidelines for F# code; +Outlining; +Show outlining and collapsible nodes for F# code; +Inline hints; +Display inline type hints (preview); +Display return type hints (preview); +Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); +Live Buffers; +Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf index 93f27375a08..4a5d1c24619 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf @@ -41,18 +41,20 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis - Guides de structure de bloc; -Afficher les directives de structure pour le code F# ; -Décrire; -Afficher les nœuds de plan et réductibles pour le code F#; -Conseils en ligne; -Afficher les conseils de type en ligne (aperçu); -Afficher les conseils sur le type de retour (aperçu); -Afficher les conseils sur le nom des paramètres en ligne (aperçu); -Tampons en direct; -Utilisez des tampons en direct (non enregistrés) pour analyser + Block Structure Guides; +Show structure guidelines for F# code; +Outlining; +Show outlining and collapsible nodes for F# code; +Inline hints; +Display inline type hints (preview); +Display return type hints (preview); +Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); +Live Buffers; +Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf index c43e7044b9b..72b883b554e 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf @@ -41,18 +41,20 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis - Guide per strutture a blocchi; -Mostra le linee guida per la struttura per il codice F#; -Struttura; -Mostra i nodi struttura e comprimibili per il codice F#; -Suggerimenti inline; -Visualizza suggerimenti di tipo inline (anteprima); -Visualizza suggerimenti di tipo restituito (anteprima); -Visualizza suggerimenti per i nomi di parametro inline (anteprima); -Buffer in tempo reale; -Usa buffer in tempo reale (non salvati) per l’analisi + Block Structure Guides; +Show structure guidelines for F# code; +Outlining; +Show outlining and collapsible nodes for F# code; +Inline hints; +Display inline type hints (preview); +Display return type hints (preview); +Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); +Live Buffers; +Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf index 9d9dfb23c2e..5a0d923c19d 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf @@ -41,18 +41,20 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis - ブロック構造のガイド; -F# コードの構造のガイドラインを表示; -アウトライン表示; -F# コードのアウトラインおよび折りたたみ可能なノードを表示する; -インライン ヒント; -インライン型のヒントを表示する (プレビュー); -戻り値型のヒントを表示する (プレビュー); -インライン パラメーター名のヒントを表示 (プレビュー); -ライブ バッファー; -分析にライブ (未保存) バッファーを使用する + Block Structure Guides; +Show structure guidelines for F# code; +Outlining; +Show outlining and collapsible nodes for F# code; +Inline hints; +Display inline type hints (preview); +Display return type hints (preview); +Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); +Live Buffers; +Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf index f5d17cf4c7d..3efb9bc0f81 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf @@ -41,18 +41,20 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis - 블록 구조 가이드; -F# 코드에 대한 구조 지침 표시; -개요; -F# 코드에 대한 개요 및 축소 가능한 노드 표시; -인라인 힌트; -인라인 형식 힌트 표시(미리 보기); -반환 형식 힌트 표시(미리 보기); -인라인 매개 변수 이름 힌트 표시(미리 보기); -라이브 버퍼; -분석에 라이브(저장되지 않은) 버퍼 사용 + Block Structure Guides; +Show structure guidelines for F# code; +Outlining; +Show outlining and collapsible nodes for F# code; +Inline hints; +Display inline type hints (preview); +Display return type hints (preview); +Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); +Live Buffers; +Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf index aadc760089a..121dc5c5183 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf @@ -41,18 +41,20 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis - Przewodniki po strukturze bloku; -Pokaż przewodniki po strukturze dla kodu języka F#; -Tworzenie konspektu; -Pokaż konspekt i węzły z możliwością zwijania dla kodu języka F#; -Wskazówki w tekście; -Wyświetl wskazówki dotyczące typu w tekście (wersja zapoznawcza); -Wyświetlaj wskazówki dotyczące zwracanego typu (wersja zapoznawcza); -Wyświetl wskazówki dotyczące nazw parametrów w tekście (wersja zapoznawcza); -Bufory bieżące; -Do analizy używaj buforów bieżących (niezapisanych) + Block Structure Guides; +Show structure guidelines for F# code; +Outlining; +Show outlining and collapsible nodes for F# code; +Inline hints; +Display inline type hints (preview); +Display return type hints (preview); +Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); +Live Buffers; +Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf index e52c4a34ea9..aafa05fa6a6 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf @@ -41,18 +41,20 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis - Guias de Estrutura de Bloco; -Mostrar diretrizes de estrutura para código F#; -Estrutura de tópicos; -Mostrar nós de estrutura de tópicos e recolhíveis para código F#; -Dicas embutidas; -Exibir dicas de tipo embutido (versão prévia); -Exibir dicas de tipo de retorno (versão prévia); -Exibir dicas de nome de parâmetro embutido (versão prévia); -Buffers Dinâmicos; -Usar buffers dinâmicos (não salvos) para verificação + Block Structure Guides; +Show structure guidelines for F# code; +Outlining; +Show outlining and collapsible nodes for F# code; +Inline hints; +Display inline type hints (preview); +Display return type hints (preview); +Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); +Live Buffers; +Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf index 6b67d4dee3e..d8f8b0e4ecc 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf @@ -41,18 +41,20 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis - Руководства по блочной структуре; -Показать рекомендации по структуре кода F#; -Структурирование; -Показать структурные и сворачиваемые узлы кода F#; -Встроенные подсказки; -Отображать подсказки встроенного типа (предварительная версия); -Отображать подсказки типа возвращаемого значения (предварительная версия); -Отображать подсказки имен встроенных параметров (предварительная версия); -Динамические буферы; -Используйте для анализа живые (несохраненные) буферы. + Block Structure Guides; +Show structure guidelines for F# code; +Outlining; +Show outlining and collapsible nodes for F# code; +Inline hints; +Display inline type hints (preview); +Display return type hints (preview); +Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); +Live Buffers; +Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf index e943accb2c6..18e447002dc 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf @@ -41,18 +41,20 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis - Blok Yapısı Kılavuzları; -F# kodu için yapı yönergelerini göster; -Ana hat oluşturma; -F# kodu için ana hattı ve daraltılabilir düğümleri göster; -Satır içi ipuçları; -Satır içi tür ipuçlarını görüntüle (önizleme); -Dönüş türü ipuçlarını görüntüle (önizleme); -Satır içi parametre adı ipuçlarını görüntüle (önizleme); -Canlı Arabellekler; -Analiz için canlı (kaydedilmemiş) arabellekleri kullan + Block Structure Guides; +Show structure guidelines for F# code; +Outlining; +Show outlining and collapsible nodes for F# code; +Inline hints; +Display inline type hints (preview); +Display return type hints (preview); +Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); +Live Buffers; +Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf index 5e7381c2da5..3533c0f345d 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf @@ -41,6 +41,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis 块结构指南; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf index 7152105652b..1ee79fd1f50 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf @@ -41,18 +41,20 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for analysis - 區塊結構輔助線; -顯示 F# 程式碼的結構方針; -大綱; -顯示 F# 程式碼的大綱與可折疊的節點; -內嵌提示; -顯示內嵌類型提示 (預覽); -顯示傳回類型提示 (預覽); -顯示內嵌參數名稱提示 (預覽); -即時緩衝區; -使用即時 (未儲存) 緩衝區進行分析 + Block Structure Guides; +Show structure guidelines for F# code; +Outlining; +Show outlining and collapsible nodes for F# code; +Inline hints; +Display inline type hints (preview); +Display return type hints (preview); +Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); +Live Buffers; +Use live (unsaved) buffers for analysis diff --git a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml index ecabe2a56fd..15397d068ec 100644 --- a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml +++ b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml @@ -38,6 +38,17 @@ + + + + diff --git a/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml b/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml index 96b8915b80e..a5c5f1c0b6f 100644 --- a/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml +++ b/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml @@ -23,6 +23,28 @@ IsChecked="{Binding EnableInMemoryCrossProjectReferences}" Content="{x:Static local:Strings.Enable_in_memory_cross_project_references}" ToolTip="{x:Static local:Strings.Tooltip_in_memory_cross_project_references}"/> + + + + + + @@ -46,7 +68,7 @@ Content="{x:Static local:Strings.Time_until_stale_completion}" Margin="15 0 0 0"/> diff --git a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs index 57b599de866..b166f234ccd 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs +++ b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -13,12 +13,12 @@ namespace Microsoft.VisualStudio.FSharp.UIResources { /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -33,7 +33,7 @@ internal Strings() { } /// - /// 返回此类使用的缓存的 ResourceManager 实例。 + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ internal Strings() { } /// - /// 重写当前线程的 CurrentUICulture 属性,对 - /// 使用此强类型资源类的所有资源查找执行重写。 + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { @@ -61,7 +61,7 @@ internal Strings() { } /// - /// 查找类似 Additional performance telemetry (experimental) 的本地化字符串。 + /// Looks up a localized string similar to Additional performance telemetry (experimental). /// public static string AdditionalTelemetry { get { @@ -70,7 +70,7 @@ public static string AdditionalTelemetry { } /// - /// 查找类似 Always place open statements at the top level 的本地化字符串。 + /// Looks up a localized string similar to Always place open statements at the top level. /// public static string Always_place_opens_at_top_level { get { @@ -79,7 +79,7 @@ public static string Always_place_opens_at_top_level { } /// - /// 查找类似 Keep analyzing the entire solution for diagnostics as a low priority background task (requires restart) 的本地化字符串。 + /// Looks up a localized string similar to Keep analyzing the entire solution for diagnostics as a low priority background task (requires restart). /// public static string Analyze_full_solution_on_background { get { @@ -88,7 +88,7 @@ public static string Analyze_full_solution_on_background { } /// - /// 查找类似 Background analysis 的本地化字符串。 + /// Looks up a localized string similar to Background analysis. /// public static string Background_analysis { get { @@ -97,7 +97,7 @@ public static string Background_analysis { } /// - /// 查找类似 Block Structure Guides 的本地化字符串。 + /// Looks up a localized string similar to Block Structure Guides. /// public static string Block_Structure { get { @@ -106,7 +106,7 @@ public static string Block_Structure { } /// - /// 查找类似 Code Fixes 的本地化字符串。 + /// Looks up a localized string similar to Code Fixes. /// public static string Code_Fixes { get { @@ -115,7 +115,7 @@ public static string Code_Fixes { } /// - /// 查找类似 Completion Lists 的本地化字符串。 + /// Looks up a localized string similar to Completion Lists. /// public static string Completion_Lists { get { @@ -124,7 +124,7 @@ public static string Completion_Lists { } /// - /// 查找类似 D_ash underline 的本地化字符串。 + /// Looks up a localized string similar to D_ash underline. /// public static string Dash_underline { get { @@ -133,7 +133,7 @@ public static string Dash_underline { } /// - /// 查找类似 Diagnostics 的本地化字符串。 + /// Looks up a localized string similar to Diagnostics. /// public static string Diagnostics { get { @@ -142,7 +142,7 @@ public static string Diagnostics { } /// - /// 查找类似 D_ot underline 的本地化字符串。 + /// Looks up a localized string similar to D_ot underline. /// public static string Dot_underline { get { @@ -151,7 +151,7 @@ public static string Dot_underline { } /// - /// 查找类似 Keep background symbol keys 的本地化字符串。 + /// Looks up a localized string similar to Keep background symbol keys. /// public static string Enable_Background_ItemKeyStore_And_Semantic_Classification { get { @@ -160,7 +160,7 @@ public static string Enable_Background_ItemKeyStore_And_Semantic_Classification } /// - /// 查找类似 Enable fast find references & rename (experimental) 的本地化字符串。 + /// Looks up a localized string similar to Enable fast find references & rename (experimental). /// public static string Enable_Fast_Find_References { get { @@ -169,7 +169,7 @@ public static string Enable_Fast_Find_References { } /// - /// 查找类似 _Enable in-memory cross project references 的本地化字符串。 + /// Looks up a localized string similar to _Enable in-memory cross project references. /// public static string Enable_in_memory_cross_project_references { get { @@ -178,7 +178,7 @@ public static string Enable_in_memory_cross_project_references { } /// - /// 查找类似 Use live (unsaved) buffers for analysis (restart required) 的本地化字符串。 + /// Looks up a localized string similar to Use live (unsaved) buffers for analysis (restart required). /// public static string Enable_Live_Buffers { get { @@ -187,7 +187,7 @@ public static string Enable_Live_Buffers { } /// - /// 查找类似 Enable parallel reference resolution 的本地化字符串。 + /// Looks up a localized string similar to Enable parallel reference resolution. /// public static string Enable_Parallel_Reference_Resolution { get { @@ -196,7 +196,7 @@ public static string Enable_Parallel_Reference_Resolution { } /// - /// 查找类似 Enable partial type checking 的本地化字符串。 + /// Looks up a localized string similar to Enable partial type checking. /// public static string Enable_partial_type_checking { get { @@ -205,7 +205,7 @@ public static string Enable_partial_type_checking { } /// - /// 查找类似 Enable stale data for IntelliSense features 的本地化字符串。 + /// Looks up a localized string similar to Enable stale data for IntelliSense features. /// public static string Enable_Stale_IntelliSense_Results { get { @@ -214,7 +214,7 @@ public static string Enable_Stale_IntelliSense_Results { } /// - /// 查找类似 Always add new line on enter 的本地化字符串。 + /// Looks up a localized string similar to Always add new line on enter. /// public static string Enter_key_always { get { @@ -223,7 +223,7 @@ public static string Enter_key_always { } /// - /// 查找类似 Never add new line on enter 的本地化字符串。 + /// Looks up a localized string similar to Never add new line on enter. /// public static string Enter_key_never { get { @@ -232,7 +232,7 @@ public static string Enter_key_never { } /// - /// 查找类似 Only add new line on enter after end of fully typed word 的本地化字符串。 + /// Looks up a localized string similar to Only add new line on enter after end of fully typed word. /// public static string Enter_key_only { get { @@ -241,7 +241,7 @@ public static string Enter_key_only { } /// - /// 查找类似 Enter key behavior 的本地化字符串。 + /// Looks up a localized string similar to Enter key behavior. /// public static string Enter_Key_Rule { get { @@ -250,7 +250,7 @@ public static string Enter_Key_Rule { } /// - /// 查找类似 Find References Performance Options 的本地化字符串。 + /// Looks up a localized string similar to Find References Performance Options. /// public static string Find_References_Performance { get { @@ -259,7 +259,7 @@ public static string Find_References_Performance { } /// - /// 查找类似 Re-format indentation on paste (Experimental) 的本地化字符串。 + /// Looks up a localized string similar to Re-format indentation on paste (Experimental). /// public static string Format_on_paste { get { @@ -268,7 +268,7 @@ public static string Format_on_paste { } /// - /// 查找类似 Formatting 的本地化字符串。 + /// Looks up a localized string similar to Formatting. /// public static string Formatting { get { @@ -277,7 +277,7 @@ public static string Formatting { } /// - /// 查找类似 Inline Hints 的本地化字符串。 + /// Looks up a localized string similar to Inline Hints. /// public static string Inline_Hints { get { @@ -286,7 +286,7 @@ public static string Inline_Hints { } /// - /// 查找类似 IntelliSense Performance Options 的本地化字符串。 + /// Looks up a localized string similar to IntelliSense Performance Options. /// public static string IntelliSense_Performance { get { @@ -295,7 +295,7 @@ public static string IntelliSense_Performance { } /// - /// 查找类似 Keep all background intermediate resolutions (increases memory usage) 的本地化字符串。 + /// Looks up a localized string similar to Keep all background intermediate resolutions (increases memory usage). /// public static string Keep_All_Background_Resolutions { get { @@ -304,7 +304,7 @@ public static string Keep_All_Background_Resolutions { } /// - /// 查找类似 Keep all background symbol uses (increases memory usage) 的本地化字符串。 + /// Looks up a localized string similar to Keep all background symbol uses (increases memory usage). /// public static string Keep_All_Background_Symbol_Uses { get { @@ -313,7 +313,7 @@ public static string Keep_All_Background_Symbol_Uses { } /// - /// 查找类似 Performance 的本地化字符串。 + /// Looks up a localized string similar to Performance. /// public static string Language_Service_Performance { get { @@ -322,7 +322,7 @@ public static string Language_Service_Performance { } /// - /// 查找类似 Language service settings (advanced) 的本地化字符串。 + /// Looks up a localized string similar to Language service settings (advanced). /// public static string Language_Service_Settings { get { @@ -331,7 +331,7 @@ public static string Language_Service_Settings { } /// - /// 查找类似 Live Buffers 的本地化字符串。 + /// Looks up a localized string similar to Live Buffers. /// public static string LiveBuffers { get { @@ -340,7 +340,7 @@ public static string LiveBuffers { } /// - /// 查找类似 Navigation links 的本地化字符串。 + /// Looks up a localized string similar to Navigation links. /// public static string Navigation_links { get { @@ -349,7 +349,7 @@ public static string Navigation_links { } /// - /// 查找类似 Outlining 的本地化字符串。 + /// Looks up a localized string similar to Outlining. /// public static string Outlining { get { @@ -358,7 +358,7 @@ public static string Outlining { } /// - /// 查找类似 Parallelization (requires restart) 的本地化字符串。 + /// Looks up a localized string similar to Parallelization (requires restart). /// public static string Parallelization { get { @@ -367,7 +367,7 @@ public static string Parallelization { } /// - /// 查找类似 Preferred description width in characters 的本地化字符串。 + /// Looks up a localized string similar to Preferred description width in characters. /// public static string Preferred_description_width_in_characters { get { @@ -376,7 +376,7 @@ public static string Preferred_description_width_in_characters { } /// - /// 查找类似 F# Project and Caching Performance Options 的本地化字符串。 + /// Looks up a localized string similar to F# Project and Caching Performance Options. /// public static string Project_Performance { get { @@ -385,7 +385,7 @@ public static string Project_Performance { } /// - /// 查找类似 Remove unnecessary parentheses (experimental, might affect typing performance) 的本地化字符串。 + /// Looks up a localized string similar to Remove unnecessary parentheses (experimental, might affect typing performance). /// public static string Remove_parens_code_fix { get { @@ -394,7 +394,7 @@ public static string Remove_parens_code_fix { } /// - /// 查找类似 Send additional performance telemetry 的本地化字符串。 + /// Looks up a localized string similar to Send additional performance telemetry. /// public static string Send_Additional_Telemetry { get { @@ -403,7 +403,7 @@ public static string Send_Additional_Telemetry { } /// - /// 查找类似 Show s_ymbols in unopened namespaces 的本地化字符串。 + /// Looks up a localized string similar to Show s_ymbols in unopened namespaces. /// public static string Show_all_symbols { get { @@ -412,7 +412,7 @@ public static string Show_all_symbols { } /// - /// 查找类似 Show completion list after a character is _deleted 的本地化字符串。 + /// Looks up a localized string similar to Show completion list after a character is _deleted. /// public static string Show_completion_list_after_a_character_is_deleted { get { @@ -421,7 +421,7 @@ public static string Show_completion_list_after_a_character_is_deleted { } /// - /// 查找类似 _Show completion list after a character is typed 的本地化字符串。 + /// Looks up a localized string similar to _Show completion list after a character is typed. /// public static string Show_completion_list_after_a_character_is_typed { get { @@ -430,7 +430,7 @@ public static string Show_completion_list_after_a_character_is_typed { } /// - /// 查找类似 Show structure guidelines for F# code 的本地化字符串。 + /// Looks up a localized string similar to Show structure guidelines for F# code. /// public static string Show_guides { get { @@ -439,7 +439,7 @@ public static string Show_guides { } /// - /// 查找类似 Display inline parameter name hints (preview) 的本地化字符串。 + /// Looks up a localized string similar to Display inline parameter name hints (preview). /// public static string Show_Inline_Parameter_Name_Hints { get { @@ -448,7 +448,7 @@ public static string Show_Inline_Parameter_Name_Hints { } /// - /// 查找类似 Display inline type hints (preview) 的本地化字符串。 + /// Looks up a localized string similar to Display inline type hints (preview). /// public static string Show_Inline_Type_Hints { get { @@ -457,7 +457,7 @@ public static string Show_Inline_Type_Hints { } /// - /// 查找类似 S_how navigation links as 的本地化字符串。 + /// Looks up a localized string similar to S_how navigation links as. /// public static string Show_navigation_links_as { get { @@ -466,7 +466,7 @@ public static string Show_navigation_links_as { } /// - /// 查找类似 Show outlining and collapsible nodes for F# code 的本地化字符串。 + /// Looks up a localized string similar to Show outlining and collapsible nodes for F# code. /// public static string Show_Outlining { get { @@ -475,7 +475,7 @@ public static string Show_Outlining { } /// - /// 查找类似 Show remarks in Quick Info 的本地化字符串。 + /// Looks up a localized string similar to Show remarks in Quick Info. /// public static string Show_remarks_in_Quick_Info { get { @@ -484,7 +484,7 @@ public static string Show_remarks_in_Quick_Info { } /// - /// 查找类似 Display return type hints (preview) 的本地化字符串。 + /// Looks up a localized string similar to Display return type hints (preview). /// public static string Show_Return_Type_Hints { get { @@ -493,7 +493,7 @@ public static string Show_Return_Type_Hints { } /// - /// 查找类似 Simplify names (remove unnecessary qualifiers) 的本地化字符串。 + /// Looks up a localized string similar to Simplify names (remove unnecessary qualifiers). /// public static string Simplify_name_code_fix { get { @@ -502,7 +502,7 @@ public static string Simplify_name_code_fix { } /// - /// 查找类似 _Solid underline 的本地化字符串。 + /// Looks up a localized string similar to _Solid underline. /// public static string Solid_underline { get { @@ -511,7 +511,7 @@ public static string Solid_underline { } /// - /// 查找类似 Suggest names for unresolved identifiers 的本地化字符串。 + /// Looks up a localized string similar to Suggest names for unresolved identifiers. /// public static string Suggest_names_for_errors_code_fix { get { @@ -520,7 +520,7 @@ public static string Suggest_names_for_errors_code_fix { } /// - /// 查找类似 Text hover 的本地化字符串。 + /// Looks up a localized string similar to Text hover. /// public static string Text_hover { get { @@ -529,7 +529,7 @@ public static string Text_hover { } /// - /// 查找类似 Time until stale results are used (in milliseconds) 的本地化字符串。 + /// Looks up a localized string similar to Time until stale results are used (in milliseconds). /// public static string Time_until_stale_completion { get { @@ -538,7 +538,7 @@ public static string Time_until_stale_completion { } /// - /// 查找类似 In-memory cross-project references store project-level data in memory to allow IDE features to work across projects. 的本地化字符串。 + /// Looks up a localized string similar to In-memory cross-project references store project-level data in memory to allow IDE features to work across projects.. /// public static string Tooltip_in_memory_cross_project_references { get { @@ -547,7 +547,7 @@ public static string Tooltip_in_memory_cross_project_references { } /// - /// 查找类似 Format signature to the given width by adding line breaks conforming with F# syntax rules. 的本地化字符串。 + /// Looks up a localized string similar to Format signature to the given width by adding line breaks conforming with F# syntax rules. . /// public static string Tooltip_preferred_description_width_in_characters { get { @@ -556,7 +556,70 @@ public static string Tooltip_preferred_description_width_in_characters { } /// - /// 查找类似 Analyze and suggest fixes for unused values 的本地化字符串。 + /// Looks up a localized string similar to Transparent Compiler Cache Factor. + /// + public static string Transparent_Compiler_Cache_Factor { + get { + return ResourceManager.GetString("Transparent_Compiler_Cache_Factor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Higher number means more memory will be used for caching. Changing the value wipes cache.. + /// + public static string Transparent_Compiler_Cache_Factor_Tooltip { + get { + return ResourceManager.GetString("Transparent_Compiler_Cache_Factor_Tooltip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new project snapshots from existing ones. + /// + public static string Transparent_Compiler_Snapshot_Reuse { + get { + return ResourceManager.GetString("Transparent_Compiler_Snapshot_Reuse", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Transparent Compiler (experimental). + /// + public static string TransparentCompiler { + get { + return ResourceManager.GetString("TransparentCompiler", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results.. + /// + public static string TransparentCompiler_Discalimer1 { + get { + return ResourceManager.GetString("TransparentCompiler_Discalimer1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use at your own risk!. + /// + public static string TransparentCompiler_Discalimer2 { + get { + return ResourceManager.GetString("TransparentCompiler_Discalimer2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to By checking this you also opt-in for additional performance telemetry. + /// + public static string TransparentCompiler_Discalimer3 { + get { + return ResourceManager.GetString("TransparentCompiler_Discalimer3", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Analyze and suggest fixes for unused values. /// public static string Unused_declaration_code_fix { get { @@ -565,7 +628,7 @@ public static string Unused_declaration_code_fix { } /// - /// 查找类似 Remove unused open statements 的本地化字符串。 + /// Looks up a localized string similar to Remove unused open statements. /// public static string Unused_opens_code_fix { get { @@ -574,12 +637,21 @@ public static string Unused_opens_code_fix { } /// - /// 查找类似 Cache parsing results (experimental) 的本地化字符串。 + /// Looks up a localized string similar to Cache parsing results (experimental). /// public static string Use_syntax_tree_cache { get { return ResourceManager.GetString("Use_syntax_tree_cache", resourceCulture); } } + + /// + /// Looks up a localized string similar to Use Transparent Compiler (restart required). + /// + public static string Use_Transparent_Compiler { + get { + return ResourceManager.GetString("Use_Transparent_Compiler", resourceCulture); + } + } } } diff --git a/vsintegration/src/FSharp.UIResources/Strings.resx b/vsintegration/src/FSharp.UIResources/Strings.resx index 7feb60ef6ae..58821d8b8be 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.resx +++ b/vsintegration/src/FSharp.UIResources/Strings.resx @@ -279,16 +279,40 @@ Display return type hints (preview) + + Transparent Compiler (experimental) + + + Use Transparent Compiler (restart required) + Keep analyzing the entire solution for diagnostics as a low priority background task (requires restart) Background analysis + + Transparent Compiler Cache Factor + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Remove unnecessary parentheses (experimental, might affect typing performance) Show remarks in Quick Info + + Create new project snapshots from existing ones + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + Use at your own risk! + + + By checking this you also opt-in for additional performance telemetry + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf index 8b6d86d1061..c0072448173 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf @@ -202,6 +202,41 @@ Umožňuje formátovat podpis na danou šířku přidáním konců řádků odpovídajících pravidlům syntaxe F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Odebrat nepoužívané otevřené výkazy @@ -287,6 +322,11 @@ Navrhovat názvy pro nerozpoznané identifikátory + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Výsledky analýzy mezipaměti (experimentální) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf index 786ef654691..8afb1d8e999 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf @@ -202,6 +202,41 @@ Formatieren Sie die Signatur in der angegebenen Breite, indem Sie Zeilenumbrüche hinzufügen, die F#-Syntaxregeln entsprechen. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Nicht verwendete "open"-Anweisungen entfernen @@ -287,6 +322,11 @@ Namen für nicht aufgelöste Bezeichner vorschlagen + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Cacheanalyseergebnisse (experimentell) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf index 64c0eaadbe2..eb0289c938c 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf @@ -202,6 +202,41 @@ Da formato a la firma al ancho dado agregando saltos de línea conforme a las reglas de sintaxis de F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Quitar instrucciones open no usadas @@ -287,6 +322,11 @@ Sugerir nombres para los identificadores no resueltos + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Resultados del análisis de la caché (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf index 985e5ccf7b2..09430b644cc 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf @@ -202,6 +202,41 @@ Formatez la signature à la largeur donnée en ajoutant des sauts de ligne conformes aux règles de syntaxe F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Supprimer les instructions open inutilisées @@ -287,6 +322,11 @@ Suggérer des noms pour les identificateurs non résolus + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Résultats de l'analyse du cache (expérimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf index 3c03c240b4d..640894aac42 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf @@ -202,6 +202,41 @@ Consente di formattare la firma in base alla larghezza specificata aggiungendo interruzioni di riga conformi alle regole di sintassi F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Rimuovi istruzioni OPEN inutilizzate @@ -287,6 +322,11 @@ Suggerisci nomi per gli identificatori non risolti + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Risultati dell'analisi della cache (sperimentale) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf index 5518aa00bee..7267c7b7061 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf @@ -202,6 +202,41 @@ F# 構文規則に準拠した改行を追加して、署名を指定された幅に書式設定します。 + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements 未使用の Open ステートメントを削除する @@ -287,6 +322,11 @@ 未解決の識別子の名前を提案します + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) キャッシュ解析の結果 (試験段階) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf index 0af709880db..cfb0da2f9b4 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf @@ -202,6 +202,41 @@ F# 구문 규칙에 맞는 줄 바꿈을 추가하여 지정된 너비에 시그니처의 서식을 지정합니다. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements 사용되지 않는 open 문 제거 @@ -287,6 +322,11 @@ 확인되지 않은 식별자의 이름 제안 + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) 캐시 구문 분석 결과(실험적) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf index 9cf39c5e71a..4fb4ff03b40 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf @@ -202,6 +202,41 @@ Sformatuj sygnaturę na daną szerokość, dodając podziały wierszy zgodne z regułami składni języka F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Usuń nieużywane otwarte instrukcje @@ -287,6 +322,11 @@ Sugeruj nazwy w przypadku nierozpoznanych identyfikatorów + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Wyniki analizy pamięci podręcznej (eksperymentalne) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf index 2b8b3afc95c..f2778bcbd8b 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf @@ -202,6 +202,41 @@ Formate a assinatura para a largura fornecida adicionando quebras de linha em conformidade com as regras de sintaxe F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Remover instruções abertas não usadas @@ -287,6 +322,11 @@ Sugerir nomes para identificadores não resolvidos + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Resultados da análise de cache (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf index 21644c83141..9b99816b43c 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf @@ -202,6 +202,41 @@ Форматирование подписи до заданной ширины путем добавления разрывов строк, соответствующих правилам синтаксиса F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Удалить неиспользуемые открытые операторы @@ -287,6 +322,11 @@ Предлагать имена для неразрешенных идентификаторов + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Результаты анализа кэша (экспериментальная функция) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf index dd8be3331e4..e6afdd2e2b1 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf @@ -202,6 +202,41 @@ F# söz dizimi kurallarına uyan satır sonları ekleyerek imzayı belirtilen genişliğe biçimlendirin. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Kullanılmayan açık deyimleri kaldır @@ -287,6 +322,11 @@ Çözümlenmemiş tanımlayıcılar için ad öner + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Ayrıştırma sonuçlarını önbelleğe al (deneysel) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf index 57a18b93541..9bd919f4b51 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf @@ -202,6 +202,41 @@ 通过添加符合 F# 语法规则的换行符,将签名设置为给定宽度的格式。 + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements 删除未使用的 open 语句 @@ -287,6 +322,11 @@ 为未解析标识符建议名称 + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) 缓存分析结果(实验性) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf index 6fcf0c141a4..d537c05ffd2 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf @@ -202,6 +202,41 @@ 透過新增符合 F# 語法規則的分行符號,將簽章格式設定為指定寬度。 + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + + + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements 移除未使用的 open 陳述式 @@ -287,6 +322,11 @@ 為未解析的識別碼建議名稱 + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) 快取剖析結果 (實驗性) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveUnnecessaryParenthesesTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveUnnecessaryParenthesesTests.fs index 69d6456e378..cd3bbdd2bfc 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveUnnecessaryParenthesesTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/RemoveUnnecessaryParenthesesTests.fs @@ -800,6 +800,24 @@ in x "$\"{(id 3)}\"", "$\"{id 3}\"" "$\"{(x)}\"", "$\"{x}\"" + "$\"{(if true then 1 else 0)}\"", "$\"{if true then 1 else 0}\"" + "$\"{(if true then 1 else 0):N0}\"", "$\"{(if true then 1 else 0):N0}\"" + "$\"{(if true then 1 else 0),-3}\"", "$\"{(if true then 1 else 0),-3}\"" + "$\"{(match () with () -> 1):N0}\"", "$\"{(match () with () -> 1):N0}\"" + "$\"{(match () with () -> 1),-3}\"", "$\"{(match () with () -> 1),-3}\"" + "$\"{(try () with _ -> 1):N0}\"", "$\"{(try () with _ -> 1):N0}\"" + "$\"{(try () with _ -> 1),-3}\"", "$\"{(try () with _ -> 1),-3}\"" + "$\"{(try 1 finally ()):N0}\"", "$\"{(try 1 finally ()):N0}\"" + "$\"{(try 1 finally ()),-3}\"", "$\"{(try 1 finally ()),-3}\"" + "$\"{(let x = 3 in x):N0}\"", "$\"{(let x = 3 in x):N0}\"" + "$\"{(let x = 3 in x),-3}\"", "$\"{(let x = 3 in x),-3}\"" + "$\"{(do (); 3):N0}\"", "$\"{(do (); 3):N0}\"" + "$\"{(do (); 3),-3}\"", "$\"{(do (); 3),-3}\"" + "$\"{(x <- 3):N0}\"", "$\"{(x <- 3):N0}\"" + "$\"{(x <- 3),-3}\"", "$\"{(x <- 3),-3}\"" + "$\"{(1, 2):N0}\"", "$\"{(1, 2):N0}\"" + "$\"{(1, 2),-3}\"", "$\"{(1, 2),-3}\"" + """ $"{(3 + LanguagePrimitives.GenericZero):N0}" """, diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index e94a5f69436..551b804bc37 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -423,7 +423,7 @@ xVal**y Assert.True(triggered, "Completion should trigger after typing an identifier that follows a mathematical operation") [] - let ShouldTriggerCompletionAtStartOfFileWithInsertion = + let ShouldTriggerCompletionAtStartOfFileWithInsertion () = let fileContents = """ l""" @@ -888,7 +888,7 @@ type T() = VerifyNoCompletionList(fileContents, "member this.M(p") [] - let ``Completion list on abstract member type signature contains modules and types but not keywords or functions`` = + let ``Completion list on abstract member type signature contains modules and types but not keywords or functions`` () = let fileContents = """ type Interface = diff --git a/vsintegration/tests/FSharp.Editor.Tests/SignatureHelpProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/SignatureHelpProviderTests.fs index d4065f8a2b3..af66c01ed69 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/SignatureHelpProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/SignatureHelpProviderTests.fs @@ -157,13 +157,18 @@ module SignatureHelpProvider = |> CancellableTask.runSynchronouslyWithoutCancellation let adjustedColumnInSource = - let rec loop ch pos = - if Char.IsWhiteSpace(ch) then - loop sourceText.[pos - 1] (pos - 1) - else + let rec loop pos = + if pos = 0 then pos + else + let nextPos = pos - 1 - loop sourceText.[caretPosition - 1] (caretPosition - 1) + if not (Char.IsWhiteSpace sourceText[nextPos]) then + pos + else + loop nextPos + + loop (caretPosition - 1) let sigHelp = FSharpSignatureHelpProvider.ProvideParametersAsyncAux( @@ -552,6 +557,18 @@ M.f let marker = "List.map " assertSignatureHelpForFunctionApplication fileContents marker 1 0 "mapping" + [] + let ``function application in middle of pipeline with two additional arguments`` () = + let fileContents = + """ +[1..10] +|> List.fold (fun acc _ -> acc) +|> List.filter (fun x -> x > 3) + """ + + let marker = "List.fold (fun acc _ -> acc) " + assertSignatureHelpForFunctionApplication fileContents marker 2 1 "state" + [] let ``function application with function as parameter`` () = let fileContents =