diff --git a/fcs/README.md b/fcs/README.md index cb2d32dbf89..0b4b45830bd 100644 --- a/fcs/README.md +++ b/fcs/README.md @@ -60,9 +60,9 @@ which does things like: Yu can push the packages if you have permissions, either automatically using ``build Release`` or manually set APIKEY=... - .nuget\nuget.exe push Release\FSharp.Compiler.Service.14.0.2.nupkg %APIKEY% -Source https://nuget.org - .nuget\nuget.exe push Release\FSharp.Compiler.Service.MSBuild.v12.14.0.2.nupkg %APIKEY% -Source https://nuget.org - .nuget\nuget.exe push Release\FSharp.Compiler.Service.ProjectCracker.14.0.2.nupkg %APIKEY% -Source https://nuget.org + .nuget\nuget.exe push Release\FSharp.Compiler.Service.16.0.1.nupkg %APIKEY% -Source https://nuget.org + .nuget\nuget.exe push Release\FSharp.Compiler.Service.MSBuild.v12.16.0.1.nupkg %APIKEY% -Source https://nuget.org + .nuget\nuget.exe push Release\FSharp.Compiler.Service.ProjectCracker.16.0.1.nupkg %APIKEY% -Source https://nuget.org ### Use of Paket and FAKE diff --git a/fcs/RELEASE_NOTES.md b/fcs/RELEASE_NOTES.md index 18f26ff87ef..cca2fc17f80 100644 --- a/fcs/RELEASE_NOTES.md +++ b/fcs/RELEASE_NOTES.md @@ -1,4 +1,10 @@ -#### 14.0.2 +#### 16.0.1 + * FSharpChecker provides non-reactor ParseFile instead of ParseFileInProject + * Add FSharpParsingOptions, GetParsingOptionsFromProjectOptions, GetParsingOptionsFromCommandLine + +#### 15.0.1 + * Integrate latest changes from visualfsharp + * Add implementation file contents to CheckFileResults * Fix non-public API in .NET Standard 1.6 version #### 14.0.1 diff --git a/fcs/docsrc/content/caches.fsx b/fcs/docsrc/content/caches.fsx index cfed3248ec5..e63857c5dac 100644 --- a/fcs/docsrc/content/caches.fsx +++ b/fcs/docsrc/content/caches.fsx @@ -21,19 +21,14 @@ Each FSharpChecker object maintains a set of caches. These are * ``braceMatchCache`` - an MRU cache of size ``braceMatchCacheSize`` (default = 5) keeping the results of calls to MatchBraces, keyed by filename, source and project options. -* ``parseFileInProjectCache`` - an MRU cache of size ``parseFileInProjectCacheSize`` (default = 2) keeping the results of ParseFileInProject, +* ``parseFileCache`` - an MRU cache of size ``parseFileCacheSize`` (default = 2) keeping the results of ParseFile, keyed by filename, source and project options. -* ``parseAndCheckFileInProjectCache`` - an MRU cache of size ``incrementalTypeCheckCacheSize`` (default = 5) keeping the results of +* ``checkFileInProjectCache`` - an MRU cache of size ``incrementalTypeCheckCacheSize`` (default = 5) keeping the results of ParseAndCheckFileInProject, CheckFileInProject and/or CheckFileInProjectIfReady. This is keyed by filename, file source and project options. The results held in this cache are only returned if they would reflect an accurate parse and check of the file. -* ``parseAndCheckFileInProjectCachePossiblyStale`` - a somewhat peculiar MRU cache of size ``incrementalTypeCheckCacheSize`` (default = 5) - keeping the results of ParseAndCheckFileInProject, CheckFileInProject and CheckFileInProjectIfReady, - keyed by filename and project options. This cache is accessed by TryGetRecentTypeCheckResultsForFile. Because the results - are accessed regardless of the content of the file, the checking results returned may be "stale". - * ``getToolTipTextCache`` - an aged lookup cache of strong size ``getToolTipTextSize`` (default = 5) computing the results of GetToolTipText. * ``ilModuleReaderCache`` - an aged lookup of weak references to "readers" for references .NET binaries. Because these diff --git a/fcs/docsrc/content/editor.fsx b/fcs/docsrc/content/editor.fsx index ccd61e922d2..d6b19fd0e6f 100644 --- a/fcs/docsrc/content/editor.fsx +++ b/fcs/docsrc/content/editor.fsx @@ -56,16 +56,19 @@ let projOptions = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously +let parsingOptions, _errors = checker.GetParsingOptionsFromProjectOptions(projOptions) + (** To perform type checking, we first need to parse the input using -`ParseFileInProject`, which gives us access to the [untyped AST](untypedtree.html). However, +`ParseFile`, which gives us access to the [untyped AST](untypedtree.html). However, then we need to call `CheckFileInProject` to perform the full type checking. This function also requires the result of `ParseFileInProject`, so the two functions are often called together. *) // Perform parsing + let parseFileResults = - checker.ParseFileInProject(file, input, projOptions) + checker.ParseFile(file, input, parsingOptions) |> Async.RunSynchronously (** Before we look at the interesting operations provided by `TypeCheckResults`, we diff --git a/fcs/docsrc/content/ja/editor.fsx b/fcs/docsrc/content/ja/editor.fsx index 9e4ac05b3ca..014be2e86c5 100644 --- a/fcs/docsrc/content/ja/editor.fsx +++ b/fcs/docsrc/content/ja/editor.fsx @@ -58,20 +58,22 @@ let file = "/home/user/Test.fsx" let projOptions = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously +let parsingOptions, _errors = checker.GetParsingOptionsFromProjectOptions(projOptions) + (** -型チェックを実行するには、まず `ParseFileInProject` を使って +型チェックを実行するには、まず `ParseFile` を使って 入力値をパースする必要があります。 このメソッドを使うと [型無しAST](untypedtree.html) にアクセスできるようになります。 しかし今回は完全な型チェックを実行するため、続けて `CheckFileInProject` を呼び出す必要があります。 -このメソッドは `ParseFileInProject` の結果も必要とするため、 +このメソッドは `ParseFile` の結果も必要とするため、 たいていの場合にはこれら2つのメソッドをセットで呼び出すことになります。 *) // パースを実行 let parseFileResults = - checker.ParseFileInProject(file, input, projOptions) + checker.ParseFile(file, input, parsingOptions) |> Async.RunSynchronously (** `TypeCheckResults` に備えられた興味深い機能の紹介に入る前に、 diff --git a/fcs/docsrc/content/ja/untypedtree.fsx b/fcs/docsrc/content/ja/untypedtree.fsx index f34f9c6b435..df6b6f4deb8 100644 --- a/fcs/docsrc/content/ja/untypedtree.fsx +++ b/fcs/docsrc/content/ja/untypedtree.fsx @@ -73,9 +73,11 @@ let getUntypedTree (file, input) = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously + let parsingOptions, _errors = checker.GetParsingOptionsFromProjectOptions(projOptions) + // コンパイラの第1フェーズを実行する let untypedRes = - checker.ParseFileInProject(file, input, projectOptions) + checker.ParseFile(file, input, parsingOptions) |> Async.RunSynchronously match untypedRes.ParseTree with diff --git a/fcs/docsrc/content/untypedtree.fsx b/fcs/docsrc/content/untypedtree.fsx index 600e87fb04e..d67a1ef71bf 100644 --- a/fcs/docsrc/content/untypedtree.fsx +++ b/fcs/docsrc/content/untypedtree.fsx @@ -49,7 +49,7 @@ To get the AST, we define a function that takes file name and the source code (the file is only used for location information and does not have to exist). We first need to get "interactive checker options" which represents the context. For simple tasks, you can use `GetProjectOptionsFromScriptRoot` which infers -the context for a script file. Then we use the `ParseFileInProject` method and +the context for a script file. Then we use the `ParseFile` method and return the `ParseTree` property: *) @@ -60,9 +60,11 @@ let getUntypedTree (file, input) = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously + let parsingOptions, _errors = checker.GetParsingOptionsFromProjectOptions(projOptions) + // Run the first phase (untyped parsing) of the compiler let parseFileResults = - checker.ParseFileInProject(file, input, projOptions) + checker.ParseFile(file, input, parsingOptions) |> Async.RunSynchronously match parseFileResults.ParseTree with diff --git a/fcs/fcs.props b/fcs/fcs.props index 2b897d37f3c..7a6e7c40585 100644 --- a/fcs/fcs.props +++ b/fcs/fcs.props @@ -3,7 +3,7 @@ - 14.0.2 + 16.0.1 $(FSharpSourcesRoot)\..\packages\FSharp.Compiler.Tools.4.1.23\tools diff --git a/fcs/nuget/FSharp.Compiler.Service.MSBuild.v12.nuspec b/fcs/nuget/FSharp.Compiler.Service.MSBuild.v12.nuspec index e2fc6d2fd71..ad905b35c0f 100644 --- a/fcs/nuget/FSharp.Compiler.Service.MSBuild.v12.nuspec +++ b/fcs/nuget/FSharp.Compiler.Service.MSBuild.v12.nuspec @@ -8,7 +8,7 @@ en-US false - 14.0.2 + 16.0.1 Microsoft Corporation and F# community contributors https://github.com/fsharp/FSharp.Compiler.Service/blob/master/LICENSE https://github.com/fsharp/FSharp.Compiler.Service diff --git a/fcs/nuget/FSharp.Compiler.Service.ProjectCracker.nuspec b/fcs/nuget/FSharp.Compiler.Service.ProjectCracker.nuspec index f9275ce2328..d8501e86006 100644 --- a/fcs/nuget/FSharp.Compiler.Service.ProjectCracker.nuspec +++ b/fcs/nuget/FSharp.Compiler.Service.ProjectCracker.nuspec @@ -10,7 +10,7 @@ en-US false - 14.0.2 + 16.0.1 Microsoft Corporation and F# community contributors https://github.com/fsharp/FSharp.Compiler.Service/blob/master/LICENSE https://github.com/fsharp/FSharp.Compiler.Service diff --git a/fcs/nuget/FSharp.Compiler.Service.nuspec b/fcs/nuget/FSharp.Compiler.Service.nuspec index bfb19e2661e..0df07c0240a 100644 --- a/fcs/nuget/FSharp.Compiler.Service.nuspec +++ b/fcs/nuget/FSharp.Compiler.Service.nuspec @@ -10,7 +10,7 @@ en-US false - 14.0.2 + 16.0.1 Microsoft Corporation and F# community contributors https://github.com/fsharp/FSharp.Compiler.Service/blob/master/LICENSE https://github.com/fsharp/FSharp.Compiler.Service diff --git a/fcs/paket.lock b/fcs/paket.lock new file mode 100644 index 00000000000..2e454bee770 --- /dev/null +++ b/fcs/paket.lock @@ -0,0 +1,10 @@ +RESTRICTION: == net45 +NUGET + remote: https://www.nuget.org/api/v2 + FAKE (4.63.2) + FSharp.Compiler.Service (2.0.0.6) + FSharp.Formatting (2.14.4) + FSharp.Compiler.Service (2.0.0.6) + FSharpVSPowerTools.Core (>= 2.3 < 2.4) + FSharpVSPowerTools.Core (2.3) + FSharp.Compiler.Service (>= 2.0.0.3) diff --git a/fcs/samples/EditorService/Program.fs b/fcs/samples/EditorService/Program.fs index 20bf20cda7a..40052ddce7e 100644 --- a/fcs/samples/EditorService/Program.fs +++ b/fcs/samples/EditorService/Program.fs @@ -8,7 +8,8 @@ let checker = FSharpChecker.Create() let parseWithTypeInfo (file, input) = let checkOptions, _errors = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously - let untypedRes = checker.ParseFileInProject(file, input, checkOptions) |> Async.RunSynchronously + let parsingOptions, _errors = checker.GetParsingOptionsFromProjectOptions(checkOptions) + let untypedRes = checker.ParseFile(file, input, parsingOptions) |> Async.RunSynchronously match checker.CheckFileInProject(untypedRes, file, 0, input, checkOptions) |> Async.RunSynchronously with | FSharpCheckFileAnswer.Succeeded(res) -> untypedRes, res diff --git a/fcs/samples/FscExe/App.config b/fcs/samples/FscExe/App.config index a895438e297..ab704c8783e 100644 --- a/fcs/samples/FscExe/App.config +++ b/fcs/samples/FscExe/App.config @@ -1,9 +1,8 @@ - + - - + diff --git a/fcs/samples/FsiExe/App.config b/fcs/samples/FsiExe/App.config index ac481ec64a8..2d8be792223 100644 --- a/fcs/samples/FsiExe/App.config +++ b/fcs/samples/FsiExe/App.config @@ -1,9 +1,8 @@ - + - - + diff --git a/fcs/samples/InteractiveService/App.config b/fcs/samples/InteractiveService/App.config index 423a5517bd5..c485ef2b042 100644 --- a/fcs/samples/InteractiveService/App.config +++ b/fcs/samples/InteractiveService/App.config @@ -3,8 +3,5 @@ - - - - + \ No newline at end of file diff --git a/fcs/samples/Tokenizer/App.config b/fcs/samples/Tokenizer/App.config index 423a5517bd5..c485ef2b042 100644 --- a/fcs/samples/Tokenizer/App.config +++ b/fcs/samples/Tokenizer/App.config @@ -3,8 +3,5 @@ - - - - + \ No newline at end of file diff --git a/fcs/samples/UntypedTree/App.config b/fcs/samples/UntypedTree/App.config index 423a5517bd5..c485ef2b042 100644 --- a/fcs/samples/UntypedTree/App.config +++ b/fcs/samples/UntypedTree/App.config @@ -3,8 +3,5 @@ - - - - + \ No newline at end of file diff --git a/fcs/samples/UntypedTree/Program.fs b/fcs/samples/UntypedTree/Program.fs index 58cc9a236d1..3334b093bad 100644 --- a/fcs/samples/UntypedTree/Program.fs +++ b/fcs/samples/UntypedTree/Program.fs @@ -10,11 +10,8 @@ let checker = FSharpChecker.Create() // Get untyped tree for a specified input let getUntypedTree (file, input) = - // Get compiler options for a single script file - let checkOptions, _diagnostics = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously - // Run the first phase (untyped parsing) of the compiler - - let untypedRes = checker.ParseFileInProject(file, input, checkOptions) |> Async.RunSynchronously + let parsingOptions = { FSharpParsingOptions.Default with SourceFiles = [| file |] } + let untypedRes = checker.ParseFile(file, input, parsingOptions) |> Async.RunSynchronously match untypedRes.ParseTree with | Some tree -> tree | None -> failwith "Something went wrong during parsing!" diff --git a/src/assemblyinfo/assemblyinfo.FSharp.Compiler.Private.dll.fs b/src/assemblyinfo/assemblyinfo.FSharp.Compiler.Private.dll.fs index b658d62627a..c74e002c1b9 100644 --- a/src/assemblyinfo/assemblyinfo.FSharp.Compiler.Private.dll.fs +++ b/src/assemblyinfo/assemblyinfo.FSharp.Compiler.Private.dll.fs @@ -46,6 +46,7 @@ open System.Runtime.InteropServices [] [] [] +[] #endif #if STRONG_NAME_FSHARP_COMPILER_WITH_TEST_KEY [] diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index 293ae17cbf4..cc288556e1f 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -4,6 +4,7 @@ module internal Microsoft.FSharp.Compiler.CompileOps open System +open System.Diagnostics open System.Text open System.IO open System.Collections.Generic @@ -1076,9 +1077,9 @@ let OutputPhasedErrorR (os:StringBuilder) (err:PhasedDiagnostic) = | Parser.TOKEN_CONST -> getErrorString("Parser.TOKEN.CONST") | Parser.TOKEN_FIXED -> getErrorString("Parser.TOKEN.FIXED") | unknown -> - System.Diagnostics.Debug.Assert(false, "unknown token tag") + Debug.Assert(false, "unknown token tag") let result = sprintf "%+A" unknown - System.Diagnostics.Debug.Assert(false, result) + Debug.Assert(false, result) result match ctxt.CurrentToken with @@ -2201,12 +2202,7 @@ type TcConfigBuilder = mutable useHighEntropyVA : bool mutable inputCodePage: int option mutable embedResources : string list - mutable globalWarnAsError: bool - mutable globalWarnLevel: int - mutable specificWarnOff: int list - mutable specificWarnOn: int list - mutable specificWarnAsError: int list - mutable specificWarnAsWarn : int list + mutable errorSeverityOptions: FSharpErrorSeverityOptions mutable mlCompatibility: bool mutable checkOverflow: bool mutable showReferenceResolutions:bool @@ -2330,55 +2326,47 @@ type TcConfigBuilder = mutable shadowCopyReferences : bool } - static member CreateNew (legacyReferenceResolver, defaultFSharpBinariesDir, optimizeForMemory, implicitIncludeDir, isInteractive, isInvalidationSupported, defaultCopyFSharpCore) = - System.Diagnostics.Debug.Assert(FileSystem.IsPathRootedShim(implicitIncludeDir), sprintf "implicitIncludeDir should be absolute: '%s'" implicitIncludeDir) - if (String.IsNullOrEmpty(defaultFSharpBinariesDir)) then - failwith "Expected a valid defaultFSharpBinariesDir" - { + static member Initial = + { #if COMPILER_SERVICE_ASSUMES_DOTNETCORE_COMPILATION primaryAssembly = PrimaryAssembly.System_Runtime // defaut value, can be overridden using the command line switch #else primaryAssembly = PrimaryAssembly.Mscorlib // defaut value, can be overridden using the command line switch #endif light = None - noFeedback=false - stackReserveSize=None - conditionalCompilationDefines=[] - implicitIncludeDir = implicitIncludeDir + noFeedback = false + stackReserveSize = None + conditionalCompilationDefines = [] + implicitIncludeDir = String.Empty autoResolveOpenDirectivesToDlls = false openBinariesInMemory = false - openDebugInformationForLaterStaticLinking=false - defaultFSharpBinariesDir=defaultFSharpBinariesDir - compilingFslib=false - compilingFslib20=None - compilingFslib40=false - compilingFslibNoBigInt=false - useIncrementalBuilder=false - useFsiAuxLib=false - implicitOpens=[] - includes=[] - resolutionEnvironment=ResolutionEnvironment.EditingOrCompilation false - framework=true - implicitlyResolveAssemblies=true + openDebugInformationForLaterStaticLinking = false + defaultFSharpBinariesDir = String.Empty + compilingFslib = false + compilingFslib20 = None + compilingFslib40 = false + compilingFslibNoBigInt = false + useIncrementalBuilder = false + useFsiAuxLib = false + implicitOpens = [] + includes = [] + resolutionEnvironment = ResolutionEnvironment.EditingOrCompilation false + framework = true + implicitlyResolveAssemblies = true referencedDLLs = [] projectReferences = [] knownUnresolvedReferences = [] loadedSources = [] - globalWarnAsError=false - globalWarnLevel=3 - specificWarnOff=[] - specificWarnOn=[] - specificWarnAsError=[] - specificWarnAsWarn=[] + errorSeverityOptions = FSharpErrorSeverityOptions.Default embedResources = [] - inputCodePage=None - optimizeForMemory=optimizeForMemory + inputCodePage = None + optimizeForMemory = true subsystemVersion = 4, 0 // per spec for 357994 useHighEntropyVA = false - mlCompatibility=false - checkOverflow=false - showReferenceResolutions=false - outputFile=None + mlCompatibility = false + checkOverflow = false + showReferenceResolutions = false + outputFile = None platform = None prefer32Bit = false useSimpleResolution = runningOnMono @@ -2432,8 +2420,8 @@ type TcConfigBuilder = win32manifest = "" includewin32manifest = true linkResources = [] - legacyReferenceResolver = legacyReferenceResolver - showFullPaths =false + legacyReferenceResolver = null + showFullPaths = false errorStyle = ErrorStyle.DefaultErrors utf8output = false @@ -2442,14 +2430,14 @@ type TcConfigBuilder = #if DEBUG showOptimizationData = false #endif - showTerms = false - writeTermsToFiles = false + showTerms = false + writeTermsToFiles = false - doDetuple = false - doTLR = false + doDetuple = false + doTLR = false doFinalSimplify = false - optsOn = false - optSettings = Optimizer.OptimizationSettings.Defaults + optsOn = false + optSettings = Optimizer.OptimizationSettings.Defaults emitTailcalls = true deterministic = false #if PREFERRED_UI_LANG @@ -2457,9 +2445,9 @@ type TcConfigBuilder = #endif lcid = None // See bug 6071 for product banner spec - productNameForBannerText = (FSComp.SR.buildProductName(FSharpEnvironment.FSharpBannerVersion)) - showBanner = true - showTimes = false + productNameForBannerText = FSComp.SR.buildProductName(FSharpEnvironment.FSharpBannerVersion) + showBanner = true + showTimes = false showLoadedAssemblies = false continueAfterParseFailure = false #if EXTENSIONTYPING @@ -2468,17 +2456,32 @@ type TcConfigBuilder = pause = false alwaysCallVirt = true noDebugData = false - isInteractive = isInteractive - isInvalidationSupported = isInvalidationSupported + isInteractive = false + isInvalidationSupported = false sqmSessionGuid = None sqmNumOfSourceFiles = 0 sqmSessionStartedTime = System.DateTime.Now.Ticks emitDebugInfoInQuotations = false exename = None - copyFSharpCore = defaultCopyFSharpCore + copyFSharpCore = false shadowCopyReferences = false } + static member CreateNew(legacyReferenceResolver, defaultFSharpBinariesDir, optimizeForMemory, implicitIncludeDir, + isInteractive, isInvalidationSupported, defaultCopyFSharpCore) = + Debug.Assert(FileSystem.IsPathRootedShim(implicitIncludeDir), sprintf "implicitIncludeDir should be absolute: '%s'" implicitIncludeDir) + if (String.IsNullOrEmpty(defaultFSharpBinariesDir)) then + failwith "Expected a valid defaultFSharpBinariesDir" + { TcConfigBuilder.Initial with + implicitIncludeDir = implicitIncludeDir + defaultFSharpBinariesDir = defaultFSharpBinariesDir + optimizeForMemory = optimizeForMemory + legacyReferenceResolver = legacyReferenceResolver + isInteractive = isInteractive + isInvalidationSupported = isInvalidationSupported + copyFSharpCore = defaultCopyFSharpCore + } + member tcConfigB.ResolveSourceFile(m, nm, pathLoadedFrom) = use unwindBuildPhase = PushThreadBuildPhaseUntilUnwind BuildPhase.Parameter ResolveFileUsingPaths(tcConfigB.includes @ [pathLoadedFrom], m, nm) @@ -2526,7 +2529,8 @@ type TcConfigBuilder = | Some n -> // nowarn:62 turns on mlCompatibility, e.g. shows ML compat items in intellisense menus if n = 62 then tcConfigB.mlCompatibility <- true - tcConfigB.specificWarnOff <- ListSet.insert (=) n tcConfigB.specificWarnOff + tcConfigB.errorSeverityOptions <- + { tcConfigB.errorSeverityOptions with WarnOff = ListSet.insert (=) n tcConfigB.errorSeverityOptions.WarnOff } member tcConfigB.TurnWarningOn(m, s:string) = use unwindBuildPhase = PushThreadBuildPhaseUntilUnwind BuildPhase.Parameter @@ -2535,7 +2539,8 @@ type TcConfigBuilder = | Some n -> // warnon 62 turns on mlCompatibility, e.g. shows ML compat items in intellisense menus if n = 62 then tcConfigB.mlCompatibility <- false - tcConfigB.specificWarnOn <- ListSet.insert (=) n tcConfigB.specificWarnOn + tcConfigB.errorSeverityOptions <- + { tcConfigB.errorSeverityOptions with WarnOn = ListSet.insert (=) n tcConfigB.errorSeverityOptions.WarnOn } member tcConfigB.AddIncludePath (m, path, pathIncludedFrom) = let absolutePath = ComputeMakePathAbsolute pathIncludedFrom path @@ -2833,12 +2838,7 @@ type TcConfig private (data : TcConfigBuilder, validate:bool) = member x.useHighEntropyVA = data.useHighEntropyVA member x.inputCodePage = data.inputCodePage member x.embedResources = data.embedResources - member x.globalWarnAsError = data.globalWarnAsError - member x.globalWarnLevel = data.globalWarnLevel - member x.specificWarnOff = data. specificWarnOff - member x.specificWarnOn = data. specificWarnOn - member x.specificWarnAsError = data.specificWarnAsError - member x.specificWarnAsWarn = data.specificWarnAsWarn + member x.errorSeverityOptions = data.errorSeverityOptions member x.mlCompatibility = data.mlCompatibility member x.checkOverflow = data.checkOverflow member x.showReferenceResolutions = data.showReferenceResolutions @@ -3282,15 +3282,14 @@ type TcConfig private (data : TcConfigBuilder, validate:bool) = member tcConfig.CoreLibraryDllReference() = fslibReference -let ReportWarning (globalWarnLevel : int, specificWarnOff : int list, specificWarnOn : int list) err = - let n = GetDiagnosticNumber err - warningOn err globalWarnLevel specificWarnOn && not (List.contains n specificWarnOff) +let ReportWarning options err = + warningOn err (options.WarnLevel) (options.WarnOn) && not (List.contains (GetDiagnosticNumber err) (options.WarnOff)) -let ReportWarningAsError (globalWarnLevel : int, specificWarnOff : int list, specificWarnOn : int list, specificWarnAsError : int list, specificWarnAsWarn : int list, globalWarnAsError : bool) err = - warningOn err globalWarnLevel specificWarnOn && - not (List.contains (GetDiagnosticNumber err) specificWarnAsWarn) && - ((globalWarnAsError && not (List.contains (GetDiagnosticNumber err) specificWarnOff)) || - List.contains (GetDiagnosticNumber err) specificWarnAsError) +let ReportWarningAsError options err = + warningOn err (options.WarnLevel) (options.WarnOn) && + not (List.contains (GetDiagnosticNumber err) (options.WarnAsWarn)) && + ((options.GlobalWarnAsError && not (List.contains (GetDiagnosticNumber err) options.WarnOff)) || + List.contains (GetDiagnosticNumber err) (options.WarnAsError)) //---------------------------------------------------------------------------- // Scoped #nowarn pragmas diff --git a/src/fsharp/CompileOps.fsi b/src/fsharp/CompileOps.fsi index e1e1429d24a..f18ed457463 100755 --- a/src/fsharp/CompileOps.fsi +++ b/src/fsharp/CompileOps.fsi @@ -268,12 +268,7 @@ type TcConfigBuilder = mutable useHighEntropyVA : bool mutable inputCodePage: int option mutable embedResources : string list - mutable globalWarnAsError: bool - mutable globalWarnLevel: int - mutable specificWarnOff: int list - mutable specificWarnOn: int list - mutable specificWarnAsError: int list - mutable specificWarnAsWarn : int list + mutable errorSeverityOptions: FSharpErrorSeverityOptions mutable mlCompatibility:bool mutable checkOverflow:bool mutable showReferenceResolutions:bool @@ -371,6 +366,8 @@ type TcConfigBuilder = mutable shadowCopyReferences : bool } + static member Initial: TcConfigBuilder + static member CreateNew : legacyReferenceResolver: ReferenceResolver.Resolver * defaultFSharpBinariesDir: string * @@ -423,12 +420,7 @@ type TcConfig = member optimizeForMemory: bool member inputCodePage: int option member embedResources : string list - member globalWarnAsError: bool - member globalWarnLevel: int - member specificWarnOn: int list - member specificWarnOff: int list - member specificWarnAsError: int list - member specificWarnAsWarn : int list + member errorSeverityOptions: FSharpErrorSeverityOptions member mlCompatibility:bool member checkOverflow:bool member showReferenceResolutions:bool @@ -750,10 +742,10 @@ val TypeCheckOneInputAndFinishEventually : -> Eventually<(TcEnv * TopAttribs * TypedImplFile list) * TcState> /// Indicates if we should report a warning -val ReportWarning : globalWarnLevel: int * specificWarnOff: int list * specificWarnOn: int list -> PhasedDiagnostic -> bool +val ReportWarning: FSharpErrorSeverityOptions -> PhasedDiagnostic -> bool /// Indicates if we should report a warning as an error -val ReportWarningAsError : globalWarnLevel: int * specificWarnOff: int list * specificWarnOn: int list * specificWarnAsError: int list * specificWarnAsWarn: int list * globalWarnAsError: bool -> PhasedDiagnostic -> bool +val ReportWarningAsError: FSharpErrorSeverityOptions -> PhasedDiagnostic -> bool //---------------------------------------------------------------------------- // #load closure diff --git a/src/fsharp/CompileOptions.fs b/src/fsharp/CompileOptions.fs index 40928c1cb03..ce26cafde0a 100644 --- a/src/fsharp/CompileOptions.fs +++ b/src/fsharp/CompileOptions.fs @@ -380,9 +380,7 @@ let ParseCompilerOptions (collectOtherArgument : string -> unit, blocks: Compile let rest = attempt specs processArg rest - let result = processArg args - result - + processArg args //---------------------------------------------------------------------------- // Compiler options @@ -550,7 +548,7 @@ let inputFileFlagsFsc tcConfigB = inputFileFlagsBoth tcConfigB // OptionBlock: Errors and warnings //--------------------------------- -let errorsAndWarningsFlags (tcConfigB : TcConfigBuilder) = +let errorsAndWarningsFlags (tcConfigB: TcConfigBuilder) = let trimFS (s:string) = if s.StartsWith("FS", StringComparison.Ordinal) = true then s.Substring(2) else s let trimFStoInt (s:string) = try @@ -559,35 +557,40 @@ let errorsAndWarningsFlags (tcConfigB : TcConfigBuilder) = errorR(Error(FSComp.SR.buildArgInvalidInt(s),rangeCmdArgs)) None [ - CompilerOption("warnaserror", tagNone, OptionSwitch(fun switch -> tcConfigB.globalWarnAsError <- switch <> OptionSwitch.Off), None, - Some (FSComp.SR.optsWarnaserrorPM())); + CompilerOption("warnaserror", tagNone, OptionSwitch(fun switch -> + tcConfigB.errorSeverityOptions <- + { tcConfigB.errorSeverityOptions with + GlobalWarnAsError = switch <> OptionSwitch.Off }), None, Some (FSComp.SR.optsWarnaserrorPM())) CompilerOption("warnaserror", tagWarnList, OptionStringListSwitch (fun n switch -> - match trimFStoInt n with - | Some n -> - if switch = OptionSwitch.Off then - tcConfigB.specificWarnAsError <- ListSet.remove (=) n tcConfigB.specificWarnAsError - tcConfigB.specificWarnAsWarn <- ListSet.insert (=) n tcConfigB.specificWarnAsWarn - else - tcConfigB.specificWarnAsWarn <- ListSet.remove (=) n tcConfigB.specificWarnAsWarn - tcConfigB.specificWarnAsError <- ListSet.insert (=) n tcConfigB.specificWarnAsError - | None -> () ), None, - Some (FSComp.SR.optsWarnaserror())); - - CompilerOption("warn", tagInt, OptionInt (fun n -> - tcConfigB.globalWarnLevel <- - if (n >= 0 && n <= 5) then n - else error(Error(FSComp.SR.optsInvalidWarningLevel(n),rangeCmdArgs))), None, - Some (FSComp.SR.optsWarn())); - - CompilerOption("nowarn", tagWarnList, OptionStringList (fun n -> tcConfigB.TurnWarningOff(rangeCmdArgs, trimFS n)), None, - Some (FSComp.SR.optsNowarn())); - - CompilerOption("warnon", tagWarnList, OptionStringList (fun n -> tcConfigB.TurnWarningOn(rangeCmdArgs, trimFS n)), None, - Some(FSComp.SR.optsWarnOn())); - - CompilerOption("consolecolors", tagNone, OptionSwitch (fun switch -> enableConsoleColoring <- switch = OptionSwitch.On), None, - Some (FSComp.SR.optsConsoleColors())) + match trimFStoInt n with + | Some n -> + let options = tcConfigB.errorSeverityOptions + tcConfigB.errorSeverityOptions <- + if switch = OptionSwitch.Off then + { options with + WarnAsError = ListSet.remove (=) n options.WarnAsError + WarnAsWarn = ListSet.insert (=) n options.WarnAsWarn } + else + { options with + WarnAsError = ListSet.insert (=) n options.WarnAsError + WarnAsWarn = ListSet.remove (=) n options.WarnAsWarn } + | None -> ()), None, Some (FSComp.SR.optsWarnaserror())) + + CompilerOption("warn", tagInt, OptionInt (fun n -> + tcConfigB.errorSeverityOptions <- + { tcConfigB.errorSeverityOptions with + WarnLevel = if (n >= 0 && n <= 5) then n else error(Error (FSComp.SR.optsInvalidWarningLevel(n), rangeCmdArgs)) } + ), None, Some (FSComp.SR.optsWarn())) + + CompilerOption("nowarn", tagWarnList, OptionStringList (fun n -> + tcConfigB.TurnWarningOff(rangeCmdArgs, trimFS n)), None, Some (FSComp.SR.optsNowarn())) + + CompilerOption("warnon", tagWarnList, OptionStringList (fun n -> + tcConfigB.TurnWarningOn(rangeCmdArgs, trimFS n)), None, Some (FSComp.SR.optsWarnOn())) + + CompilerOption("consolecolors", tagNone, OptionSwitch (fun switch -> + enableConsoleColoring <- switch = OptionSwitch.On), None, Some (FSComp.SR.optsConsoleColors())) ] @@ -1129,7 +1132,15 @@ let GetCoreFsiCompilerOptions (tcConfigB: TcConfigBuilder) = testingAndQAFlags tcConfigB]) ] - +let ApplyCommandLineArgs(tcConfigB: TcConfigBuilder, sourceFiles: string list, commandLineArgs) = + try + let sourceFilesAcc = ResizeArray(sourceFiles) + let collect name = if not (Filename.isDll name) then sourceFilesAcc.Add(name) + ParseCompilerOptions(collect, GetCoreServiceCompilerOptions tcConfigB, commandLineArgs) + ResizeArray.toList(sourceFilesAcc) + with e -> + errorRecovery e range0 + sourceFiles //---------------------------------------------------------------------------- diff --git a/src/fsharp/CompileOptions.fsi b/src/fsharp/CompileOptions.fsi index 2626f1ebe22..2a6858b1ff0 100644 --- a/src/fsharp/CompileOptions.fsi +++ b/src/fsharp/CompileOptions.fsi @@ -69,6 +69,9 @@ val GetCoreFscCompilerOptions : TcConfigBuilder -> CompilerOptionBlock list val GetCoreFsiCompilerOptions : TcConfigBuilder -> CompilerOptionBlock list val GetCoreServiceCompilerOptions : TcConfigBuilder -> CompilerOptionBlock list +/// Apply args to TcConfigBuilder and return new list of source files +val ApplyCommandLineArgs: tcConfigB: TcConfigBuilder * sourceFiles: string list * argv: string list -> string list + // Expose the "setters" for some user switches, to enable setting of defaults val SetOptimizeSwitch : TcConfigBuilder -> OptionSwitch -> unit val SetTailcallSwitch : TcConfigBuilder -> OptionSwitch -> unit diff --git a/src/fsharp/ErrorLogger.fs b/src/fsharp/ErrorLogger.fs index 5567225e4ed..fde22b1b7cf 100755 --- a/src/fsharp/ErrorLogger.fs +++ b/src/fsharp/ErrorLogger.fs @@ -639,4 +639,27 @@ let NormalizeErrorString (text : string) = buf.Append(c) |> ignore 1 i <- i + delta - buf.ToString() \ No newline at end of file + buf.ToString() + +#if COMPILER_PUBLIC_API +type FSharpErrorSeverityOptions = +#else +type internal FSharpErrorSeverityOptions = +#endif + { + WarnLevel: int + GlobalWarnAsError: bool + WarnOff: int list + WarnOn: int list + WarnAsError: int list + WarnAsWarn: int list + } + static member Default = + { + WarnLevel = 3 + GlobalWarnAsError = false + WarnOff = [] + WarnOn = [] + WarnAsError = [] + WarnAsWarn = [] + } diff --git a/src/fsharp/ReferenceResolver.fs b/src/fsharp/ReferenceResolver.fs index 805e1050aab..dd4ca025a52 100644 --- a/src/fsharp/ReferenceResolver.fs +++ b/src/fsharp/ReferenceResolver.fs @@ -28,6 +28,7 @@ module internal ReferenceResolver = override this.ToString() = sprintf "ResolvedFile(%s)" this.itemSpec + [] type Resolver = /// Get the "v4.5.1"-style moniker for the highest installed .NET Framework version. /// This is the value passed back to Resolve if no explicit "mscorlib" has been given. diff --git a/src/fsharp/fsc.fs b/src/fsharp/fsc.fs index 5984d6ef1bc..b598f70394e 100644 --- a/src/fsharp/fsc.fs +++ b/src/fsharp/fsc.fs @@ -77,7 +77,7 @@ type ErrorLoggerUpToMaxErrors(tcConfigB: TcConfigBuilder, exiter: Exiter, nameFo override x.ErrorCount = errors override x.DiagnosticSink(err, isError) = - if isError || ReportWarningAsError (tcConfigB.globalWarnLevel, tcConfigB.specificWarnOff, tcConfigB.specificWarnOn, tcConfigB.specificWarnAsError, tcConfigB.specificWarnAsWarn, tcConfigB.globalWarnAsError) err then + if isError || ReportWarningAsError tcConfigB.errorSeverityOptions err then if errors >= tcConfigB.maxErrors then x.HandleTooManyErrors(FSComp.SR.fscTooManyErrors()) exiter.Exit 1 @@ -92,7 +92,7 @@ type ErrorLoggerUpToMaxErrors(tcConfigB: TcConfigBuilder, exiter: Exiter, nameFo | :? KeyNotFoundException, None -> Debug.Assert(false, sprintf "Lookup exception in compiler: %s" (err.Exception.ToString())) | _ -> () - elif ReportWarning (tcConfigB.globalWarnLevel, tcConfigB.specificWarnOff, tcConfigB.specificWarnOn) err then + elif ReportWarning tcConfigB.errorSeverityOptions err then x.HandleIssue(tcConfigB, err, isError) diff --git a/src/fsharp/fsi/fsi.fs b/src/fsharp/fsi/fsi.fs index b9331351ffe..c5e3d54509b 100644 --- a/src/fsharp/fsi/fsi.fs +++ b/src/fsharp/fsi/fsi.fs @@ -558,7 +558,7 @@ type internal ErrorLoggerThatStopsOnFirstError(tcConfigB:TcConfigBuilder, fsiStd member x.ResetErrorCount() = (errorCount <- 0) override x.DiagnosticSink(err, isError) = - if isError || ReportWarningAsError (tcConfigB.globalWarnLevel, tcConfigB.specificWarnOff, tcConfigB.specificWarnOn, tcConfigB.specificWarnAsError, tcConfigB.specificWarnAsWarn, tcConfigB.globalWarnAsError) err then + if isError || ReportWarningAsError tcConfigB.errorSeverityOptions err then fsiStdinSyphon.PrintError(tcConfigB,err) errorCount <- errorCount + 1 if tcConfigB.abortOnError then exit 1 (* non-zero exit code *) @@ -566,7 +566,7 @@ type internal ErrorLoggerThatStopsOnFirstError(tcConfigB:TcConfigBuilder, fsiStd raise StopProcessing else DoWithErrorColor isError (fun () -> - if ReportWarning (tcConfigB.globalWarnLevel, tcConfigB.specificWarnOff, tcConfigB.specificWarnOn) err then + if ReportWarning tcConfigB.errorSeverityOptions err then fsiConsoleOutput.Error.WriteLine() writeViaBufferWithEnvironmentNewLines fsiConsoleOutput.Error (OutputDiagnosticContext " " fsiStdinSyphon.GetLine) err writeViaBufferWithEnvironmentNewLines fsiConsoleOutput.Error (OutputDiagnostic (tcConfigB.implicitIncludeDir,tcConfigB.showFullPaths,tcConfigB.flatErrors,tcConfigB.errorStyle,isError)) err @@ -2592,7 +2592,7 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i | Choice2Of2 None -> failwith "Operation failed. The error text has been printed in the error stream. To return the corresponding FSharpErrorInfo use the EvalInteractionNonThrowing, EvalScriptNonThrowing or EvalExpressionNonThrowing" | Choice2Of2 (Some userExn) -> raise userExn - let commitResultNonThrowing tcConfig scriptFile (errorLogger: CompilationErrorLogger) res = + let commitResultNonThrowing errorOptions scriptFile (errorLogger: CompilationErrorLogger) res = let errs = errorLogger.GetErrors() let userRes = match res with @@ -2600,7 +2600,7 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i | Choice2Of2 None -> Choice2Of2 (System.Exception "Operation could not be completed due to earlier error") | Choice2Of2 (Some userExn) -> Choice2Of2 userExn - userRes, ErrorHelpers.CreateErrorInfos (tcConfig, true, scriptFile, errs) + userRes, ErrorHelpers.CreateErrorInfos (errorOptions, true, scriptFile, errs) let dummyScriptFileName = "input.fsx" @@ -2725,10 +2725,10 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i // is not safe to call concurrently. let ctok = AssumeCompilationThreadWithoutEvidence() - let tcConfig = TcConfig.Create(tcConfigB,validate=false) - let errorLogger = CompilationErrorLogger("EvalInteraction",tcConfig) + let errorOptions = TcConfig.Create(tcConfigB,validate = false).errorSeverityOptions + let errorLogger = CompilationErrorLogger("EvalInteraction", errorOptions) fsiInteractionProcessor.EvalExpression(ctok, sourceText, dummyScriptFileName, errorLogger) - |> commitResultNonThrowing tcConfig dummyScriptFileName errorLogger + |> commitResultNonThrowing errorOptions dummyScriptFileName errorLogger member x.EvalInteraction(sourceText) : unit = // Explanation: When the user of the FsiInteractiveSession object calls this method, the @@ -2746,11 +2746,11 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i // is not safe to call concurrently. let ctok = AssumeCompilationThreadWithoutEvidence() - let tcConfig = TcConfig.Create(tcConfigB,validate=false) - let errorLogger = CompilationErrorLogger("EvalInteraction",tcConfig) + let errorOptions = TcConfig.Create(tcConfigB,validate = false).errorSeverityOptions + let errorLogger = CompilationErrorLogger("EvalInteraction", errorOptions) fsiInteractionProcessor.EvalInteraction(ctok, sourceText, dummyScriptFileName, errorLogger) - |> commitResultNonThrowing tcConfig "input.fsx" errorLogger - |> function Choice1Of2(_), errs -> Choice1Of2 (), errs | Choice2Of2 exn, errs -> Choice2Of2 exn, errs + |> commitResultNonThrowing errorOptions "input.fsx" errorLogger + |> function Choice1Of2 (_), errs -> Choice1Of2 (), errs | Choice2Of2 exn, errs -> Choice2Of2 exn, errs member x.EvalScript(scriptPath) : unit = // Explanation: When the user of the FsiInteractiveSession object calls this method, the @@ -2768,11 +2768,11 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i // is not safe to call concurrently. let ctok = AssumeCompilationThreadWithoutEvidence() - let tcConfig = TcConfig.Create(tcConfigB,validate=false) - let errorLogger = CompilationErrorLogger("EvalInteraction",tcConfig) + let errorOptions = TcConfig.Create(tcConfigB, validate = false).errorSeverityOptions + let errorLogger = CompilationErrorLogger("EvalInteraction", errorOptions) fsiInteractionProcessor.EvalScript(ctok, scriptPath, errorLogger) - |> commitResultNonThrowing tcConfig scriptPath errorLogger - |> function Choice1Of2(_), errs -> Choice1Of2 (), errs | Choice2Of2 exn, errs -> Choice2Of2 exn, errs + |> commitResultNonThrowing errorOptions scriptPath errorLogger + |> function Choice1Of2 (_), errs -> Choice1Of2 (), errs | Choice2Of2 exn, errs -> Choice2Of2 exn, errs /// Performs these steps: /// - Load the dummy interaction, if any diff --git a/src/fsharp/symbols/Exprs.fsi b/src/fsharp/symbols/Exprs.fsi index 291f380e297..7aa34ed00d5 100644 --- a/src/fsharp/symbols/Exprs.fsi +++ b/src/fsharp/symbols/Exprs.fsi @@ -28,6 +28,7 @@ and [] FSharpImplementationFileContents = #else and [] internal FSharpImplementationFileContents = #endif + internal new : cenv: Impl.cenv * mimpl: TypedImplFile -> FSharpImplementationFileContents /// The qualified name acts to fully-qualify module specifications and implementations member QualifiedName: string diff --git a/src/fsharp/symbols/SymbolHelpers.fs b/src/fsharp/symbols/SymbolHelpers.fs index 98229231b52..e6fd4a6edf5 100644 --- a/src/fsharp/symbols/SymbolHelpers.fs +++ b/src/fsharp/symbols/SymbolHelpers.fs @@ -145,17 +145,17 @@ type ErrorScope() = | None -> err "" /// An error logger that capture errors, filtering them according to warning levels etc. -type internal CompilationErrorLogger (debugName:string, tcConfig:TcConfig) = +type internal CompilationErrorLogger (debugName: string, options: FSharpErrorSeverityOptions) = inherit ErrorLogger("CompilationErrorLogger("+debugName+")") let mutable errorCount = 0 let diagnostics = new ResizeArray<_>() override x.DiagnosticSink(exn, isError) = - if isError || ReportWarningAsError (tcConfig.globalWarnLevel, tcConfig.specificWarnOff, tcConfig.specificWarnOn, tcConfig.specificWarnAsError, tcConfig.specificWarnAsWarn, tcConfig.globalWarnAsError) exn then + if isError || ReportWarningAsError options exn then diagnostics.Add(exn, isError) errorCount <- errorCount + 1 - else if ReportWarning (tcConfig.globalWarnLevel, tcConfig.specificWarnOff, tcConfig.specificWarnOn) exn then + else if ReportWarning options exn then diagnostics.Add(exn, isError) override x.ErrorCount = errorCount @@ -177,26 +177,26 @@ type CompilationGlobalsScope(errorLogger:ErrorLogger, phase: BuildPhase) = unwindEL.Dispose() module ErrorHelpers = - let ReportError (tcConfig:TcConfig, allErrors, mainInputFileName, fileInfo, (exn, sev)) = - [ let isError = (sev = FSharpErrorSeverity.Error) || ReportWarningAsError (tcConfig.globalWarnLevel, tcConfig.specificWarnOff, tcConfig.specificWarnOn, tcConfig.specificWarnAsError, tcConfig.specificWarnAsWarn, tcConfig.globalWarnAsError) exn - if (isError || ReportWarning (tcConfig.globalWarnLevel, tcConfig.specificWarnOff, tcConfig.specificWarnOn) exn) then + let ReportError (options, allErrors, mainInputFileName, fileInfo, (exn, sev)) = + [ let isError = (sev = FSharpErrorSeverity.Error) || ReportWarningAsError options exn + if (isError || ReportWarning options exn) then let oneError trim exn = [ // We use the first line of the file as a fallbackRange for reporting unexpected errors. // Not ideal, but it's hard to see what else to do. let fallbackRange = rangeN mainInputFileName 1 let ei = FSharpErrorInfo.CreateFromExceptionAndAdjustEof (exn, isError, trim, fallbackRange, fileInfo) - if allErrors || (ei.FileName=mainInputFileName) || (ei.FileName=Microsoft.FSharp.Compiler.TcGlobals.DummyFileNameForRangesWithoutASpecificLocation) then + if allErrors || (ei.FileName = mainInputFileName) || (ei.FileName = TcGlobals.DummyFileNameForRangesWithoutASpecificLocation) then yield ei ] - + let mainError, relatedErrors = SplitRelatedDiagnostics exn yield! oneError false mainError for e in relatedErrors do yield! oneError true e ] - let CreateErrorInfos (tcConfig:TcConfig, allErrors, mainInputFileName, errors) = + let CreateErrorInfos (options, allErrors, mainInputFileName, errors) = let fileInfo = (Int32.MaxValue, Int32.MaxValue) [| for (exn, isError) in errors do - yield! ReportError (tcConfig, allErrors, mainInputFileName, fileInfo, (exn, isError)) |] + yield! ReportError (options, allErrors, mainInputFileName, fileInfo, (exn, isError)) |] //---------------------------------------------------------------------------- diff --git a/src/fsharp/symbols/SymbolHelpers.fsi b/src/fsharp/symbols/SymbolHelpers.fsi index fbcc8757838..c36f931fe8d 100755 --- a/src/fsharp/symbols/SymbolHelpers.fsi +++ b/src/fsharp/symbols/SymbolHelpers.fsi @@ -235,10 +235,10 @@ type internal CompilationErrorLogger = inherit ErrorLogger /// Create the error logger - new : debugName:string * tcConfig:TcConfig -> CompilationErrorLogger + new: debugName:string * options: FSharpErrorSeverityOptions -> CompilationErrorLogger /// Get the captured errors - member GetErrors : unit -> (PhasedDiagnostic * FSharpErrorSeverity) list + member GetErrors: unit -> (PhasedDiagnostic * FSharpErrorSeverity) list /// This represents the global state established as each task function runs as part of the build. /// @@ -248,5 +248,5 @@ type internal CompilationGlobalsScope = interface IDisposable module internal ErrorHelpers = - val ReportError: TcConfig * allErrors: bool * mainInputFileName: string * fileInfo: (int * int) * (PhasedDiagnostic * FSharpErrorSeverity) -> FSharpErrorInfo list - val CreateErrorInfos: TcConfig * allErrors: bool * mainInputFileName: string * seq<(PhasedDiagnostic * FSharpErrorSeverity)> -> FSharpErrorInfo[] + val ReportError: FSharpErrorSeverityOptions * allErrors: bool * mainInputFileName: string * fileInfo: (int * int) * (PhasedDiagnostic * FSharpErrorSeverity) -> FSharpErrorInfo list + val CreateErrorInfos: FSharpErrorSeverityOptions * allErrors: bool * mainInputFileName: string * seq<(PhasedDiagnostic * FSharpErrorSeverity)> -> FSharpErrorInfo[] diff --git a/src/fsharp/vs/IncrementalBuild.fs b/src/fsharp/vs/IncrementalBuild.fs index f940a554be1..0d94b79c542 100755 --- a/src/fsharp/vs/IncrementalBuild.fs +++ b/src/fsharp/vs/IncrementalBuild.fs @@ -1095,7 +1095,8 @@ type PartialCheckResults = TcSymbolUses: TcSymbolUses list TcDependencyFiles: string list TopAttribs: TopAttribs option - TimeStamp: System.DateTime } + TimeStamp: System.DateTime + ImplementationFiles: TypedImplFile list } static member Create (tcAcc: TypeCheckAccumulator, timestamp) = { TcState = tcAcc.tcState @@ -1108,7 +1109,8 @@ type PartialCheckResults = TcSymbolUses = tcAcc.tcSymbolUses TcDependencyFiles = tcAcc.tcDependencyFiles TopAttribs = tcAcc.topAttribs - TimeStamp = timestamp } + TimeStamp = timestamp + ImplementationFiles = tcAcc.typedImplFiles } [] @@ -1190,9 +1192,9 @@ type IncrementalBuilder(tcGlobals,frameworkTcImports, nonFrameworkAssemblyInputs yield r.resolvedPath ] let allDependencies = - [ yield! basicDependencies - for (_,f,_) in sourceFiles do - yield f ] + [| yield! basicDependencies + for (_,f,_) in sourceFiles do + yield f |] // The IncrementalBuilder needs to hold up to one item that needs to be disposed, which is the tcImports for the incremental // build. @@ -1236,7 +1238,7 @@ type IncrementalBuilder(tcGlobals,frameworkTcImports, nonFrameworkAssemblyInputs assertNotDisposed() DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok - let errorLogger = CompilationErrorLogger("ParseTask", tcConfig) + let errorLogger = CompilationErrorLogger("ParseTask", tcConfig.errorSeverityOptions) // Return the disposable object that cleans up use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.Parse) @@ -1266,7 +1268,7 @@ type IncrementalBuilder(tcGlobals,frameworkTcImports, nonFrameworkAssemblyInputs let CombineImportedAssembliesTask ctok _ : Cancellable = cancellable { assertNotDisposed() - let errorLogger = CompilationErrorLogger("CombineImportedAssembliesTask", tcConfig) + let errorLogger = CompilationErrorLogger("CombineImportedAssembliesTask", tcConfig.errorSeverityOptions) // Return the disposable object that cleans up use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.Parameter) @@ -1330,7 +1332,7 @@ type IncrementalBuilder(tcGlobals,frameworkTcImports, nonFrameworkAssemblyInputs match input with | Some input, _sourceRange, filename, parseErrors-> IncrementalBuilderEventTesting.MRU.Add(IncrementalBuilderEventTesting.IBETypechecked filename) - let capturingErrorLogger = CompilationErrorLogger("TypeCheckTask", tcConfig) + let capturingErrorLogger = CompilationErrorLogger("TypeCheckTask", tcConfig.errorSeverityOptions) let errorLogger = GetErrorLoggerFilteringByScopedPragmas(false,GetScopedPragmasForInput(input),capturingErrorLogger) let fullComputation = eventually { @@ -1395,7 +1397,7 @@ type IncrementalBuilder(tcGlobals,frameworkTcImports, nonFrameworkAssemblyInputs assertNotDisposed() DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok - let errorLogger = CompilationErrorLogger("CombineImportedAssembliesTask", tcConfig) + let errorLogger = CompilationErrorLogger("CombineImportedAssembliesTask", tcConfig.errorSeverityOptions) use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.TypeCheck) // Get the state at the end of the type-checking of the last file @@ -1537,7 +1539,7 @@ type IncrementalBuilder(tcGlobals,frameworkTcImports, nonFrameworkAssemblyInputs member __.FileChecked = fileChecked.Publish member __.ProjectChecked = projectChecked.Publish member __.ImportedCcusInvalidated = importsInvalidated.Publish - member __.AllDependenciesDeprecated = allDependencies + member __.AllDependenciesDeprecated = allDependencies #if EXTENSIONTYPING member __.ThereAreLiveTypeProviders = @@ -1731,15 +1733,7 @@ type IncrementalBuilder(tcGlobals,frameworkTcImports, nonFrameworkAssemblyInputs | None -> () // Apply command-line arguments and collect more source files if they are in the arguments - let sourceFilesNew = - try - let sourceFilesAcc = ResizeArray(sourceFiles) - let collect name = if not (Filename.isDll name) then sourceFilesAcc.Add name - ParseCompilerOptions (collect, GetCoreServiceCompilerOptions tcConfigB, commandLineArgs) - sourceFilesAcc |> ResizeArray.toList - with e -> - errorRecovery e range0 - sourceFiles + let sourceFilesNew = ApplyCommandLineArgs(tcConfigB, sourceFiles, commandLineArgs) // Never open PDB files for the language service, even if --standalone is specified tcConfigB.openDebugInformationForLaterStaticLinking <- false @@ -1780,7 +1774,8 @@ type IncrementalBuilder(tcGlobals,frameworkTcImports, nonFrameworkAssemblyInputs // Note we are not calling errorLogger.GetErrors() anywhere for this task. // This is ok because not much can actually go wrong here. - let errorLogger = CompilationErrorLogger("nonFrameworkAssemblyInputs", tcConfig) + let errorOptions = tcConfig.errorSeverityOptions + let errorLogger = CompilationErrorLogger("nonFrameworkAssemblyInputs", errorOptions) // Return the disposable object that cleans up use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.Parameter) @@ -1791,7 +1786,7 @@ type IncrementalBuilder(tcGlobals,frameworkTcImports, nonFrameworkAssemblyInputs let nonFrameworkAssemblyInputs = // Note we are not calling errorLogger.GetErrors() anywhere for this task. // This is ok because not much can actually go wrong here. - let errorLogger = CompilationErrorLogger("nonFrameworkAssemblyInputs", tcConfig) + let errorLogger = CompilationErrorLogger("nonFrameworkAssemblyInputs", errorOptions) // Return the disposable object that cleans up use _holder = new CompilationGlobalsScope(errorLogger, BuildPhase.Parameter) diff --git a/src/fsharp/vs/IncrementalBuild.fsi b/src/fsharp/vs/IncrementalBuild.fsi index 3364cf212c4..cca9dba7f90 100755 --- a/src/fsharp/vs/IncrementalBuild.fsi +++ b/src/fsharp/vs/IncrementalBuild.fsi @@ -59,7 +59,10 @@ type internal PartialCheckResults = /// Represents the collected attributes to apply to the module of assuembly generates TopAttribs: TypeChecker.TopAttribs option - TimeStamp: DateTime } + TimeStamp: DateTime + + /// Represents complete typechecked implementation files, including thier typechecked signatures if any. + ImplementationFiles: TypedImplFile list } /// Manages an incremental build graph for the build of an F# project [] @@ -93,7 +96,7 @@ type internal IncrementalBuilder = member ImportedCcusInvalidated : IEvent /// The list of files the build depends on - member AllDependenciesDeprecated : string list + member AllDependenciesDeprecated : string[] #if EXTENSIONTYPING /// Whether there are any 'live' type providers that may need a refresh when a project is Cleaned member ThereAreLiveTypeProviders : bool diff --git a/src/fsharp/vs/ServiceUntypedParse.fs b/src/fsharp/vs/ServiceUntypedParse.fs index e64e5da8c38..c5e30bf3df0 100755 --- a/src/fsharp/vs/ServiceUntypedParse.fs +++ b/src/fsharp/vs/ServiceUntypedParse.fs @@ -85,7 +85,7 @@ type CompletionContext = //---------------------------------------------------------------------------- [] -type FSharpParseFileResults(errors : FSharpErrorInfo[], input : Ast.ParsedInput option, parseHadErrors : bool, dependencyFiles : string list) = +type FSharpParseFileResults(errors: FSharpErrorInfo[], input: Ast.ParsedInput option, parseHadErrors: bool, dependencyFiles: string[]) = member scope.Errors = errors @@ -382,7 +382,7 @@ type FSharpParseFileResults(errors : FSharpErrorInfo[], input : Ast.ParsedInput /// When these files appear or disappear the configuration for the current project is invalidated. member scope.DependencyFiles = dependencyFiles - + member scope.FileName = match input with | Some(ParsedInput.ImplFile(ParsedImplFileInput(fileName = modname))) diff --git a/src/fsharp/vs/ServiceUntypedParse.fsi b/src/fsharp/vs/ServiceUntypedParse.fsi index d766e6fb568..9a2c8379d53 100755 --- a/src/fsharp/vs/ServiceUntypedParse.fsi +++ b/src/fsharp/vs/ServiceUntypedParse.fsi @@ -37,7 +37,7 @@ type internal FSharpParseFileResults = member ValidateBreakpointLocation : pos:pos -> range option /// When these files change then the build is invalid - member DependencyFiles : string list + member DependencyFiles : string[] /// Get the errors and warnings for the parse member Errors : FSharpErrorInfo[] @@ -45,7 +45,7 @@ type internal FSharpParseFileResults = /// Indicates if any errors occurred during the parse member ParseHadErrors : bool - internal new : errors : FSharpErrorInfo[] * input : Ast.ParsedInput option * parseHadErrors : bool * dependencyFiles : string list -> FSharpParseFileResults + internal new: errors: FSharpErrorInfo[] * input: Ast.ParsedInput option * parseHadErrors: bool * dependencyFiles: string[] -> FSharpParseFileResults /// Information about F# source file names #if COMPILER_PUBLIC_API diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index f8e1dac2df8..5c2fc8f5445 100644 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -24,6 +24,7 @@ open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library open Microsoft.FSharp.Compiler.AccessibilityLogic open Microsoft.FSharp.Compiler.Ast open Microsoft.FSharp.Compiler.CompileOps +open Microsoft.FSharp.Compiler.CompileOptions open Microsoft.FSharp.Compiler.Driver open Microsoft.FSharp.Compiler.ErrorLogger open Microsoft.FSharp.Compiler.Lib @@ -53,11 +54,11 @@ type internal Layout = StructuredFormat.Layout [] module EnvMisc = - let getToolTipTextSize = GetEnvInteger "FCS_RecentForegroundTypeCheckCacheSize" 5 + let getToolTipTextSize = GetEnvInteger "FCS_GetToolTipTextCacheSize" 5 let maxTypeCheckErrorsOutOfProjectContext = GetEnvInteger "FCS_MaxErrorsOutOfProjectContext" 3 let braceMatchCacheSize = GetEnvInteger "FCS_BraceMatchCacheSize" 5 - let parseFileInProjectCacheSize = GetEnvInteger "FCS_ParseFileInProjectCacheSize" 2 - let incrementalTypeCheckCacheSize = GetEnvInteger "FCS_IncrementalTypeCheckCacheSize" 5 + let parseFileCacheSize = GetEnvInteger "FCS_ParseFileCacheSize" 2 + let checkFileInProjectCacheSize = GetEnvInteger "FCS_CheckFileInProjectCacheSize" 5 let projectCacheSizeDefault = GetEnvInteger "FCS_ProjectCacheSizeDefault" 3 let frameworkTcImportsCacheStrongSize = GetEnvInteger "FCS_frameworkTcImportsCacheStrongSizeDefault" 8 @@ -163,7 +164,8 @@ type TypeCheckInfo loadClosure : LoadClosure option, reactorOps : IReactorOperations, checkAlive : (unit -> bool), - textSnapshotInfo:obj option) = + textSnapshotInfo:obj option, + implementationFiles: TypedImplFile list) = let textSnapshotInfo = defaultArg textSnapshotInfo null let (|CNR|) (cnr:CapturedNameResolution) = @@ -1356,9 +1358,52 @@ type TypeCheckInfo /// The assembly being analyzed member __.ThisCcu = thisCcu + member __.ImplementationFiles = implementationFiles + override __.ToString() = "TypeCheckInfo(" + mainInputFileName + ")" +type FSharpParsingOptions = + { + SourceFiles: string [] + ConditionalCompilationDefines: string list + ErrorSeverityOptions: FSharpErrorSeverityOptions + LightSyntax: bool option + CompilingFsLib: bool + IsExe: bool + } + + member x.LastFileName = + Debug.Assert(not (Array.isEmpty x.SourceFiles), "Parsing options don't contain any file") + Array.last x.SourceFiles + + static member Default = + { SourceFiles = Array.empty + ConditionalCompilationDefines = [] + ErrorSeverityOptions = FSharpErrorSeverityOptions.Default + LightSyntax = None + CompilingFsLib = false + IsExe = false + } + + static member FromTcConfig(tcConfig: TcConfig, sourceFiles) = + { + SourceFiles = sourceFiles + ConditionalCompilationDefines = tcConfig.conditionalCompilationDefines + ErrorSeverityOptions = tcConfig.errorSeverityOptions + LightSyntax = tcConfig.light + CompilingFsLib = tcConfig.compilingFslib + IsExe = tcConfig.target.IsExe + } + static member FromTcConfigBuidler(tcConfigB: TcConfigBuilder, sourceFiles) = + { + SourceFiles = sourceFiles + ConditionalCompilationDefines = tcConfigB.conditionalCompilationDefines + ErrorSeverityOptions = tcConfigB.errorSeverityOptions + LightSyntax = tcConfigB.light + CompilingFsLib = tcConfigB.compilingFslib + IsExe = tcConfigB.target.IsExe + } module internal Parser = @@ -1372,153 +1417,127 @@ module internal Parser = /// Error handler for parsing & type checking while processing a single file - type ErrorHandler(reportErrors, mainInputFileName, tcConfig: TcConfig, source: string) = - let mutable tcConfig = tcConfig + type ErrorHandler(reportErrors, mainInputFileName, errorSeverityOptions: FSharpErrorSeverityOptions, source) = + let mutable options = errorSeverityOptions let errorsAndWarningsCollector = new ResizeArray<_>() let mutable errorCount = 0 - + // We'll need number of lines for adjusting error messages at EOF let fileInfo = GetFileInfoForLastLineErrors source - + // This function gets called whenever an error happens during parsing or checking - let diagnosticSink sev (exn:PhasedDiagnostic) = + let diagnosticSink sev (exn: PhasedDiagnostic) = // Sanity check here. The phase of an error should be in a phase known to the language service. let exn = if not(exn.IsPhaseInCompile()) then // Reaching this point means that the error would be sticky if we let it prop up to the language service. // Assert and recover by replacing phase with one known to the language service. Trace.TraceInformation(sprintf "The subcategory '%s' seen in an error should not be seen by the language service" (exn.Subcategory())) - {exn with Phase=BuildPhase.TypeCheck} + { exn with Phase = BuildPhase.TypeCheck } else exn - if reportErrors then - let report exn = - for ei in ErrorHelpers.ReportError (tcConfig, false, mainInputFileName, fileInfo, (exn, sev)) do + if reportErrors then + let report exn = + for ei in ErrorHelpers.ReportError (options, false, mainInputFileName, fileInfo, (exn, sev)) do errorsAndWarningsCollector.Add ei - if sev = FSharpErrorSeverity.Error then + if sev = FSharpErrorSeverity.Error then errorCount <- errorCount + 1 - + match exn with #if EXTENSIONTYPING - | {Exception = (:? TypeProviderError as tpe)} -> - tpe.Iter (fun e -> - let newExn = {exn with Exception = e} - report newExn - ) + | { Exception = (:? TypeProviderError as tpe) } -> tpe.Iter(fun e -> report { exn with Exception = e }) #endif | e -> report e - - let errorLogger = - { new ErrorLogger("ErrorHandler") with + + let errorLogger = + { new ErrorLogger("ErrorHandler") with member x.DiagnosticSink (exn, isError) = diagnosticSink (if isError then FSharpErrorSeverity.Error else FSharpErrorSeverity.Warning) exn member x.ErrorCount = errorCount } - - + // Public members member x.ErrorLogger = errorLogger member x.CollectedDiagnostics = errorsAndWarningsCollector.ToArray() member x.ErrorCount = errorCount - member x.TcConfig with set tc = tcConfig <- tc + member x.ErrorSeverityOptions with set opts = options <- opts member x.AnyErrors = errorCount > 0 + let getLightSyntaxStatus fileName options = + let lower = String.lowercase fileName + let lightOnByDefault = List.exists (Filename.checkSuffix lower) FSharpLightSyntaxFileSuffixes + let lightSyntaxStatus = if lightOnByDefault then (options.LightSyntax <> Some false) else (options.LightSyntax = Some true) + LightSyntaxStatus(lightSyntaxStatus, true) - /// ParseOneFile builds all the information necessary to report errors, match braces and build scopes - /// - /// projectSourceFiles is only used to compute isLastCompiland, and is ignored if Build.IsScript(mainInputFileName) is true. - let ParseOneFile (ctok, source: string, matchBracesOnly: bool, reportErrors: bool, mainInputFileName: string, projectSourceFiles: string list, tcConfig: TcConfig) = - - // This function requires the compilation thread because we install error handlers, whose callbacks must - // be invoked on the compilation thread, no other reason known to date. - // We should check whether those are "real" reasons - we could for example make collecting errors thread safe. - RequireCompilationThread ctok - - // Initialize the error handler - let errHandler = new ErrorHandler(reportErrors, mainInputFileName, tcConfig, source) + let createLexerFunction fileName options lexbuf (errHandler: ErrorHandler) = + let lightSyntaxStatus = getLightSyntaxStatus fileName options - // Adding this new-line character at the end of the source seems odd but is required for some unit tests - let source = if source.Length = 0 || not (source.[source.Length - 1] = '\n') then source + "\n" else source - let lexbuf = UnicodeLexing.StringAsLexbuf source + // If we're editing a script then we define INTERACTIVE otherwise COMPILED. + // Since this parsing for intellisense we always define EDITING. + let defines = SourceFileImpl.AdditionalDefinesForUseInEditor(fileName) @ options.ConditionalCompilationDefines - // Collector for parens matching - let matchPairRef = new ResizeArray<_>() - - use unwindEL = PushErrorLoggerPhaseUntilUnwind (fun _oldLogger -> errHandler.ErrorLogger) - use unwindBP = PushThreadBuildPhaseUntilUnwind BuildPhase.Parse - - // Errors on while parsing project arguments - - let parseResult = - - // If we're editing a script then we define INTERACTIVE otherwise COMPILED. Since this parsing for intellisense we always - // define EDITING - let conditionalCompilationDefines = - SourceFileImpl.AdditionalDefinesForUseInEditor(mainInputFileName) @ tcConfig.conditionalCompilationDefines + // Note: we don't really attempt to intern strings across a large scope. + let lexResourceManager = new Lexhelp.LexResourceManager() - let lightSyntaxStatusInital = tcConfig.ComputeLightSyntaxInitialStatus (mainInputFileName) - let lightSyntaxStatus = LightSyntaxStatus(lightSyntaxStatusInital,true) - - // Note: we don't really attempt to intern strings across a large scope - let lexResourceManager = new Lexhelp.LexResourceManager() - let lexargs = mkLexargs(mainInputFileName, - conditionalCompilationDefines, - lightSyntaxStatus, - lexResourceManager, - ref [], - errHandler.ErrorLogger) - - // When analyzing files using ParseOneFile, i.e. for the use of editing clients, we do not apply line directives. - let lexargs = { lexargs with applyLineDirectives=false } - - Lexhelp.usingLexbufForParsing (lexbuf, mainInputFileName) (fun lexbuf -> - try - let skip = true - let tokenizer = LexFilter.LexFilter (lightSyntaxStatus, tcConfig.compilingFslib, Lexer.token lexargs skip, lexbuf) - let lexfun = tokenizer.Lexer - if matchBracesOnly then - // Quick bracket matching parse - let parenTokensBalance t1 t2 = - match t1,t2 with - | (LPAREN,RPAREN) - | (LPAREN,RPAREN_IS_HERE) - | (LBRACE,RBRACE) - | (LBRACE,RBRACE_IS_HERE) - | (SIG,END) - | (STRUCT,END) - | (LBRACK_BAR,BAR_RBRACK) - | (LBRACK,RBRACK) - | (LBRACK_LESS,GREATER_RBRACK) - | (BEGIN,END) -> true - | (LQUOTE q1,RQUOTE q2) when q1 = q2 -> true - | _ -> false - let rec matchBraces stack = - match lexfun lexbuf,stack with - | tok2,((tok1,m1) :: stack') when parenTokensBalance tok1 tok2-> - if matchBracesOnly then - matchPairRef.Add (m1, lexbuf.LexemeRange) - matchBraces stack' - | ((LPAREN | LBRACE | LBRACK | LBRACK_BAR | LQUOTE _ | LBRACK_LESS) as tok),_ -> matchBraces ((tok,lexbuf.LexemeRange) :: stack) - | (EOF _ | LEX_FAILURE _),_ -> () - | _ -> matchBraces stack - - matchBraces [] - None - else - let isLastCompiland = - projectSourceFiles.Length >= 1 && - System.String.Compare(projectSourceFiles.[projectSourceFiles.Length-1],mainInputFileName,StringComparison.CurrentCultureIgnoreCase)=0 - let isLastCompiland = isLastCompiland || CompileOps.IsScript(mainInputFileName) - let isExe = tcConfig.target.IsExe - let parseResult = ParseInput(lexfun,errHandler.ErrorLogger,lexbuf,None,mainInputFileName,(isLastCompiland,isExe)) - Some parseResult - with e -> + // When analyzing files using ParseOneFile, i.e. for the use of editing clients, we do not apply line directives. + let lexargs = mkLexargs(fileName, defines, lightSyntaxStatus, lexResourceManager, ref [], errHandler.ErrorLogger) + let lexargs = { lexargs with applyLineDirectives = false } + + let tokenizer = LexFilter.LexFilter(lightSyntaxStatus, options.CompilingFsLib, Lexer.token lexargs true, lexbuf) + tokenizer.Lexer + + // Adding this new-line character at the end of the source seems odd but is required for some unit tests + // Todo: fix tests + let addNewLine (source: string) = + if source.Length = 0 || not (source.[source.Length - 1] = '\n') then source + "\n" else source + + let matchBraces(source, fileName, options: FSharpParsingOptions, userOpName: string) = + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "matchBraces", fileName) + let matchingBraces = new ResizeArray<_>() + Lexhelp.usingLexbufForParsing(UnicodeLexing.StringAsLexbuf(addNewLine source), fileName) (fun lexbuf -> + let errHandler = ErrorHandler(false, fileName, options.ErrorSeverityOptions, source) + let lexfun = createLexerFunction fileName options lexbuf errHandler + let parenTokensBalance t1 t2 = + match t1, t2 with + | (LPAREN, RPAREN) + | (LPAREN, RPAREN_IS_HERE) + | (LBRACE, RBRACE) + | (LBRACE, RBRACE_IS_HERE) + | (SIG, END) + | (STRUCT, END) + | (LBRACK_BAR, BAR_RBRACK) + | (LBRACK, RBRACK) + | (LBRACK_LESS, GREATER_RBRACK) + | (BEGIN, END) -> true + | (LQUOTE q1, RQUOTE q2) -> q1 = q2 + | _ -> false + let rec matchBraces stack = + match lexfun lexbuf, stack with + | tok2, ((tok1, m1) :: stack') when parenTokensBalance tok1 tok2 -> + matchingBraces.Add(m1, lexbuf.LexemeRange) + matchBraces stack' + | ((LPAREN | LBRACE | LBRACK | LBRACK_BAR | LQUOTE _ | LBRACK_LESS) as tok), _ -> + matchBraces ((tok, lexbuf.LexemeRange) :: stack) + | (EOF _ | LEX_FAILURE _), _ -> () + | _ -> matchBraces stack + matchBraces []) + matchingBraces.ToArray() + + let parseFile(source, fileName, options: FSharpParsingOptions, userOpName: string) = + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "parseFile", fileName) + let errHandler = new ErrorHandler(true, fileName, options.ErrorSeverityOptions, source) + use unwindEL = PushErrorLoggerPhaseUntilUnwind (fun _oldLogger -> errHandler.ErrorLogger) + use unwindBP = PushThreadBuildPhaseUntilUnwind BuildPhase.Parse + + let parseResult = + Lexhelp.usingLexbufForParsing(UnicodeLexing.StringAsLexbuf(addNewLine source), fileName) (fun lexbuf -> + let lexfun = createLexerFunction fileName options lexbuf errHandler + let isLastCompiland = + fileName.Equals(options.LastFileName, StringComparison.CurrentCultureIgnoreCase) || + CompileOps.IsScript(fileName) + let isExe = options.IsExe + try Some (ParseInput(lexfun, errHandler.ErrorLogger, lexbuf, None, fileName, (isLastCompiland, isExe))) + with e -> errHandler.ErrorLogger.ErrorR(e) None) - - - errHandler.CollectedDiagnostics, - matchPairRef.ToArray(), - parseResult, - errHandler.AnyErrors - + errHandler.CollectedDiagnostics, parseResult, errHandler.AnyErrors /// Indicates if the type check got aborted because it is no longer relevant. type TypeCheckAborted = Yes | No of TypeCheckInfo @@ -1550,7 +1569,7 @@ module internal Parser = // Run the type checker... | Some parsedMainInput -> // Initialize the error handler - let errHandler = new ErrorHandler(true, mainInputFileName, tcConfig, source) + let errHandler = new ErrorHandler(true, mainInputFileName, tcConfig.errorSeverityOptions, source) use _unwindEL = PushErrorLoggerPhaseUntilUnwind (fun _oldLogger -> errHandler.ErrorLogger) use _unwindBP = PushThreadBuildPhaseUntilUnwind BuildPhase.TypeCheck @@ -1559,7 +1578,7 @@ module internal Parser = let tcConfig = ApplyNoWarnsToTcConfig (tcConfig, parsedMainInput,Path.GetDirectoryName mainInputFileName) // update the error handler with the modified tcConfig - errHandler.TcConfig <- tcConfig + errHandler.ErrorSeverityOptions <- tcConfig.errorSeverityOptions // Play background errors and warnings for this file. for (err,sev) in backgroundDiagnostics do @@ -1662,7 +1681,7 @@ module internal Parser = let errors = errHandler.CollectedDiagnostics match tcEnvAtEndOpt with - | Some (tcEnvAtEnd, _typedImplFiles, tcState) -> + | Some (tcEnvAtEnd, typedImplFiles, tcState) -> let scope = TypeCheckInfo(tcConfig, tcGlobals, tcState.PartialAssemblySignature, @@ -1678,7 +1697,8 @@ module internal Parser = loadClosure, reactorOps, checkAlive, - textSnapshotInfo) + textSnapshotInfo, + typedImplFiles) return errors, TypeCheckAborted.No scope | None -> return errors, TypeCheckAborted.Yes @@ -1706,15 +1726,6 @@ type FSharpProjectOptions = static member UseSameProjectFileName(options1,options2) = options1.ProjectFileName = options2.ProjectFileName - /// Compare two options sets with respect to the parts of the options that are important to parsing. - static member AreSameForParsing(options1,options2) = - match options1.Stamp, options2.Stamp with - | Some x, Some y -> (x = y) - | _ -> - options1.ProjectFileName = options2.ProjectFileName && - options1.OtherOptions = options2.OtherOptions && - options1.UnresolvedReferences = options2.UnresolvedReferences - /// Compare two options sets with respect to the parts of the options that are important to building. static member AreSameForChecking(options1,options2) = match options1.Stamp, options2.Stamp with @@ -1745,7 +1756,7 @@ type FSharpProjectContext(thisCcu: CcuThunk, assemblies: FSharpAssembly list, ad [] // 'details' is an option because the creation of the tcGlobals etc. for the project may have failed. -type FSharpCheckProjectResults(projectFileName:string, keepAssemblyContents, errors: FSharpErrorInfo[], details:(TcGlobals*TcImports*CcuThunk*ModuleOrNamespaceType*TcSymbolUses list*TopAttribs option*CompileOps.IRawFSharpAssemblyData option * ILAssemblyRef * AccessorDomain * TypedImplFile list option * string list) option, _reactorOps: IReactorOperations) = +type FSharpCheckProjectResults(projectFileName:string, keepAssemblyContents, errors: FSharpErrorInfo[], details:(TcGlobals*TcImports*CcuThunk*ModuleOrNamespaceType*TcSymbolUses list*TopAttribs option*CompileOps.IRawFSharpAssemblyData option * ILAssemblyRef * AccessorDomain * TypedImplFile list option * string[]) option, _reactorOps: IReactorOperations) = let getDetails() = match details with @@ -1761,7 +1772,7 @@ type FSharpCheckProjectResults(projectFileName:string, keepAssemblyContents, err FSharpAssemblySignature(tcGlobals, thisCcu, tcImports, topAttribs, ccuSig) member info.AssemblyContents = - if not keepAssemblyContents then invalidOp "The 'keepAssemblyContents' flag must be set to tru on the FSharpChecker in order to access the checked contents of assemblies" + if not keepAssemblyContents then invalidOp "The 'keepAssemblyContents' flag must be set to true on the FSharpChecker in order to access the checked contents of assemblies" let (tcGlobals, tcImports, thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles) = getDetails() let mimpls = match tcAssemblyExpr with @@ -1817,7 +1828,7 @@ type FSharpCheckProjectResults(projectFileName:string, keepAssemblyContents, err // // There is an important property of all the objects returned by the methods of this type: they do not require // the corresponding background builder to be alive. That is, they are simply plain-old-data through pre-formatting of all result text. -type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOptX: TypeCheckInfo option, dependencyFiles: string list, builderX: IncrementalBuilder option, reactorOpsX:IReactorOperations) = +type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOptX: TypeCheckInfo option, dependencyFiles: string[], builderX: IncrementalBuilder option, reactorOpsX:IReactorOperations, keepAssemblyContents: bool) = // This may be None initially, or may be set to None when the object is disposed or finalized let mutable details = match scopeOptX with None -> None | Some scopeX -> Some (scopeX, builderX, reactorOpsX) @@ -2002,6 +2013,12 @@ type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOp RequireCompilationThread ctok scope.IsRelativeNameResolvable(pos, plid, item)) + member info.ImplementationFiles = + if not keepAssemblyContents then invalidOp "The 'keepAssemblyContents' flag must be set to true on the FSharpChecker in order to access the checked contents of assemblies" + scopeOptX + |> Option.map (fun scope -> + let cenv = Impl.cenv(scope.TcGlobals, scope.ThisCcu, scope.TcImports) + [ for mimpl in scope.ImplementationFiles -> FSharpImplementationFileContents(cenv, mimpl)]) override info.ToString() = "FSharpCheckFileResults(" + filename + ")" @@ -2038,10 +2055,11 @@ module Helpers = && FSharpProjectOptions.UseSameProjectFileName(o1,o2) /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. parsing - let AreSameForParsing3((fileName1: string, source1: string, options1: FSharpProjectOptions), (fileName2, source2, options2)) = - (fileName1 = fileName2) - && FSharpProjectOptions.AreSameForParsing(options1,options2) - && (source1 = source2) + let AreSameForParsing((fileName1: string, source1: string, options1), (fileName2, source2, options2)) = + fileName1 = fileName2 && options1 = options2 && source1 = source2 + + let AreSimilarForParsing((fileName1, _, _), (fileName2, _, _)) = + fileName1 = fileName2 /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. checking let AreSameForChecking3((fileName1: string, source1: string, options1: FSharpProjectOptions), (fileName2, source2, options2)) = @@ -2289,29 +2307,26 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.parseFileInProjectCache. Most recently used cache for parsing files. - let parseFileInProjectCache = - MruCache(parseFileInProjectCacheSize, - areSame=AreSameForParsing3, - areSimilar=AreSubsumable3) + let parseFileCache = MruCache(parseFileCacheSize, areSimilar = AreSimilarForParsing, areSame = AreSameForParsing) - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.parseAndCheckFileInProjectCachePossiblyStale - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.parseAndCheckFileInProjectCache + // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.checkFileInProjectCachePossiblyStale + // 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 - let parseAndCheckFileInProjectCachePossiblyStale = + let checkFileInProjectCachePossiblyStale = MruCache - (keepStrongly=incrementalTypeCheckCacheSize, + (keepStrongly=checkFileInProjectCacheSize, areSame=AreSameForChecking2, areSimilar=AreSubsumable2) // Also keyed on source. This can only be out of date if the antecedent is out of date - let parseAndCheckFileInProjectCache = + let checkFileInProjectCache = MruCache - (keepStrongly=incrementalTypeCheckCacheSize, + (keepStrongly=checkFileInProjectCacheSize, areSame=AreSameForChecking3, areSimilar=AreSubsumable3) @@ -2326,7 +2341,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC static let mutable foregroundTypeCheckCount = 0 let MakeCheckFileResultsEmpty(filename, creationErrors) = - FSharpCheckFileResults (filename, Array.ofList creationErrors, None, [], None, reactorOps) + FSharpCheckFileResults (filename, Array.ofList creationErrors, None, [| |], None, reactorOps, keepAssemblyContents) let MakeCheckFileResults(filename, options:FSharpProjectOptions, builder, scope, dependencyFiles, creationErrors, parseErrors, tcErrors) = let errors = @@ -2337,64 +2352,39 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC else yield! tcErrors |] - FSharpCheckFileResults (filename, errors, Some scope, dependencyFiles, Some builder, reactorOps) + FSharpCheckFileResults (filename, errors, Some scope, dependencyFiles, Some builder, reactorOps, keepAssemblyContents) let MakeCheckFileAnswer(filename, tcFileResult, options:FSharpProjectOptions, builder, dependencyFiles, creationErrors, parseErrors, tcErrors) = match tcFileResult with | Parser.TypeCheckAborted.Yes -> FSharpCheckFileAnswer.Aborted | Parser.TypeCheckAborted.No scope -> FSharpCheckFileAnswer.Succeeded(MakeCheckFileResults(filename, options, builder, scope, dependencyFiles, creationErrors, parseErrors, tcErrors)) - member bc.RecordTypeCheckFileInProjectResults(filename,options,parseResults,fileVersion,priorTimeStamp,checkAnswer,source) = + member bc.RecordTypeCheckFileInProjectResults(filename,options,parsingOptions,parseResults,fileVersion,priorTimeStamp,checkAnswer,source) = match checkAnswer with | None | Some FSharpCheckFileAnswer.Aborted -> () | Some (FSharpCheckFileAnswer.Succeeded typedResults) -> foregroundTypeCheckCount <- foregroundTypeCheckCount + 1 parseCacheLock.AcquireLock (fun ltok -> - parseAndCheckFileInProjectCachePossiblyStale.Set(ltok, (filename,options),(parseResults,typedResults,fileVersion)) - parseAndCheckFileInProjectCache.Set(ltok, (filename,source,options),(parseResults,typedResults,fileVersion,priorTimeStamp)) - parseFileInProjectCache.Set(ltok, (filename,source,options),parseResults)) + checkFileInProjectCachePossiblyStale.Set(ltok, (filename,options),(parseResults,typedResults,fileVersion)) + checkFileInProjectCache.Set(ltok, (filename,source,options),(parseResults,typedResults,fileVersion,priorTimeStamp)) + parseFileCache.Set(ltok, (filename, source, parsingOptions), parseResults)) member bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) = if implicitlyStartBackgroundWork then bc.CheckProjectInBackground(options, userOpName + ".ImplicitlyStartCheckProjectInBackground") - /// Parses the source file and returns untyped AST - member bc.ParseFileInProject(filename:string, source,options:FSharpProjectOptions, userOpName) = - match parseCacheLock.AcquireLock (fun ctok -> parseFileInProjectCache.TryGet (ctok, (filename, source, options))) with - | Some parseResults -> async.Return parseResults - | None -> - // Try this cache too (which might contain different entries) - let cachedResults = parseCacheLock.AcquireLock (fun ctok -> parseAndCheckFileInProjectCache.TryGet(ctok,(filename,source,options))) - match cachedResults with - | Some (parseResults, _checkResults,_,_) -> async.Return parseResults - | _ -> - reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseFileInProject", filename, fun ctok -> - cancellable { - // Try the caches again - it may have been filled by the time this operation runs - match parseCacheLock.AcquireLock (fun ctok -> parseFileInProjectCache.TryGet (ctok, (filename, source, options))) with - | Some parseResults -> return parseResults - | None -> - let cachedResults = parseCacheLock.AcquireLock (fun ctok -> parseAndCheckFileInProjectCache.TryGet(ctok, (filename,source,options))) - match cachedResults with - | Some (parseResults, _checkResults,_,_) -> return parseResults - | _ -> - Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "ParseFileInProject.CacheMiss", filename) - foregroundParseCount <- foregroundParseCount + 1 - let! builderOpt,creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) - use _unwind = decrement - match builderOpt with - | None -> return FSharpParseFileResults(List.toArray creationErrors, None, true, []) - | Some builder -> - // Do the parsing. - let parseErrors, _matchPairs, inputOpt, anyErrors = - Parser.ParseOneFile (ctok, source, false, true, filename, builder.SourceFiles, builder.TcConfig) - - let res = FSharpParseFileResults(parseErrors, inputOpt, anyErrors, builder.AllDependenciesDeprecated ) - parseCacheLock.AcquireLock (fun ctok -> parseFileInProjectCache.Set (ctok, (filename, source, options), res)) - return res - } - ) + member bc.ParseFile(filename: string, source: string, options: FSharpParsingOptions, userOpName: string) = + async { + match parseCacheLock.AcquireLock(fun ltok -> parseFileCache.TryGet(ltok, (filename, source, options))) with + | Some res -> return res + | None -> + foregroundParseCount <- foregroundParseCount + 1 + let parseErrors, inputOpt, anyErrors = Parser.parseFile(source, filename, options, userOpName) + let res = FSharpParseFileResults(parseErrors, inputOpt, anyErrors, options.SourceFiles) + parseCacheLock.AcquireLock(fun ltok -> parseFileCache.Set(ltok, (filename, source, options), res)) + return res + } /// Fetch the parse information from the background compiler (which checks w.r.t. the FileSystem API) member bc.GetBackgroundParseResultsForFileInProject(filename, options, userOpName) = @@ -2403,32 +2393,17 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let! builderOpt, creationErrors, decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) use _unwind = decrement match builderOpt with - | None -> return FSharpParseFileResults(List.toArray creationErrors, None, true, []) + | None -> return FSharpParseFileResults(List.toArray creationErrors, None, true, [| |]) | Some builder -> let! inputOpt,_,_,parseErrors = builder.GetParseResultsForFile (ctok, filename) - let errors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (builder.TcConfig, false, filename, parseErrors) |] + let errors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (builder.TcConfig.errorSeverityOptions, false, filename, parseErrors) |] return FSharpParseFileResults(errors = errors, input = inputOpt, parseHadErrors = false, dependencyFiles = builder.AllDependenciesDeprecated) } ) - member bc.MatchBraces(filename:string, source, options, userOpName) = - reactor.EnqueueAndAwaitOpAsync(userOpName, "MatchBraces", filename, fun ctok -> - cancellable { - let! builderOpt,_,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) - use _unwind = decrement - match builderOpt with - | None -> return [| |] - | Some builder -> - let _parseErrors, matchPairs, _inputOpt, _anyErrors = - Parser.ParseOneFile (ctok, source, true, false, filename, builder.SourceFiles, builder.TcConfig) - - return matchPairs - } - ) - member bc.GetCachedCheckFileResult(builder: IncrementalBuilder,filename,source,options) = // 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 = parseCacheLock.AcquireLock (fun ltok -> parseAndCheckFileInProjectCache.TryGet(ltok, (filename,source,options))) + let cachedResults = parseCacheLock.AcquireLock (fun ltok -> checkFileInProjectCache.TryGet(ltok, (filename,source,options))) match cachedResults with // | Some (parseResults, checkResults, _, _) when builder.AreCheckResultsBeforeFileInProjectReady(filename) -> @@ -2486,8 +2461,9 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let! tcErrors, tcFileResult = Parser.CheckOneFile(parseResults, source, fileName, options.ProjectFileName, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcPrior.TcState, loadClosure, tcPrior.Errors, reactorOps, (fun () -> builder.IsAlive), textSnapshotInfo, userOpName) - let checkAnswer = MakeCheckFileAnswer(fileName, tcFileResult, options, builder, tcPrior.TcDependencyFiles, creationErrors, parseResults.Errors, tcErrors) - bc.RecordTypeCheckFileInProjectResults(fileName, options, parseResults, fileVersion, tcPrior.TimeStamp, Some checkAnswer, source) + let parsingOptions = FSharpParsingOptions.FromTcConfig(tcPrior.TcConfig, Array.ofList builder.SourceFiles) + let checkAnswer = MakeCheckFileAnswer(fileName, tcFileResult, options, builder, Array.ofList tcPrior.TcDependencyFiles, creationErrors, parseResults.Errors, tcErrors) + bc.RecordTypeCheckFileInProjectResults(fileName, options, parsingOptions, parseResults, fileVersion, tcPrior.TimeStamp, Some checkAnswer, source) return checkAnswer finally let dummy = ref () @@ -2504,20 +2480,25 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC /// Type-check the result obtained by parsing, but only if the antecedent type checking context is available. member bc.CheckFileInProjectAllowingStaleCachedResults(parseResults: FSharpParseFileResults, filename, fileVersion, source, options, textSnapshotInfo: obj option, userOpName) = - let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync(userOpName, "CheckFileInProjectAllowingStaleCachedResults ", filename, action >> cancellable.Return) + let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync(userOpName, "CheckFileInProjectAllowingStaleCachedResults ", filename, action) async { try if implicitlyStartBackgroundWork then reactor.CancelBackgroundOp() // cancel the background work, since we will start new work after we're done let! cachedResults = - execWithReactorAsync <| fun ctok -> + execWithReactorAsync <| fun ctok -> + cancellable { + let! _builderOpt,_creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) + use _unwind = decrement + match incrementalBuildersCache.TryGetAny (ctok, options) with | Some (Some builder, creationErrors, _) -> match bc.GetCachedCheckFileResult(builder, filename, source, options) with - | Some (_, checkResults) -> Some (builder, creationErrors, Some (FSharpCheckFileAnswer.Succeeded checkResults)) - | _ -> Some (builder, creationErrors, None) - | _ -> None // the builder wasn't ready + | Some (_, checkResults) -> return Some (builder, creationErrors, Some (FSharpCheckFileAnswer.Succeeded checkResults)) + | _ -> return Some (builder, creationErrors, None) + | _ -> return None // the builder wasn't ready + } match cachedResults with | None -> return None @@ -2526,8 +2507,11 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CheckFileInProjectAllowingStaleCachedResults.CacheMiss", filename) let! tcPrior = execWithReactorAsync <| fun ctok -> + cancellable { DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok - builder.GetCheckResultsBeforeFileInProjectEvenIfStale filename + return builder.GetCheckResultsBeforeFileInProjectEvenIfStale filename + } + match tcPrior with | Some tcPrior -> let! checkResults = bc.CheckOneFileImpl(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) @@ -2574,7 +2558,7 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC use _unwind = decrement match builderOpt with | None -> - let parseResults = FSharpParseFileResults(List.toArray creationErrors, None, true, []) + let parseResults = FSharpParseFileResults(List.toArray creationErrors, None, true, [| |]) return (parseResults, FSharpCheckFileAnswer.Aborted) | Some builder -> @@ -2589,9 +2573,8 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC let! tcPrior = execWithReactorAsync <| fun ctok -> builder.GetCheckResultsBeforeFileInProject (ctok, filename) // Do the parsing. - let! parseErrors, _matchPairs, inputOpt, anyErrors = - execWithReactorAsync <| fun ctok -> - Parser.ParseOneFile (ctok, source, false, true, filename, builder.SourceFiles, builder.TcConfig) |> cancellable.Return + let parsingOptions = FSharpParsingOptions.FromTcConfig(builder.TcConfig, Array.ofList (builder.SourceFiles)) + let parseErrors, inputOpt, anyErrors = Parser.parseFile (source, filename, parsingOptions, userOpName) let parseResults = FSharpParseFileResults(parseErrors, inputOpt, anyErrors, builder.AllDependenciesDeprecated) let! checkResults = bc.CheckOneFileImpl(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) @@ -2608,14 +2591,15 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC use _unwind = decrement match builderOpt with | None -> - let parseResults = FSharpParseFileResults(Array.ofList creationErrors, None, true, []) + let parseResults = FSharpParseFileResults(Array.ofList creationErrors, None, true, [| |]) let typedResults = MakeCheckFileResultsEmpty(filename, creationErrors) return (parseResults, typedResults) | Some builder -> let! (inputOpt, _, _, untypedErrors) = builder.GetParseResultsForFile (ctok, filename) let! tcProj = builder.GetCheckResultsAfterFileInProject (ctok, filename) - let untypedErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (builder.TcConfig, false, filename, untypedErrors) |] - let tcErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (builder.TcConfig, false, filename, tcProj.Errors) |] + let errorOptions = builder.TcConfig.errorSeverityOptions + let untypedErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, untypedErrors) |] + let tcErrors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, false, filename, tcProj.Errors) |] let parseResults = FSharpParseFileResults(errors = untypedErrors, input = inputOpt, parseHadErrors = false, dependencyFiles = builder.AllDependenciesDeprecated) let loadClosure = scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.TryGet (ltok, options) ) let scope = @@ -2624,8 +2608,9 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC List.last tcProj.TcResolutions, List.last tcProj.TcSymbolUses, tcProj.TcEnvAtEnd.NameEnv, - loadClosure, reactorOps, (fun () -> builder.IsAlive), None) - let typedResults = MakeCheckFileResults(filename, options, builder, scope, tcProj.TcDependencyFiles, creationErrors, parseResults.Errors, tcErrors) + loadClosure, reactorOps, (fun () -> builder.IsAlive), None, + tcProj.ImplementationFiles) + let typedResults = MakeCheckFileResults(filename, options, builder, scope, Array.ofList tcProj.TcDependencyFiles, creationErrors, parseResults.Errors, tcErrors) return (parseResults, typedResults) }) @@ -2635,10 +2620,10 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC match source with | Some sourceText -> parseCacheLock.AcquireLock (fun ltok -> - match parseAndCheckFileInProjectCache.TryGet(ltok,(filename,sourceText,options)) with + match checkFileInProjectCache.TryGet(ltok,(filename,sourceText,options)) with | Some (a,b,c,_) -> Some (a,b,c) | None -> None) - | None -> parseCacheLock.AcquireLock (fun ltok -> parseAndCheckFileInProjectCachePossiblyStale.TryGet(ltok,(filename,options))) + | None -> parseCacheLock.AcquireLock (fun ltok -> checkFileInProjectCachePossiblyStale.TryGet(ltok,(filename,options))) /// Parse and typecheck the whole project (the implementation, called recursively as project graph is evaluated) member private bc.ParseAndCheckProjectImpl(options, ctok, userOpName) : Cancellable = @@ -2650,8 +2635,10 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC return FSharpCheckProjectResults (options.ProjectFileName, keepAssemblyContents, Array.ofList creationErrors, None, reactorOps) | Some builder -> let! (tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt) = builder.GetCheckResultsAndImplementationsForProject(ctok) - let errors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (tcProj.TcConfig, true, Microsoft.FSharp.Compiler.TcGlobals.DummyFileNameForRangesWithoutASpecificLocation, tcProj.Errors) |] - return FSharpCheckProjectResults (options.ProjectFileName, keepAssemblyContents, errors, Some(tcProj.TcGlobals, tcProj.TcImports, tcProj.TcState.Ccu, tcProj.TcState.PartialAssemblySignature, tcProj.TcSymbolUses, tcProj.TopAttribs, tcAssemblyDataOpt, ilAssemRef, tcProj.TcEnvAtEnd.AccessRights, tcAssemblyExprOpt, tcProj.TcDependencyFiles), reactorOps) + let errorOptions = tcProj.TcConfig.errorSeverityOptions + let fileName = TcGlobals.DummyFileNameForRangesWithoutASpecificLocation + let errors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (errorOptions, true, fileName, tcProj.Errors) |] + return FSharpCheckProjectResults (options.ProjectFileName, keepAssemblyContents, errors, Some(tcProj.TcGlobals, tcProj.TcImports, tcProj.TcState.Ccu, tcProj.TcState.PartialAssemblySignature, tcProj.TcSymbolUses, tcProj.TopAttribs, tcAssemblyDataOpt, ilAssemRef, tcProj.TcEnvAtEnd.AccessRights, tcAssemblyExprOpt, Array.ofList tcProj.TcDependencyFiles), reactorOps) } /// Get the timestamp that would be on the output if fully built immediately @@ -2787,9 +2774,9 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC member bc.ClearCachesAsync (userOpName) = reactor.EnqueueAndAwaitOpAsync (userOpName, "ClearCachesAsync", "", fun ctok -> parseCacheLock.AcquireLock (fun ltok -> - parseAndCheckFileInProjectCachePossiblyStale.Clear ltok - parseAndCheckFileInProjectCache.Clear ltok - parseFileInProjectCache.Clear ltok) + checkFileInProjectCachePossiblyStale.Clear ltok + checkFileInProjectCache.Clear ltok + parseFileCache.Clear(ltok)) incrementalBuildersCache.Clear ctok frameworkTcImportsCache.Clear ctok scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.Clear ltok) @@ -2798,9 +2785,9 @@ type BackgroundCompiler(legacyReferenceResolver, projectCacheSize, keepAssemblyC member bc.DownsizeCaches(userOpName) = reactor.EnqueueAndAwaitOpAsync (userOpName, "DownsizeCaches", "", fun ctok -> parseCacheLock.AcquireLock (fun ltok -> - parseAndCheckFileInProjectCachePossiblyStale.Resize(ltok, keepStrongly=1) - parseAndCheckFileInProjectCache.Resize(ltok, keepStrongly=1) - parseFileInProjectCache.Resize(ltok, keepStrongly=1)) + checkFileInProjectCachePossiblyStale.Resize(ltok, keepStrongly=1) + checkFileInProjectCache.Resize(ltok, keepStrongly=1) + parseFileCache.Resize(ltok, keepStrongly=1)) incrementalBuildersCache.Resize(ctok, keepStrongly=1, keepMax=1) frameworkTcImportsCache.Downsize(ctok) scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.Resize(ltok,keepStrongly=1, keepMax=1)) @@ -2830,10 +2817,7 @@ type FSharpChecker(legacyReferenceResolver, projectCacheSize, keepAssemblyConten // background UI thread, not on the compiler thread. // // This cache is safe for concurrent access because there is no onDiscard action for the items in the cache. - let braceMatchCache = - MruCache(braceMatchCacheSize, - areSame=AreSameForParsing3, - areSimilar=AreSubsumable3) + let braceMatchCache = MruCache(braceMatchCacheSize, areSimilar = AreSimilarForParsing, areSame = AreSameForParsing) let mutable maxMemoryReached = false let mutable maxMB = maxMBDefault @@ -2854,22 +2838,38 @@ type FSharpChecker(legacyReferenceResolver, projectCacheSize, keepAssemblyConten member ic.ReferenceResolver = legacyReferenceResolver - member ic.MatchBraces(filename, source, options, ?userOpName: string) = + member ic.MatchBraces(filename, source, options: FSharpParsingOptions, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" - async { - match braceMatchCache.TryGet (AssumeAnyCallerThreadWithoutEvidence(), (filename, source, options)) with + async { + match braceMatchCache.TryGet(AssumeAnyCallerThreadWithoutEvidence(), (filename, source, options)) with | Some res -> return res - | None -> - let! res = backgroundCompiler.MatchBraces(filename, source, options, userOpName) - braceMatchCache.Set (AssumeAnyCallerThreadWithoutEvidence(), (filename, source, options), res) - return res - } + | None -> + let res = Parser.matchBraces(source, filename, options, userOpName) + braceMatchCache.Set(AssumeAnyCallerThreadWithoutEvidence(), (filename, source, options), res) + return res + } - member ic.ParseFileInProject(filename, source, options, ?userOpName: string) = + member ic.GetParsingOptionsFromProjectOptions(options): FSharpParsingOptions * _ = + let sourceFiles = List.ofArray options.SourceFiles + let argv = List.ofArray options.OtherOptions + ic.GetParsingOptionsFromCommandLineArgs(sourceFiles, argv) + + member ic.MatchBraces(filename, source, options: FSharpProjectOptions, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + let parsingOptions, _ = ic.GetParsingOptionsFromProjectOptions(options) + ic.MatchBraces(filename, source, parsingOptions, userOpName) + + member ic.ParseFile(filename, source, options, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" ic.CheckMaxMemoryReached() - backgroundCompiler.ParseFileInProject(filename, source, options, userOpName) - + backgroundCompiler.ParseFile(filename, source, options, userOpName) + + + member ic.ParseFileInProject(filename, source, options, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + let parsingOptions, _ = ic.GetParsingOptionsFromProjectOptions(options) + ic.ParseFile(filename, source, parsingOptions, userOpName) + member ic.GetBackgroundParseResultsForFileInProject (filename,options, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.GetBackgroundParseResultsForFileInProject(filename, options, userOpName) @@ -3055,6 +3055,17 @@ type FSharpChecker(legacyReferenceResolver, projectCacheSize, keepAssemblyConten ExtraProjectInfo=extraProjectInfo Stamp = None } + member ic.GetParsingOptionsFromCommandLineArgs(initialSourceFiles, argv) = + use errorScope = new ErrorScope() + let tcConfigBuilder = TcConfigBuilder.Initial + + // Apply command-line arguments and collect more source files if they are in the arguments + let sourceFilesNew = ApplyCommandLineArgs(tcConfigBuilder, initialSourceFiles, argv) + FSharpParsingOptions.FromTcConfigBuidler(tcConfigBuilder, Array.ofList sourceFilesNew), errorScope.Diagnostics + + member ic.GetParsingOptionsFromCommandLineArgs(argv) = + ic.GetParsingOptionsFromCommandLineArgs([], argv) + /// Begin background parsing the given project. member ic.StartBackgroundCompile(options, ?userOpName) = let userOpName = defaultArg userOpName "Unknown" @@ -3124,9 +3135,9 @@ type FsiInteractiveChecker(legacyReferenceResolver, reactorOps: IReactorOperatio let userOpName = defaultArg userOpName "Unknown" let filename = Path.Combine(tcConfig.implicitIncludeDir, "stdin.fsx") // Note: projectSourceFiles is only used to compute isLastCompiland, and is ignored if Build.IsScript(mainInputFileName) is true (which it is in this case). - let projectSourceFiles = [ ] - let parseErrors, _matchPairs, inputOpt, anyErrors = Parser.ParseOneFile (ctok, source, false, true, filename, projectSourceFiles, tcConfig) - let dependencyFiles = [] // interactions have no dependencies + let parsingOptions = FSharpParsingOptions.FromTcConfig(tcConfig, [| filename |]) + let parseErrors, inputOpt, anyErrors = Parser.parseFile (source, filename, parsingOptions, userOpName) + let dependencyFiles = [| |] // interactions have no dependencies let parseResults = FSharpParseFileResults(parseErrors, inputOpt, parseHadErrors = anyErrors, dependencyFiles = dependencyFiles) let backgroundDiagnostics = [] @@ -3140,11 +3151,11 @@ type FsiInteractiveChecker(legacyReferenceResolver, reactorOps: IReactorOperatio let loadClosure = LoadClosure.ComputeClosureOfSourceText(ctok, legacyReferenceResolver, defaultFSharpBinariesDir, filename, source, CodeContext.Editing, tcConfig.useSimpleResolution, tcConfig.useFsiAuxLib, new Lexhelp.LexResourceManager(), applyCompilerOptions, assumeDotNetFramework) let! tcErrors, tcFileResult = Parser.CheckOneFile(parseResults, source, filename, "project", tcConfig, tcGlobals, tcImports, tcState, Some loadClosure, backgroundDiagnostics, reactorOps, (fun () -> true), None, userOpName) - return + return match tcFileResult with | Parser.TypeCheckAborted.No scope -> let errors = [| yield! parseErrors; yield! tcErrors |] - let typeCheckResults = FSharpCheckFileResults (filename, errors, Some scope, dependencyFiles, None, reactorOps) + let typeCheckResults = FSharpCheckFileResults (filename, errors, Some scope, dependencyFiles, None, reactorOps, false) let projectResults = FSharpCheckProjectResults (filename, keepAssemblyContents, errors, Some(tcGlobals, tcImports, scope.ThisCcu, scope.CcuSig, [scope.ScopeSymbolUses], None, None, mkSimpleAssRef "stdin", tcState.TcEnvFromImpls.AccessRights, None, dependencyFiles), reactorOps) parseResults, typeCheckResults, projectResults | _ -> @@ -3167,21 +3178,9 @@ module CompilerEnvironment = let DefaultReferencesForOrphanSources(assumeDotNetFramework) = DefaultReferencesForScriptsAndOutOfProjectSources(assumeDotNetFramework) /// Publish compiler-flags parsing logic. Must be fast because its used by the colorizer. - let GetCompilationDefinesForEditing(filename:string, compilerFlags : string list) = - let defines = ref(SourceFileImpl.AdditionalDefinesForUseInEditor(filename)) - let MatchAndExtract(flag:string,prefix:string) = - if flag.StartsWith(prefix) then - let sub = flag.Substring(prefix.Length) - let trimmed = sub.Trim() - defines := trimmed :: !defines - let rec QuickParseDefines = function - | hd :: tail -> - MatchAndExtract(hd,"-d:") - MatchAndExtract(hd,"--define:") - QuickParseDefines tail - | _ -> () - QuickParseDefines compilerFlags - !defines + let GetCompilationDefinesForEditing(filename:string, parsingOptions: FSharpParsingOptions) = + SourceFileImpl.AdditionalDefinesForUseInEditor(filename) @ + parsingOptions.ConditionalCompilationDefines /// Return true if this is a subcategory of error or warning message that the language service can emit let IsCheckerSupportedSubcategory(subcategory:string) = diff --git a/src/fsharp/vs/service.fsi b/src/fsharp/vs/service.fsi index 2d55ebd91f7..986b9a0e4c8 100755 --- a/src/fsharp/vs/service.fsi +++ b/src/fsharp/vs/service.fsi @@ -122,7 +122,7 @@ type internal FSharpCheckFileResults = /// Indicates the set of files which must be watched to accurately track changes that affect these results, /// Clients interested in reacting to updates to these files should watch these files and take actions as described /// in the documentation for compiler service. - member DependencyFiles : string list + member DependencyFiles : string[] /// Get the items for a declaration list /// @@ -262,6 +262,9 @@ type internal FSharpCheckFileResults = /// An optional string used for tracing compiler operations associated with this request. member internal IsRelativeNameResolvable: cursorPos : pos * plid : string list * item: Item * ?userOpName: string -> Async + /// Represents complete typechecked implementation files, including thier typechecked signatures if any. + member ImplementationFiles: FSharpImplementationFileContents list option + /// A handle to the results of CheckFileInProject. [] #if COMPILER_PUBLIC_API @@ -294,7 +297,7 @@ type internal FSharpCheckProjectResults = /// Indicates the set of files which must be watched to accurately track changes that affect these results, /// Clients interested in reacting to updates to these files should watch these files and take actions as described /// in the documentation for compiler service. - member DependencyFiles: string list + member DependencyFiles: string[] /// Unused in this API #if COMPILER_PUBLIC_API @@ -303,6 +306,22 @@ type UnresolvedReferencesSet type internal UnresolvedReferencesSet #endif +/// Options used to determine active --define conditionals and other options relevant to parsing files in a project +#if COMPILER_PUBLIC_API +type FSharpParsingOptions = +#else +type internal FSharpParsingOptions = +#endif + { + SourceFiles: string[] + ConditionalCompilationDefines: string list + ErrorSeverityOptions: FSharpErrorSeverityOptions + LightSyntax: bool option + CompilingFsLib: bool + IsExe: bool + } + static member Default: FSharpParsingOptions + /// A set of information describing a project or script build configuration. #if COMPILER_PUBLIC_API type FSharpProjectOptions = @@ -385,9 +404,32 @@ type internal FSharpChecker = /// /// The filename for the file, used to help caching of results. /// The full source for the file. - /// The options for the project or script, used to determine active --define conditionals and other options relevant to parsing. + /// Parsing options for the project or script. + /// An optional string used for tracing compiler operations associated with this request. + member MatchBraces: filename: string * source: string * options: FSharpParsingOptions * ?userOpName: string -> Async<(range * range)[]> + + /// + /// Parse a source code file, returning information about brace matching in the file. + /// Return an enumeration of the matching parenthetical tokens in the file. + /// + /// + /// The filename for the file, used to help caching of results. + /// The full source for the file. + /// Parsing options for the project or script. /// An optional string used for tracing compiler operations associated with this request. - member MatchBraces : filename : string * source: string * options: FSharpProjectOptions * ?userOpName: string -> Async<(range * range)[]> + [] + member MatchBraces: filename: string * source: string * options: FSharpProjectOptions * ?userOpName: string -> Async<(range * range)[]> + + /// + /// Parse a source code file, returning a handle that can be used for obtaining navigation bar information + /// To get the full information, call 'CheckFileInProject' method on the result + /// + /// + /// The filename for the file. + /// The full source for the file. + /// Parsing options for the project or script. + /// An optional string used for tracing compiler operations associated with this request. + member ParseFile: filename: string * source: string * options: FSharpParsingOptions * ?userOpName: string -> Async /// /// Parse a source code file, returning a handle that can be used for obtaining navigation bar information @@ -399,7 +441,8 @@ type internal FSharpChecker = /// The full source for the file. /// The options for the project or script, used to determine active --define conditionals and other options relevant to parsing. /// An optional string used for tracing compiler operations associated with this request. - member ParseFileInProject : filename: string * source: string * options: FSharpProjectOptions * ?userOpName: string -> Async + [] + member ParseFileInProject: filename: string * source: string * options: FSharpProjectOptions * ?userOpName: string -> Async /// /// Check a source code file, returning a handle to the results of the parse including @@ -412,7 +455,7 @@ type internal FSharpChecker = /// /// /// - /// The results of ParseFileInProject for this file. + /// The results of ParseFile for this file. /// The name of the file in the project whose source is being checked. /// An integer that can be used to indicate the version of the file. This will be returned by TryGetRecentCheckResultsForFile when looking up the file. /// The full source for the file. @@ -423,6 +466,7 @@ type internal FSharpChecker = /// can be used to marginally increase accuracy of intellisense results in some situations. /// /// An optional string used for tracing compiler operations associated with this request. + [] member CheckFileInProjectAllowingStaleCachedResults : parsed: FSharpParseFileResults * filename: string * fileversion: int * source: string * options: FSharpProjectOptions * ?textSnapshotInfo: obj * ?userOpName: string -> Async /// @@ -437,7 +481,7 @@ type internal FSharpChecker = /// /// /// - /// The results of ParseFileInProject for this file. + /// The results of ParseFile for this file. /// The name of the file in the project whose source is being checked. /// An integer that can be used to indicate the version of the file. This will be returned by TryGetRecentCheckResultsForFile when looking up the file. /// The full source for the file. @@ -515,9 +559,31 @@ type internal FSharpChecker = /// so that an 'unload' and 'reload' action will cause the script to be considered as a new project, /// so that references are re-resolved. member GetProjectOptionsFromCommandLineArgs : projectFileName: string * argv: string[] * ?loadedTimeStamp: DateTime * ?extraProjectInfo: obj -> FSharpProjectOptions - + + /// + /// Get the FSharpParsingOptions implied by a set of command line arguments and list of source files. + /// + /// + /// Initial source files list. Additional files may be added during argv evaluation. + /// The command line arguments for the project build. + member GetParsingOptionsFromCommandLineArgs: sourceFiles: string list * argv: string list -> FSharpParsingOptions * FSharpErrorInfo list + + /// + /// Get the FSharpParsingOptions implied by a set of command line arguments. + /// + /// + /// The command line arguments for the project build. + member GetParsingOptionsFromCommandLineArgs: argv: string list -> FSharpParsingOptions * FSharpErrorInfo list + + /// + /// Get the FSharpParsingOptions implied by a FSharpProjectOptions. + /// + /// + /// The command line arguments for the project build. + member GetParsingOptionsFromProjectOptions: FSharpProjectOptions -> FSharpParsingOptions * FSharpErrorInfo list + /// - /// Like ParseFileInProject, but uses results from the background builder. + /// Like ParseFile, but uses results from the background builder. /// All files are read from the FileSystem API, including the file being checked. /// /// @@ -527,7 +593,7 @@ type internal FSharpChecker = member GetBackgroundParseResultsForFileInProject : filename : string * options : FSharpProjectOptions * ?userOpName: string -> Async /// - /// Like ParseFileInProject, but uses the existing results from the background builder. + /// Like CheckFileInProject, but uses the existing results from the background builder. /// All files are read from the FileSystem API, including the file being checked. /// /// @@ -703,7 +769,7 @@ module internal CompilerEnvironment = /// are not associated with a project. val DefaultReferencesForOrphanSources : assumeDotNetFramework: bool -> string list /// Return the compilation defines that should be used when editing the given file. - val GetCompilationDefinesForEditing : filename : string * compilerFlags : string list -> string list + val GetCompilationDefinesForEditing : filename : string * parsingOptions : FSharpParsingOptions -> string list /// Return true if this is a subcategory of error or warning message that the language service can emit val IsCheckerSupportedSubcategory : string -> bool diff --git a/tests/service/Common.fs b/tests/service/Common.fs index 9f5e5676177..ef3049110e6 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -192,8 +192,8 @@ let parseSourceCode (name: string, code: string) = let filePath = Path.Combine(location, name + ".fs") let dllPath = Path.Combine(location, name + ".dll") let args = mkProjectCommandLineArgs(dllPath, [filePath]) - let options = checker.GetProjectOptionsFromCommandLineArgs(projPath, args) - let parseResults = checker.ParseFileInProject(filePath, code, options) |> Async.RunSynchronously + let options, errors = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) + let parseResults = checker.ParseFile(filePath, code, options) |> Async.RunSynchronously parseResults.ParseTree /// Extract range info diff --git a/tests/service/PerfTests.fs b/tests/service/PerfTests.fs index f7510bb224e..164d6cde324 100644 --- a/tests/service/PerfTests.fs +++ b/tests/service/PerfTests.fs @@ -35,7 +35,8 @@ module internal Project1 = let fileNames = [ for (_,f) in fileNamesI -> f ] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let parsingOptions, _ = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) [] @@ -47,7 +48,7 @@ let ``Test request for parse and check doesn't check whole project`` () = checker.FileParsed.Add (fun x -> incr backgroundParseCount) let pB, tB = FSharpChecker.GlobalForegroundParseCountStatistic, FSharpChecker.GlobalForegroundTypeCheckCountStatistic - let parseResults1 = checker.ParseFileInProject(Project1.fileNames.[5], Project1.fileSources2.[5], Project1.options) |> Async.RunSynchronously + let parseResults1 = checker.ParseFile(Project1.fileNames.[5], Project1.fileSources2.[5], Project1.parsingOptions) |> Async.RunSynchronously let pC, tC = FSharpChecker.GlobalForegroundParseCountStatistic, FSharpChecker.GlobalForegroundTypeCheckCountStatistic (pC - pB) |> shouldEqual 1 (tC - tB) |> shouldEqual 0 diff --git a/tests/service/ProjectAnalysisTests.fs b/tests/service/ProjectAnalysisTests.fs index 18da534ddeb..a5b188a415c 100644 --- a/tests/service/ProjectAnalysisTests.fs +++ b/tests/service/ProjectAnalysisTests.fs @@ -87,7 +87,8 @@ let mmmm2 : M.CAbbrev = new M.CAbbrev() // note, these don't count as uses of C let fileNames = [fileName1; fileName2] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let parsingOptions, _ = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) let cleanFileName a = if a = fileName1 then "file1" else if a = fileName2 then "file2" else "??" [] @@ -534,8 +535,8 @@ let ``Test file explicit parse symbols`` () = let wholeProjectResults = checker.ParseAndCheckProject(Project1.options) |> Async.RunSynchronously - let parseResults1 = checker.ParseFileInProject(Project1.fileName1, Project1.fileSource1, Project1.options) |> Async.RunSynchronously - let parseResults2 = checker.ParseFileInProject(Project1.fileName2, Project1.fileSource2, Project1.options) |> Async.RunSynchronously + let parseResults1 = checker.ParseFile(Project1.fileName1, Project1.fileSource1, Project1.parsingOptions) |> Async.RunSynchronously + let parseResults2 = checker.ParseFile(Project1.fileName2, Project1.fileSource2, Project1.parsingOptions) |> Async.RunSynchronously let checkResults1 = checker.CheckFileInProject(parseResults1, Project1.fileName1, 0, Project1.fileSource1, Project1.options) @@ -580,8 +581,8 @@ let ``Test file explicit parse all symbols`` () = let wholeProjectResults = checker.ParseAndCheckProject(Project1.options) |> Async.RunSynchronously - let parseResults1 = checker.ParseFileInProject(Project1.fileName1, Project1.fileSource1, Project1.options) |> Async.RunSynchronously - let parseResults2 = checker.ParseFileInProject(Project1.fileName2, Project1.fileSource2, Project1.options) |> Async.RunSynchronously + let parseResults1 = checker.ParseFile(Project1.fileName1, Project1.fileSource1, Project1.parsingOptions) |> Async.RunSynchronously + let parseResults2 = checker.ParseFile(Project1.fileName2, Project1.fileSource2, Project1.parsingOptions) |> Async.RunSynchronously let checkResults1 = checker.CheckFileInProject(parseResults1, Project1.fileName1, 0, Project1.fileSource1, Project1.options) @@ -4532,26 +4533,26 @@ let ``Test project35b Dependency files for ParseAndCheckFileInProject`` () = | _ -> failwithf "Parsing aborted unexpectedly..." for d in checkFileResults.DependencyFiles do printfn "ParseAndCheckFileInProject dependency: %s" d - checkFileResults.DependencyFiles |> List.exists (fun s -> s.Contains "notexist.dll") |> shouldEqual true + checkFileResults.DependencyFiles |> Array.exists (fun s -> s.Contains "notexist.dll") |> shouldEqual true // The file itself is not a dependency since it is never read from the file system when using ParseAndCheckFileInProject - checkFileResults.DependencyFiles |> List.exists (fun s -> s.Contains Project35b.fileName1) |> shouldEqual false + checkFileResults.DependencyFiles |> Array.exists (fun s -> s.Contains Project35b.fileName1) |> shouldEqual false [] let ``Test project35b Dependency files for GetBackgroundCheckResultsForFileInProject`` () = let _,checkFileResults = checker.GetBackgroundCheckResultsForFileInProject(Project35b.fileName1, Project35b.options) |> Async.RunSynchronously for d in checkFileResults.DependencyFiles do printfn "GetBackgroundCheckResultsForFileInProject dependency: %s" d - checkFileResults.DependencyFiles |> List.exists (fun s -> s.Contains "notexist.dll") |> shouldEqual true + checkFileResults.DependencyFiles |> Array.exists (fun s -> s.Contains "notexist.dll") |> shouldEqual true // The file is a dependency since it is read from the file system when using GetBackgroundCheckResultsForFileInProject - checkFileResults.DependencyFiles |> List.exists (fun s -> s.Contains Project35b.fileName1) |> shouldEqual true + checkFileResults.DependencyFiles |> Array.exists (fun s -> s.Contains Project35b.fileName1) |> shouldEqual true [] let ``Test project35b Dependency files for check of project`` () = let checkResults = checker.ParseAndCheckProject(Project35b.options) |> Async.RunSynchronously for d in checkResults.DependencyFiles do printfn "ParseAndCheckProject dependency: %s" d - checkResults.DependencyFiles |> List.exists (fun s -> s.Contains "notexist.dll") |> shouldEqual true - checkResults.DependencyFiles |> List.exists (fun s -> s.Contains Project35b.fileName1) |> shouldEqual true + checkResults.DependencyFiles |> Array.exists (fun s -> s.Contains "notexist.dll") |> shouldEqual true + checkResults.DependencyFiles |> Array.exists (fun s -> s.Contains Project35b.fileName1) |> shouldEqual true //------------------------------------------------------ @@ -5012,7 +5013,8 @@ module internal ProjectBig = let fileNames = [ for (_,f) in fileNamesI -> f ] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let parsingOptions, _ = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) [] @@ -5025,7 +5027,7 @@ let ``Test request for parse and check doesn't check whole project`` () = checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() let pB, tB = FSharpChecker.GlobalForegroundParseCountStatistic, FSharpChecker.GlobalForegroundTypeCheckCountStatistic - let parseResults1 = checker.ParseFileInProject(ProjectBig.fileNames.[5], ProjectBig.fileSources2.[5], ProjectBig.options) |> Async.RunSynchronously + let parseResults1 = checker.ParseFile(ProjectBig.fileNames.[5], ProjectBig.fileSources2.[5], ProjectBig.parsingOptions) |> Async.RunSynchronously let pC, tC = FSharpChecker.GlobalForegroundParseCountStatistic, FSharpChecker.GlobalForegroundTypeCheckCountStatistic (pC - pB) |> shouldEqual 1 (tC - tB) |> shouldEqual 0 @@ -5174,3 +5176,45 @@ let ``Test line directives in foreground analysis`` () = // see https://github.c [ for e in checkResults1.Errors -> e.StartLineAlternate, e.EndLineAlternate, e.FileName ] |> shouldEqual [(4, 4, ProjectLineDirectives.fileName1)] +//------------------------------------------------------ + +[] +let ``ParseAndCheckFileResults contains ImplFile list if FSharpChecker is created with keepAssemblyContent flag set to true``() = + + let fileName1 = Path.ChangeExtension(Path.GetTempFileName(), ".fs") + let base2 = Path.GetTempFileName() + let dllName = Path.ChangeExtension(base2, ".dll") + let projFileName = Path.ChangeExtension(base2, ".fsproj") + let fileSource1 = """ +type A(i:int) = + member x.Value = i +""" + File.WriteAllText(fileName1, fileSource1) + + let fileNames = [fileName1] + let args = mkProjectCommandLineArgs (dllName, fileNames) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) + let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + + let fileCheckResults = + keepAssemblyContentsChecker.ParseAndCheckFileInProject(fileName1, 0, fileSource1, options) |> Async.RunSynchronously + |> function + | _, FSharpCheckFileAnswer.Succeeded(res) -> res + | _ -> failwithf "Parsing aborted unexpectedly..." + + let declarations = + match fileCheckResults.ImplementationFiles with + | Some (implFile :: _) -> + match implFile.Declarations |> List.tryHead with + | Some (FSharpImplementationFileDeclaration.Entity (_, subDecls)) -> subDecls + | _ -> failwith "unexpected declaration" + | Some [] | None -> failwith "File check results does not contain any `ImplementationFile`s" + + match declarations |> List.tryHead with + | Some (FSharpImplementationFileDeclaration.Entity(entity, [])) -> + entity.DisplayName |> shouldEqual "A" + let memberNames = entity.MembersFunctionsAndValues |> Seq.map (fun x -> x.DisplayName) |> Set.ofSeq + Assert.That(memberNames, Contains.Item "Value") + + | Some decl -> failwithf "unexpected declaration %A" decl + | None -> failwith "declaration list is empty" diff --git a/vsintegration/Utils/LanguageServiceProfiling/Program.fs b/vsintegration/Utils/LanguageServiceProfiling/Program.fs index a64218f3e4a..bc785b787cd 100644 --- a/vsintegration/Utils/LanguageServiceProfiling/Program.fs +++ b/vsintegration/Utils/LanguageServiceProfiling/Program.fs @@ -156,7 +156,8 @@ let main argv = let! fileResults = checkFile fileVersion match fileResults with | Some fileResults -> - let! parseResult = checker.ParseFileInProject(options.FileToCheck, getFileText(), options.Options) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions(options.Options) + let! parseResult = checker.ParseFile(options.FileToCheck, getFileText(), parsingOptions) for completion in options.CompletionPositions do eprintfn "querying %A %s" completion.QualifyingNames completion.PartialName let! listInfo = diff --git a/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs b/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs index b40aaa4d0a2..b60ee3b9bcc 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs @@ -40,9 +40,9 @@ type internal FSharpColorizationService asyncMaybe { do Trace.TraceInformation("{0:n3} (start) SemanticColorization", DateTime.Now.TimeOfDay.TotalSeconds) do! Async.Sleep DefaultTuning.SemanticColorizationInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time - let! options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = false, userOpName=userOpName) + let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, allowStaleResults = false, userOpName=userOpName) // it's crucial to not return duplicated or overlapping `ClassifiedSpan`s because Find Usages service crashes. let targetRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) let colorizationData = checkResults.GetSemanticClassification (Some targetRange) |> Array.distinctBy fst diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 92622f4fe7e..76f88464e7a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -96,12 +96,12 @@ type internal FSharpAddOpenCodeFixProvider override __.RegisterCodeFixesAsync context : Task = asyncMaybe { let document = context.Document - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true, sourceText = sourceText, userOpName = userOpName) + let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText = sourceText, userOpName = userOpName) let line = sourceText.Lines.GetLineFromPosition(context.Span.End) let linePos = sourceText.Lines.GetLinePosition(context.Span.End) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions) let! symbol = asyncMaybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index a37be23a58e..5cf5fcba215 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -139,12 +139,12 @@ type internal FSharpImplementInterfaceCodeFixProvider override __.RegisterCodeFixesAsync context : Task = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject context.Document + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject context.Document let cancellationToken = context.CancellationToken let! sourceText = context.Document.GetTextAsync(cancellationToken) - let! _, parsedInput, checkFileResults = checker.ParseAndCheckDocument(context.Document, options, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) + let! _, parsedInput, checkFileResults = checker.ParseAndCheckDocument(context.Document, projectOptions, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition context.Span.Start - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(context.Document.FilePath, options.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(context.Document.FilePath, parsingOptions) // Notice that context.Span doesn't return reliable ranges to find tokens at exact positions. // That's why we tokenize the line and try to find the last successive identifier token let tokens = Tokenizer.tokenizeLine(context.Document.Id, sourceText, context.Span.Start, context.Document.FilePath, defines) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs index fc6c71fb3a9..5e97220d08c 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs @@ -32,8 +32,8 @@ type internal FSharpRemoveUnusedOpensCodeFixProvider let document = context.Document let! sourceText = document.GetTextAsync() let checker = checkerProvider.Checker - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) - let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, options, checker) + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, projectOptions, checker) let changes = unusedOpens |> List.map (fun m -> diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index 7b544ca866a..710bd2244c8 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -54,10 +54,10 @@ type internal FSharpRenameUnusedValueCodeFixProvider // We have to use the additional check for backtickes because `IsOperatorOrBacktickedName` operates on display names // where backtickes are replaced with parens. if not (PrettyNaming.IsOperatorOrBacktickedName ident) && not (ident.StartsWith "``") then - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document - let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true, sourceText = sourceText, userOpName=userOpName) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document + let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText = sourceText, userOpName=userOpName) let m = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing (document.FilePath, options.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing (document.FilePath, parsingOptions) let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, context.Span.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false) let lineText = (sourceText.Lines.GetLineFromPosition context.Span.Start).ToString() let! symbolUse = checkResults.GetSymbolUseAtLocation(m.StartLine, m.EndColumn, lineText, lexerSymbol.FullIsland, userOpName=userOpName) diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index 2c7ece30131..99bcd091c91 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -99,13 +99,13 @@ type internal FSharpHelpContextService member this.GetHelpTermAsync(document, textSpan, cancellationToken) = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! textVersion = document.GetTextVersionAsync(cancellationToken) let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) let textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start) let tokens = Tokenizer.getColorizationData(document.Id, sourceText, textLine.Span, Some document.Name, defines, cancellationToken) - return! FSharpHelpContextService.GetHelpTerm(checkerProvider.Checker, sourceText, document.FilePath, options, textSpan, tokens, textVersion.GetHashCode()) + return! FSharpHelpContextService.GetHelpTerm(checkerProvider.Checker, sourceText, document.FilePath, projectOptions, textSpan, tokens, textVersion.GetHashCode()) } |> Async.map (Option.defaultValue "") |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs index 2af89506561..9d5c2990aec 100644 --- a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs @@ -67,9 +67,9 @@ type internal XmlDocCommandFilter // XmlDocable line #1 are 1-based, editor is 0-based let curLineNum = wpfTextView.Caret.Position.BufferPosition.GetContainingLine().LineNumber + 1 let! document = document.Value - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let sourceText = wpfTextView.TextBuffer.CurrentSnapshot.GetText() - let! parsedInput = checker.ParseDocument(document, options, sourceText, userOpName) + let! parsedInput = checker.ParseDocument(document, parsingOptions, sourceText, userOpName) let xmlDocables = XmlDocParser.getXmlDocables (sourceText, Some parsedInput) let xmlDocablesBelowThisLine = // +1 because looking below current line for e.g. a 'member' diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index f77988f2702..e30c0322b47 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -210,15 +210,15 @@ type internal FSharpCompletionProvider let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) do! Option.guard (CompletionUtils.shouldProvideCompletion(document.Id, document.FilePath, defines, sourceText, context.Position)) - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! textVersion = context.Document.GetTextVersionAsync(context.CancellationToken) - let! _, _, fileCheckResults = checker.ParseAndCheckDocument(document, options, true, userOpName=userOpName) + let! _, _, fileCheckResults = checker.ParseAndCheckDocument(document, projectOptions, true, userOpName=userOpName) let getAllSymbols() = if Settings.IntelliSense.ShowAllSymbols then assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies(fileCheckResults) else [] let! results = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, sourceText, context.Position, options, + FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, sourceText, context.Position, projectOptions, document.FilePath, textVersion.GetHashCode(), getAllSymbols) context.AddItems(results) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask context.CancellationToken @@ -268,8 +268,8 @@ type internal FSharpCompletionProvider let! sourceText = document.GetTextAsync(cancellationToken) let textWithItemCommitted = sourceText.WithChanges(TextChange(item.Span, nameInCode)) let line = sourceText.Lines.GetLineFromPosition(item.Span.Start) - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) - let! parsedInput = checker.ParseDocument(document, options, sourceText, userOpName) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsedInput = checker.ParseDocument(document, parsingOptions, sourceText, userOpName) let fullNameIdents = fullName |> Option.map (fun x -> x.Split '.') |> Option.defaultValue [||] let insertionPoint = diff --git a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs index 22310a9bf43..852fa7bfd56 100644 --- a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs +++ b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs @@ -196,7 +196,7 @@ type internal FSharpSignatureHelpProvider member this.GetItemsAsync(document, position, triggerInfo, cancellationToken) = asyncMaybe { try - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! textVersion = document.GetTextVersionAsync(cancellationToken) @@ -206,7 +206,7 @@ type internal FSharpSignatureHelpProvider else None let! (results,applicableSpan,argumentIndex,argumentCount,argumentName) = - FSharpSignatureHelpProvider.ProvideMethodsAsyncAux(checkerProvider.Checker, documentationBuilder, sourceText, position, options, triggerTypedChar, document.FilePath, textVersion.GetHashCode()) + FSharpSignatureHelpProvider.ProvideMethodsAsyncAux(checkerProvider.Checker, documentationBuilder, sourceText, position, projectOptions, triggerTypedChar, document.FilePath, textVersion.GetHashCode()) let items = results |> Array.map (fun (hasParamArrayArg, doc, prefixParts, separatorParts, suffixParts, parameters, descriptionParts) -> diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index 864a77e44aa..97bf920d70a 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -27,13 +27,8 @@ type internal FSharpBreakpointResolutionService ) = static let userOpName = "BreakpointResolution" - static member GetBreakpointLocation(checker: FSharpChecker, sourceText: SourceText, fileName: string, textSpan: TextSpan, options: FSharpProjectOptions) = + static member GetBreakpointLocation(checker: FSharpChecker, sourceText: SourceText, fileName: string, textSpan: TextSpan, parsingOptions: FSharpParsingOptions) = async { - // REVIEW: ParseFileInProject can cause FSharp.Compiler.Service to become unavailable (i.e. not responding to requests) for - // an arbitrarily long time while it parses all files prior to this one in the project (plus dependent projects if we enable - // cross-project checking in multi-project solutions). FCS will not respond to other - // requests unless this task is cancelled. We need to check that this task is cancelled in a timely way by the - // Roslyn UI machinery. let textLinePos = sourceText.Lines.GetLinePosition(textSpan.Start) let textInLine = sourceText.GetSubText(sourceText.Lines.[textLinePos.Line].Span).ToString() @@ -42,16 +37,16 @@ type internal FSharpBreakpointResolutionService else let textLineColumn = textLinePos.Character let fcsTextLineNumber = Line.fromZ textLinePos.Line // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based - let! parseResults = checker.ParseFileInProject(fileName, sourceText.ToString(), options, userOpName = userOpName) + let! parseResults = checker.ParseFile(fileName, sourceText.ToString(), parsingOptions, userOpName = userOpName) return parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) } interface IBreakpointResolutionService with member this.ResolveBreakpointAsync(document: Document, textSpan: TextSpan, cancellationToken: CancellationToken): Task = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! range = FSharpBreakpointResolutionService.GetBreakpointLocation(checkerProvider.Checker, sourceText, document.Name, textSpan, options) + let! range = FSharpBreakpointResolutionService.GetBreakpointLocation(checkerProvider.Checker, sourceText, document.Name, textSpan, parsingOptions) let! span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) return BreakpointResolutionResult.CreateSpanResult(document, span) } diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 412496efa35..ffcf5c6ef83 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -57,9 +57,9 @@ type internal FSharpDocumentDiagnosticAnalyzer() = hash } - static member GetDiagnostics(checker: FSharpChecker, filePath: string, sourceText: SourceText, textVersionHash: int, options: FSharpProjectOptions, diagnosticType: DiagnosticsType) = + static member GetDiagnostics(checker: FSharpChecker, filePath: string, sourceText: SourceText, textVersionHash: int, parsingOptions: FSharpParsingOptions, options: FSharpProjectOptions, diagnosticType: DiagnosticsType) = async { - let! parseResults = checker.ParseFileInProject(filePath, sourceText.ToString(), options, userOpName=userOpName) + let! parseResults = checker.ParseFile(filePath, sourceText.ToString(), parsingOptions, userOpName=userOpName) let! errors = async { match diagnosticType with @@ -109,11 +109,11 @@ type internal FSharpDocumentDiagnosticAnalyzer() = override this.AnalyzeSyntaxAsync(document: Document, cancellationToken: CancellationToken): Task> = let projectInfoManager = getProjectInfoManager document asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! textVersion = document.GetTextVersionAsync(cancellationToken) return! - FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), options, DiagnosticsType.Syntax) + FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Syntax) |> liftAsync } |> Async.map (Option.defaultValue ImmutableArray.Empty) @@ -122,11 +122,11 @@ type internal FSharpDocumentDiagnosticAnalyzer() = override this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken): Task> = let projectInfoManager = getProjectInfoManager document asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! textVersion = document.GetTextVersionAsync(cancellationToken) return! - FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), options, DiagnosticsType.Semantic) + FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Semantic) |> liftAsync } |> Async.map (Option.defaultValue ImmutableArray.Empty) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs index 225a32d29e7..b0b25ca6ead 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs @@ -51,7 +51,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = do! Option.guard Settings.CodeFixes.SimplifyName do Trace.TraceInformation("{0:n3} (start) SimplifyName", DateTime.Now.TimeOfDay.TotalSeconds) do! Async.Sleep DefaultTuning.SimplifyNameInitialDelay |> liftAsync - let! options = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) + let! _parsingOptions, projectOptions = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) let! textVersion = document.GetTextVersionAsync(cancellationToken) let textVersionHash = textVersion.GetHashCode() let! _ = guard.WaitAsync(cancellationToken) |> Async.AwaitTask |> liftAsync @@ -61,7 +61,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = | _ -> let! sourceText = document.GetTextAsync() let checker = getChecker document - let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true, userOpName=userOpName) + let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, allowStaleResults = true, userOpName=userOpName) let! symbolUses = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync let mutable result = ResizeArray() let symbolUses = diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index a0466dd4ff4..cb541cac322 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -103,10 +103,10 @@ type internal UnusedDeclarationsAnalyzer() = do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) do! Async.Sleep DefaultTuning.UnusedDeclarationsAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time match getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) with - | Some options -> + | Some (_parsingOptions, projectOptions) -> let! sourceText = document.GetTextAsync() let checker = getChecker document - let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) + let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) let! allSymbolUsesInFile = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync let unusedRanges = getUnusedDeclarationRanges allSymbolUsesInFile (isScriptFile document.FilePath) return diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index 58226fb8f1e..0dcb5c9da5d 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -173,10 +173,10 @@ type internal UnusedOpensDiagnosticAnalyzer() = asyncMaybe { do Trace.TraceInformation("{0:n3} (start) UnusedOpensAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) do! Async.Sleep DefaultTuning.UnusedOpensAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time - let! options = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) + let! _parsingOptions, projectOptions = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync() let checker = getChecker document - let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, options, checker) + let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, projectOptions, checker) return unusedOpens diff --git a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs index e4829fd4764..354c41759dd 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -76,12 +76,12 @@ type internal FSharpDocumentHighlightsService [] (checkerP interface IDocumentHighlightsService with member __.GetDocumentHighlightsAsync(document, position, _documentsToSearch, cancellationToken) : Task> = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! textVersion = document.GetTextVersionAsync(cancellationToken) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions) let! spans = FSharpDocumentHighlightsService.GetDocumentHighlights(checkerProvider.Checker, document.Id, sourceText, document.FilePath, - position, defines, options, textVersion.GetHashCode()) + position, defines, projectOptions, textVersion.GetHashCode()) let highlightSpans = spans |> Array.map (fun span -> diff --git a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs index 700768a20c9..7b795f0cc82 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs @@ -18,9 +18,9 @@ type internal FSharpBraceMatchingService static let defaultUserOpName = "BraceMatching" - static member GetBraceMatchingResult(checker: FSharpChecker, sourceText, fileName, options, position: int, userOpName: string) = + static member GetBraceMatchingResult(checker: FSharpChecker, sourceText, fileName, parsingOptions: FSharpParsingOptions, position: int, userOpName: string) = async { - let! matchedBraces = checker.MatchBraces(fileName, sourceText.ToString(), options, userOpName) + let! matchedBraces = checker.MatchBraces(fileName, sourceText.ToString(), parsingOptions, userOpName) let isPositionInRange range = match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) with | None -> false @@ -33,9 +33,9 @@ type internal FSharpBraceMatchingService interface IBraceMatcher with member this.FindBracesAsync(document, position, cancellationToken) = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! (left, right) = FSharpBraceMatchingService.GetBraceMatchingResult(checkerProvider.Checker, sourceText, document.Name, options, position, defaultUserOpName) + let! (left, right) = FSharpBraceMatchingService.GetBraceMatchingResult(checkerProvider.Checker, sourceText, document.Name, parsingOptions, position, defaultUserOpName) let! leftSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, left) let! rightSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, right) return BraceMatchingResult(leftSpan, rightSpan) diff --git a/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs b/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs index 0abbd0abddf..d4b1b24ebf6 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs @@ -23,7 +23,7 @@ type internal FSharpEditorFormattingService projectInfoManager: FSharpProjectOptionsManager ) = - static member GetFormattingChanges(documentId: DocumentId, sourceText: SourceText, filePath: string, checker: FSharpChecker, indentStyle: FormattingOptions.IndentStyle, projectOptions: FSharpProjectOptions option, position: int) = + static member GetFormattingChanges(documentId: DocumentId, sourceText: SourceText, filePath: string, checker: FSharpChecker, indentStyle: FormattingOptions.IndentStyle, options: (FSharpParsingOptions * FSharpProjectOptions) option, position: int) = // Logic for determining formatting changes: // If first token on the current line is a closing brace, // match the indent with the indent on the line that opened it @@ -34,11 +34,11 @@ type internal FSharpEditorFormattingService // (this is what C# does) do! Option.guard (indentStyle = FormattingOptions.IndentStyle.Smart) - let! projectOptions = projectOptions + let! parsingOptions, _projectOptions = options let line = sourceText.Lines.[sourceText.Lines.IndexOf position] - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(filePath, projectOptions.OtherOptions |> List.ofArray) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(filePath, parsingOptions) let tokens = Tokenizer.tokenizeLine(documentId, sourceText, line.Start, filePath, defines) @@ -50,7 +50,7 @@ type internal FSharpEditorFormattingService x.Tag <> FSharpTokenTag.LINE_COMMENT) let! (left, right) = - FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, filePath, projectOptions, position, "FormattingService") + FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, filePath, parsingOptions, position, "FormattingService") if right.StartColumn = firstMeaningfulToken.LeftColumn then // Replace the indentation on this line with the indentation of the left bracket diff --git a/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs b/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs index 4fa71c024fb..23afe5131ce 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs @@ -23,7 +23,7 @@ type internal FSharpIndentationService static member IsSmartIndentEnabled (options: Microsoft.CodeAnalysis.Options.OptionSet) = options.GetOption(FormattingOptions.SmartIndent, FSharpConstants.FSharpLanguageName) = FormattingOptions.IndentStyle.Smart - static member GetDesiredIndentation(documentId: DocumentId, sourceText: SourceText, filePath: string, lineNumber: int, tabSize: int, indentStyle: FormattingOptions.IndentStyle, projectOptions: FSharpProjectOptions option): Option = + static member GetDesiredIndentation(documentId: DocumentId, sourceText: SourceText, filePath: string, lineNumber: int, tabSize: int, indentStyle: FormattingOptions.IndentStyle, options: (FSharpParsingOptions * FSharpProjectOptions) option): Option = // Match indentation with previous line let rec tryFindPreviousNonEmptyLine l = @@ -36,8 +36,8 @@ type internal FSharpIndentationService tryFindPreviousNonEmptyLine (l - 1) let rec tryFindLastNonWhitespaceOrCommentToken (line: TextLine) = maybe { - let! projectOptions = projectOptions - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(filePath, projectOptions.OtherOptions |> Seq.toList) + let! parsingOptions, _projectOptions = options + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(filePath, parsingOptions) let tokens = Tokenizer.tokenizeLine(documentId, sourceText, line.Start, filePath, defines) return! diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index d2f804ebe55..89e9d6a8f2d 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -167,10 +167,10 @@ type internal InlineRenameService interface IEditorInlineRenameService with member __.GetRenameInfoAsync(document: Document, position: int, cancellationToken: CancellationToken) : Task = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) - return! InlineRenameService.GetInlineRenameInfo(checkerProvider.Checker, projectInfoManager, document, sourceText, position, defines, options) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions) + return! InlineRenameService.GetInlineRenameInfo(checkerProvider.Checker, projectInfoManager, document, sourceText, position, defines, projectOptions) } |> Async.map (Option.defaultValue FailureInlineRenameInfo.Instance) |> RoslynHelpers.StartAsyncAsTask(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs index 7eb2616f283..2d6b877fee9 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs @@ -15,14 +15,14 @@ type CheckResults = | StillRunning of Async<(FSharpParseFileResults * FSharpCheckFileResults) option> type FSharpChecker with - member checker.ParseDocument(document: Document, options: FSharpProjectOptions, sourceText: string, userOpName: string) = + member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, sourceText: string, userOpName: string) = asyncMaybe { - let! fileParseResults = checker.ParseFileInProject(document.FilePath, sourceText, options, userOpName=userOpName) |> liftAsync + let! fileParseResults = checker.ParseFile(document.FilePath, sourceText, parsingOptions, userOpName=userOpName) |> liftAsync return! fileParseResults.ParseTree } - member checker.ParseDocument(document: Document, options: FSharpProjectOptions, sourceText: SourceText, userOpName: string) = - checker.ParseDocument(document, options, sourceText=sourceText.ToString(), userOpName=userOpName) + member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, sourceText: SourceText, userOpName: string) = + checker.ParseDocument(document, parsingOptions, sourceText=sourceText.ToString(), userOpName=userOpName) member checker.ParseAndCheckDocument(filePath: string, textVersionHash: int, sourceText: string, options: FSharpProjectOptions, allowStaleResults: bool, userOpName: string) = let parseAndCheckFile = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 29658d69a56..311e7ba928c 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -101,11 +101,11 @@ type internal FSharpProjectOptionsManager ) = // A table of information about projects, excluding single-file projects. - let projectTable = ConcurrentDictionary>() + let projectTable = ConcurrentDictionary>() // A table of information about single-file projects. Currently we only need the load time of each such file, plus // the original options for editing - let singleFileProjectTable = ConcurrentDictionary() + let singleFileProjectTable = ConcurrentDictionary() // Accumulate sources and references for each project file let projectInfo = new ConcurrentDictionary() @@ -128,7 +128,7 @@ type internal FSharpProjectOptionsManager member this.RefreshInfoForProjectsThatReferenceThisProject(projectId: ProjectId) = // Search the projectTable for things to refresh - for KeyValue(otherProjectId, ((referencedProjectIds, _options), refresh)) in projectTable.ToArray() do + for KeyValue(otherProjectId, ((referencedProjectIds, _parsingOptions, _options), refresh)) in projectTable.ToArray() do for referencedProjectId in referencedProjectIds do if referencedProjectId = projectId then projectTable.[otherProjectId] <- (refresh true, refresh) @@ -143,7 +143,7 @@ type internal FSharpProjectOptionsManager /// Get the exact options for a single-file script member this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, fileContents, workspace: Workspace) = async { let extraProjectInfo = Some(box workspace) - let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject + let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject |> Option.map snd if SourceFile.MustBeSingleFileProject(fileName) then // NOTE: we don't use a unique stamp for single files, instead comparing options structurally. // This is because we repeatedly recompute the options. @@ -153,37 +153,42 @@ type internal FSharpProjectOptionsManager // compiled and #r will refer to files on disk let referencedProjectFileNames = [| |] let site = ProjectSitesAndFiles.CreateProjectSiteForScript(fileName, referencedProjectFileNames, options) - return ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject,site,fileName,options.ExtraProjectInfo,serviceProvider, true) + let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject,site,fileName,options.ExtraProjectInfo,serviceProvider, true) + let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) + return (deps, parsingOptions, projectOptions) else let site = ProjectSitesAndFiles.ProjectSiteOfSingleFile(fileName) - return ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject,site,fileName,extraProjectInfo,serviceProvider, true) + let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject,site,fileName,extraProjectInfo,serviceProvider, true) + let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) + return (deps, parsingOptions, projectOptions) } /// Update the info for a project in the project table member this.UpdateProjectInfo(tryGetOrCreateProjectId, projectId: ProjectId, site: IProjectSite, userOpName) = this.AddOrUpdateProject(projectId, (fun isRefresh -> let extraProjectInfo = Some(box workspace) - let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject - let referencedProjects, options = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, site.ProjectFileName(), extraProjectInfo, serviceProvider, true) + let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject |> Option.map snd + let referencedProjects, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, site.ProjectFileName(), extraProjectInfo, serviceProvider, true) let referencedProjectIds = referencedProjects |> Array.choose tryGetOrCreateProjectId - checkerProvider.Checker.InvalidateConfiguration(options, startBackgroundCompileIfAlreadySeen = not isRefresh, userOpName= userOpName + ".UpdateProjectInfo") - referencedProjectIds, options)) + checkerProvider.Checker.InvalidateConfiguration(projectOptions, startBackgroundCompileIfAlreadySeen = not isRefresh, userOpName= userOpName + ".UpdateProjectInfo") + let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) + referencedProjectIds, parsingOptions, projectOptions)) /// Get compilation defines relevant for syntax processing. /// Quicker then TryGetOptionsForDocumentOrProject as it doesn't need to recompute the exact project /// options for a script. member this.GetCompilationDefinesForEditingDocument(document: Document) = let projectOptionsOpt = this.TryGetOptionsForProject(document.Project.Id) - let otherOptions = + let parsingOptions = match projectOptionsOpt with - | None -> [] - | Some options -> options.OtherOptions |> Array.toList - CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, otherOptions) + | None -> FSharpParsingOptions.Default + | Some (parsingOptions, _projectOptions) -> parsingOptions + CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions) /// Get the options for a project member this.TryGetOptionsForProject(projectId: ProjectId) = match projectTable.TryGetValue(projectId) with - | true, ((_referencedProjects, options), _) -> Some options + | true, ((_referencedProjects, parsingOptions, projectOptions), _) -> Some (parsingOptions, projectOptions) | _ -> None /// Get the exact options for a document or project @@ -194,7 +199,7 @@ type internal FSharpProjectOptionsManager // single-file project may contain #load and #r references which are changing as the user edits, and we may need to re-analyze // to determine the latest settings. FCS keeps a cache to help ensure these are up-to-date. match singleFileProjectTable.TryGetValue(projectId) with - | true, (loadTime, _) -> + | true, (loadTime, _, _) -> try let fileName = document.FilePath let! cancellationToken = Async.CancellationToken @@ -202,9 +207,9 @@ type internal FSharpProjectOptionsManager // NOTE: we don't use FCS cross-project references from scripts to projects. The projects must have been // compiled and #r will refer to files on disk. let tryGetOrCreateProjectId _ = None - let! _referencedProjectFileNames, options = this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, sourceText.ToString(), document.Project.Solution.Workspace) - this.AddOrUpdateSingleFileProject(projectId, (loadTime, options)) - return Some options + let! _referencedProjectFileNames, parsingOptions, projectOptions = this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, sourceText.ToString(), document.Project.Solution.Workspace) + this.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions)) + return Some (parsingOptions, projectOptions) with ex -> Assert.Exception(ex) return None @@ -216,7 +221,7 @@ type internal FSharpProjectOptionsManager member this.TryGetOptionsForEditingDocumentOrProject(document: Document) = let projectId = document.Project.Id match singleFileProjectTable.TryGetValue(projectId) with - | true, (_loadTime, originalOptions) -> Some originalOptions + | true, (_loadTime, parsingOptions, originalOptions) -> Some (parsingOptions, originalOptions) | _ -> this.TryGetOptionsForProject(projectId) member this.ProvideProjectSiteProvider(project:Project) = @@ -301,6 +306,8 @@ type internal FSharpProjectOptionsManager | true, value -> value | _ -> [||], [||], [||] + member __.Checker = checkerProvider.Checker + // Used to expose FSharpChecker/ProjectInfo manager to diagnostic providers // Diagnostic providers can be executed in environment that does not use MEF so they can rely only // on services exposed by the workspace @@ -574,8 +581,8 @@ type let projectDisplayName = projectDisplayNameOf projectFileName let projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName) - let _referencedProjectFileNames, options = projectInfoManager.ComputeSingleFileOptions (tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents, workspace) |> Async.RunSynchronously - projectInfoManager.AddOrUpdateSingleFileProject(projectId, (loadTime, options)) + let _referencedProjectFileNames, parsingOptions, projectOptions = projectInfoManager.ComputeSingleFileOptions (tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents, workspace) |> Async.RunSynchronously + projectInfoManager.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions)) if isNull (workspace.ProjectTracker.GetProject projectId) then let projectContextFactory = package.ComponentModel.GetService(); diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index dfe76098b80..15995d015f0 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -30,10 +30,10 @@ module internal SymbolHelpers = let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions) let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document.FilePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true, userOpName = userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document.FilePath, textVersionHash, sourceText.ToString(), projectOptions, allowStaleResults = true, userOpName = userOpName) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, userOpName=userOpName) let! symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) |> liftAsync return symbolUses @@ -59,8 +59,8 @@ module internal SymbolHelpers = |> Seq.map (fun project -> async { match projectInfoManager.TryGetOptionsForProject(project.Id) with - | Some options -> - let! projectCheckResults = checker.ParseAndCheckProject(options, userOpName = userOpName) + | Some (_parsingOptions, projectOptions) -> + let! projectCheckResults = checker.ParseAndCheckProject(projectOptions, userOpName = userOpName) return! projectCheckResults.GetUsesOfSymbol(symbol) | None -> return [||] }) @@ -103,10 +103,10 @@ module internal SymbolHelpers = let! sourceText = document.GetTextAsync(cancellationToken) let originalText = sourceText.ToString(symbolSpan) do! Option.guard (originalText.Length > 0) - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions) let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, symbolSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true, userOpName = userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition(symbolSpan.Start) let textLinePos = sourceText.Lines.GetLinePosition(symbolSpan.Start) let fcsTextLineNumber = Line.fromZ textLinePos.Line diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index b964f1d2eff..e3479737db0 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -51,11 +51,11 @@ type internal FSharpFindUsagesService asyncMaybe { let! sourceText = document.GetTextAsync(context.CancellationToken) |> Async.AwaitTask |> liftAsync let checker = checkerProvider.Checker - let! options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition(position).ToString() let lineNumber = sourceText.Lines.GetLinePosition(position).Line + 1 - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, options.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, parsingOptions) let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, userOpName=userOpName) @@ -112,8 +112,8 @@ type internal FSharpFindUsagesService projectsToCheck |> Seq.map (fun project -> asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForProject(project.Id) - let! projectCheckResults = checker.ParseAndCheckProject(options, userOpName = userOpName) |> liftAsync + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForProject(project.Id) + let! projectCheckResults = checker.ParseAndCheckProject(projectOptions, userOpName = userOpName) |> liftAsync return! projectCheckResults.GetUsesOfSymbol(symbolUse.Symbol) |> liftAsync } |> Async.map (Option.defaultValue [||])) |> Async.Parallel diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index 9925058fcfd..7707b8a2554 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -57,8 +57,8 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP /// Helper function that is used to determine the navigation strategy to apply, can be tuned towards signatures or implementation files. let findSymbolHelper (originDocument: Document, originRange: range, sourceText: SourceText, preferSignature: bool) : Async = asyncMaybe { - let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject originDocument - let defines = CompilerEnvironment.GetCompilationDefinesForEditing (originDocument.FilePath, projectOptions.OtherOptions |> Seq.toList) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject originDocument + let defines = CompilerEnvironment.GetCompilationDefinesForEditing (originDocument.FilePath, parsingOptions) let! originTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (sourceText, originRange) let position = originTextSpan.Start let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position, originDocument.FilePath, defines, SymbolLookupKind.Greedy, false) @@ -67,7 +67,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument,projectOptions,allowStaleResults=true,sourceText=sourceText, userOpName = userOpName) + let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument, projectOptions, allowStaleResults=true,sourceText=sourceText, userOpName = userOpName) let idRange = lexerSymbol.Ident.idRange let! fsSymbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland, userOpName=userOpName) let symbol = fsSymbolUse.Symbol @@ -79,7 +79,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP if not (File.Exists fsfilePath) then return! None else let! implDoc = originDocument.Project.Solution.TryGetDocumentFromPath fsfilePath let! implSourceText = implDoc.GetTextAsync () - let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject implDoc + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject implDoc let! _, _, checkFileResults = checker.ParseAndCheckDocument (implDoc, projectOptions, allowStaleResults=true, sourceText=implSourceText, userOpName = userOpName) let! symbolUses = checkFileResults.GetUsesOfSymbolInFile symbol |> liftAsync let! implSymbol = symbolUses |> Array.tryHead @@ -295,9 +295,9 @@ type internal FSharpGoToDefinitionService /// at the provided position in the document. member __.FindDefinitionsTask(originDocument: Document, position: int, cancellationToken: CancellationToken) = asyncMaybe { - let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject originDocument + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject originDocument let! sourceText = originDocument.GetTextAsync () |> liftTaskAsync - let defines = CompilerEnvironment.GetCompilationDefinesForEditing (originDocument.FilePath, projectOptions.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing (originDocument.FilePath, parsingOptions) let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line @@ -382,7 +382,7 @@ type internal FSharpGoToDefinitionService let! implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath let! implVersion = implDocument.GetTextVersionAsync () |> liftTaskAsync let! implSourceText = implDocument.GetTextAsync () |> liftTaskAsync - let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject implDocument + let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject implDocument let! targetRange = gotoDefinition.FindSymbolDeclarationInFile(targetSymbolUse, implFilePath, implSourceText.ToString(), projectOptions, implVersion.GetHashCode()) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index d84202eac73..a1cee3bff49 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -187,11 +187,11 @@ type internal FSharpNavigateToSearchService let itemsByDocumentId = ConditionalWeakTable() - let getNavigableItems(document: Document, options: FSharpProjectOptions) = + let getNavigableItems(document: Document, parsingOptions: FSharpParsingOptions) = async { let! cancellationToken = Async.CancellationToken let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask - let! parseResults = checkerProvider.Checker.ParseFileInProject(document.FilePath, sourceText.ToString(), options) + let! parseResults = checkerProvider.Checker.ParseFile(document.FilePath, sourceText.ToString(), parsingOptions) return match parseResults.ParseTree |> Option.map NavigateTo.getNavigableItems with | Some items -> @@ -206,7 +206,7 @@ type internal FSharpNavigateToSearchService | None -> [||] } - let getCachedIndexedNavigableItems(document: Document, options: FSharpProjectOptions) = + let getCachedIndexedNavigableItems(document: Document, parsingOptions: FSharpParsingOptions) = async { let! cancellationToken = Async.CancellationToken let! textVersion = document.GetTextVersionAsync(cancellationToken) |> Async.AwaitTask @@ -215,7 +215,7 @@ type internal FSharpNavigateToSearchService | true, (oldTextVersionHash, items) when oldTextVersionHash = textVersionHash -> return items | _ -> - let! items = getNavigableItems(document, options) + let! items = getNavigableItems(document, parsingOptions) let indexedItems = Index.build items itemsByDocumentId.Remove(document.Id) |> ignore itemsByDocumentId.Add(document.Id, (textVersionHash, indexedItems)) @@ -233,10 +233,10 @@ type internal FSharpNavigateToSearchService interface INavigateToSearchService with member __.SearchProjectAsync(project, searchPattern, cancellationToken) : Task> = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForProject(project.Id) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForProject(project.Id) let! items = project.Documents - |> Seq.map (fun document -> getCachedIndexedNavigableItems(document, options)) + |> Seq.map (fun document -> getCachedIndexedNavigableItems(document, parsingOptions)) |> Async.Parallel |> liftAsync @@ -265,8 +265,8 @@ type internal FSharpNavigateToSearchService member __.SearchDocumentAsync(document, searchPattern, cancellationToken) : Task> = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) - let! items = getCachedIndexedNavigableItems(document, options) |> liftAsync + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) + let! items = getCachedIndexedNavigableItems(document, parsingOptions) |> liftAsync return items.Find(searchPattern) } |> Async.map (Option.defaultValue [||]) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs index 7e0fdac0def..34fad074bac 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs @@ -32,9 +32,9 @@ type internal FSharpNavigationBarItemService interface INavigationBarItemService with member __.GetItemsAsync(document, cancellationToken) : Task> = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! parsedInput = checkerProvider.Checker.ParseDocument(document, options, sourceText=sourceText, userOpName=userOpName) + let! parsedInput = checkerProvider.Checker.ParseDocument(document, parsingOptions, sourceText=sourceText, userOpName=userOpName) let navItems = NavigationImpl.getNavigation parsedInput let rangeToTextSpan range = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) return diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index 1324602ffb2..8e4a6fa82a0 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -55,8 +55,8 @@ module private FSharpQuickInfo = let extLineText = (extSourceText.Lines.GetLineFromPosition extSpan.Start).ToString() // project options need to be retrieved because the signature file could be in another project - let! extProjectOptions = projectInfoManager.TryGetOptionsForProject extDocId.ProjectId - let extDefines = CompilerEnvironment.GetCompilationDefinesForEditing (extDocument.FilePath, List.ofSeq extProjectOptions.OtherOptions) + let! extParsingOptions, extProjectOptions = projectInfoManager.TryGetOptionsForProject extDocId.ProjectId + let extDefines = CompilerEnvironment.GetCompilationDefinesForEditing (extDocument.FilePath, extParsingOptions) let! extLexerSymbol = Tokenizer.getSymbolAtPosition(extDocId, extSourceText, extSpan.Start, declRange.FileName, extDefines, SymbolLookupKind.Greedy, true) let! _, _, extCheckFileResults = checker.ParseAndCheckDocument(extDocument, extProjectOptions, allowStaleResults=true, sourceText=extSourceText, userOpName = userOpName) @@ -91,8 +91,8 @@ module private FSharpQuickInfo = asyncMaybe { let! sourceText = document.GetTextAsync cancellationToken - let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document - let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, projectOptions.OtherOptions |> Seq.toList) + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document + let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, parsingOptions) let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, true) let idRange = lexerSymbol.Ident.idRange let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText=sourceText, userOpName = userOpName) @@ -170,12 +170,12 @@ type internal FSharpQuickInfoProvider let xmlMemberIndexService = serviceProvider.GetService(typeof) :?> IVsXMLMemberIndexService let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService, serviceProvider.DTE) - static member ProvideQuickInfo(checker: FSharpChecker, documentId: DocumentId, sourceText: SourceText, filePath: string, position: int, options: FSharpProjectOptions, textVersionHash: int) = + static member ProvideQuickInfo(checker: FSharpChecker, documentId: DocumentId, sourceText: SourceText, filePath: string, position: int, parsingOptions: FSharpParsingOptions, options: FSharpProjectOptions, textVersionHash: int) = asyncMaybe { let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true, userOpName = FSharpQuickInfo.userOpName) let textLine = sourceText.Lines.GetLineFromPosition position let textLineNumber = textLine.LineNumber + 1 // Roslyn line numbers are zero-based - let defines = CompilerEnvironment.GetCompilationDefinesForEditing (filePath, options.OtherOptions |> Seq.toList) + let defines = CompilerEnvironment.GetCompilationDefinesForEditing (filePath, parsingOptions) let! symbol = Tokenizer.getSymbolAtPosition (documentId, sourceText, position, filePath, defines, SymbolLookupKind.Precise, true) let! res = checkFileResults.GetStructuredToolTipText (textLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, FSharpTokenTag.IDENT, userOpName=FSharpQuickInfo.userOpName) |> liftAsync match res with diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index 68a41f19e6d..865165665a3 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -148,9 +148,9 @@ type internal FSharpBlockStructureService(checker: FSharpChecker, projectInfoMan override __.GetBlockStructureAsync(document, cancellationToken) : Task = asyncMaybe { - let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! parsedInput = checker.ParseDocument(document, options, sourceText, userOpName) + let! parsedInput = checker.ParseDocument(document, parsingOptions, sourceText, userOpName) return createBlockSpans sourceText parsedInput |> Seq.toImmutableArray } |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) diff --git a/vsintegration/src/FSharp.LanguageService/BackgroundRequests.fs b/vsintegration/src/FSharp.LanguageService/BackgroundRequests.fs index 69ac749e232..d0c3c38a874 100644 --- a/vsintegration/src/FSharp.LanguageService/BackgroundRequests.fs +++ b/vsintegration/src/FSharp.LanguageService/BackgroundRequests.fs @@ -14,6 +14,9 @@ open Microsoft.VisualStudio.Shell.Interop open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.SourceCodeServices + +#nowarn "44" // use of obsolete CheckFileInProjectAllowingStaleCachedResults + // // Note: DEPRECATED CODE ONLY ACTIVE IN UNIT TESTING VIA "UNROSLYNIZED" UNIT TESTS. // @@ -204,7 +207,12 @@ type internal FSharpLanguageServiceBackgroundRequests_DEPRECATED // Now that we have the parseResults, we can SetDependencyFiles(). // // If the set of dependencies changes, the file needs to be re-checked - let anyDependenciesChanged = source.SetDependencyFiles(parseResults.DependencyFiles) + let dependencyFiles = + match typedResults with + | None -> parseResults.DependencyFiles + | Some r -> r.DependencyFiles + + let anyDependenciesChanged = source.SetDependencyFiles(dependencyFiles) if anyDependenciesChanged then req.ResultClearsDirtinessOfFile <- false // Furthermore, if the project is out-of-date behave just as if we were notified dependency files changed. diff --git a/vsintegration/src/FSharp.LanguageService/FSharpSource.fs b/vsintegration/src/FSharp.LanguageService/FSharpSource.fs index f24edf88fc8..f270d3ba94a 100644 --- a/vsintegration/src/FSharp.LanguageService/FSharpSource.fs +++ b/vsintegration/src/FSharp.LanguageService/FSharpSource.fs @@ -66,7 +66,7 @@ type internal IFSharpSource_DEPRECATED = /// Store a ProjectSite for obtaining a task provider abstract ProjectSite : IProjectSite option with get,set /// Specify the files that should trigger a rebuild for the project behind this source - abstract SetDependencyFiles : string list -> bool + abstract SetDependencyFiles : string[] -> bool @@ -356,7 +356,7 @@ type internal FSharpSource_DEPRECATED(service:LanguageService_DEPRECATED, textLi |] // get a sync parse of the file - let co = + let co, _ = { ProjectFileName = fileName + ".dummy.fsproj" SourceFiles = [| fileName |] OtherOptions = flags @@ -368,8 +368,9 @@ type internal FSharpSource_DEPRECATED(service:LanguageService_DEPRECATED, textLi OriginalLoadReferences = [] ExtraProjectInfo=None Stamp = None } + |> ic.GetParsingOptionsFromProjectOptions - ic.ParseFileInProject(fileName, source.GetText(), co) |> Async.RunSynchronously + ic.ParseFile(fileName, source.GetText(), co) |> Async.RunSynchronously override source.GetCommentFormat() = let mutable info = new CommentInfo() diff --git a/vsintegration/src/FSharp.LanguageService/ProjectSitesAndFiles.fs b/vsintegration/src/FSharp.LanguageService/ProjectSitesAndFiles.fs index a7367999ab4..8f4ddaa8772 100644 --- a/vsintegration/src/FSharp.LanguageService/ProjectSitesAndFiles.fs +++ b/vsintegration/src/FSharp.LanguageService/ProjectSitesAndFiles.fs @@ -242,10 +242,10 @@ type internal ProjectSitesAndFiles() = | None -> None - member art.GetDefinesForFile_DEPRECATED(rdt:IVsRunningDocumentTable, filename : string) = + member art.GetDefinesForFile_DEPRECATED(rdt:IVsRunningDocumentTable, filename : string, checker:FSharpChecker) = // The only caller of this function calls it each time it needs to colorize a line, so this call must execute very fast. if SourceFile.MustBeSingleFileProject(filename) then - CompilerEnvironment.GetCompilationDefinesForEditing(filename,[]) + CompilerEnvironment.GetCompilationDefinesForEditing(filename,FSharpParsingOptions.Default) else let siteOpt = match VsRunningDocumentTable.FindDocumentWithoutLocking(rdt,filename) with @@ -257,7 +257,8 @@ type internal ProjectSitesAndFiles() = | Some site -> site | None -> ProjectSitesAndFiles.ProjectSiteOfSingleFile(filename) - CompilerEnvironment.GetCompilationDefinesForEditing(filename,site.CompilerFlags() |> Array.toList) + let parsingOptions,_ = checker.GetParsingOptionsFromCommandLineArgs(site.CompilerFlags() |> Array.toList) + CompilerEnvironment.GetCompilationDefinesForEditing(filename,parsingOptions) member art.TryFindOwningProject_DEPRECATED(rdt:IVsRunningDocumentTable, filename) = diff --git a/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs b/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs index 23b24f741d9..4e95bf58303 100644 --- a/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs +++ b/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs @@ -210,7 +210,7 @@ type internal FSharpLanguageServiceTestable() as this = // So this is not ideal from a perf perspective, but it is easy to reason about the correctness. let filename = VsTextLines.GetFilename buffer let rdt = this.ServiceProvider.RunningDocumentTable - let defines = this.ProjectSitesAndFiles.GetDefinesForFile_DEPRECATED(rdt, filename) + let defines = this.ProjectSitesAndFiles.GetDefinesForFile_DEPRECATED(rdt, filename, this.FSharpChecker) let sourceTokenizer = FSharpSourceTokenizer(defines,Some(filename)) sourceTokenizer.CreateLineTokenizer(source)) diff --git a/vsintegration/tests/unittests/BraceMatchingServiceTests.fs b/vsintegration/tests/unittests/BraceMatchingServiceTests.fs index 277ad012f43..8e3d4ee7c9a 100644 --- a/vsintegration/tests/unittests/BraceMatchingServiceTests.fs +++ b/vsintegration/tests/unittests/BraceMatchingServiceTests.fs @@ -17,7 +17,7 @@ open UnitTests.TestLib.LanguageService [][] type BraceMatchingServiceTests() = let fileName = "C:\\test.fs" - let options: FSharpProjectOptions = { + let projectOptions: FSharpProjectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| fileName |] ReferencedProjects = [| |] @@ -36,7 +36,8 @@ type BraceMatchingServiceTests() = let position = fileContents.IndexOf(marker) Assert.IsTrue(position >= 0, "Cannot find marker '{0}' in file contents", marker) - match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, options, position, "UnitTest") |> Async.RunSynchronously with + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, parsingOptions, position, "UnitTest") |> Async.RunSynchronously with | None -> () | Some(left, right) -> Assert.Fail("Found match for brace '{0}'", marker) @@ -48,7 +49,8 @@ type BraceMatchingServiceTests() = Assert.IsTrue(startMarkerPosition >= 0, "Cannot find start marker '{0}' in file contents", startMarkerPosition) Assert.IsTrue(endMarkerPosition >= 0, "Cannot find end marker '{0}' in file contents", endMarkerPosition) - match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, options, startMarkerPosition, "UnitTest") |> Async.RunSynchronously with + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, parsingOptions, startMarkerPosition, "UnitTest") |> Async.RunSynchronously with | None -> Assert.Fail("Didn't find a match for start brace at position '{0}", startMarkerPosition) | Some(left, right) -> let endPositionInRange(range) = @@ -169,9 +171,10 @@ let main argv = // https://github.com/Microsoft/visualfsharp/issues/2092 let sourceText = SourceText.From(fileContents) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions matchingPositions |> Array.iter (fun position -> - match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, options, position, "UnitTest") |> Async.RunSynchronously with + match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, parsingOptions, position, "UnitTest") |> Async.RunSynchronously with | Some _ -> () | None -> match position with diff --git a/vsintegration/tests/unittests/BreakpointResolutionService.fs b/vsintegration/tests/unittests/BreakpointResolutionService.fs index 3461d3009b1..c84c1b76f76 100644 --- a/vsintegration/tests/unittests/BreakpointResolutionService.fs +++ b/vsintegration/tests/unittests/BreakpointResolutionService.fs @@ -22,7 +22,7 @@ open UnitTests.TestLib.LanguageService type BreakpointResolutionServiceTests() = let fileName = "C:\\test.fs" - let options: FSharpProjectOptions = { + let projectOptions: FSharpProjectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| fileName |] ReferencedProjects = [| |] @@ -74,7 +74,8 @@ let main argv = let sourceText = SourceText.From(code) let searchSpan = TextSpan.FromBounds(searchPosition, searchPosition + searchToken.Length) - let actualResolutionOption = FSharpBreakpointResolutionService.GetBreakpointLocation(checker, sourceText, fileName, searchSpan, options) |> Async.RunSynchronously + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + let actualResolutionOption = FSharpBreakpointResolutionService.GetBreakpointLocation(checker, sourceText, fileName, searchSpan, parsingOptions) |> Async.RunSynchronously match actualResolutionOption with | None -> Assert.IsTrue(expectedResolution.IsNone, "BreakpointResolutionService failed to resolve breakpoint position") diff --git a/vsintegration/tests/unittests/CompletionProviderTests.fs b/vsintegration/tests/unittests/CompletionProviderTests.fs index a0de868fc75..e34ed22f0ea 100644 --- a/vsintegration/tests/unittests/CompletionProviderTests.fs +++ b/vsintegration/tests/unittests/CompletionProviderTests.fs @@ -35,7 +35,7 @@ open Microsoft.FSharp.Compiler.SourceCodeServices open UnitTests.TestLib.LanguageService let filePath = "C:\\test.fs" -let internal options = { +let internal projectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -52,7 +52,7 @@ let internal options = { let VerifyCompletionList(fileContents: string, marker: string, expected: string list, unexpected: string list) = let caretPosition = fileContents.IndexOf(marker) + marker.Length let results = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, options, filePath, 0, fun _ -> []) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, projectOptions, filePath, 0, fun _ -> []) |> Async.RunSynchronously |> Option.defaultValue (ResizeArray()) |> Seq.map(fun result -> result.DisplayText) @@ -67,7 +67,7 @@ let VerifyCompletionListExactly(fileContents: string, marker: string, expected: let caretPosition = fileContents.IndexOf(marker) + marker.Length let actual = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, options, filePath, 0, fun _ -> []) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, projectOptions, filePath, 0, fun _ -> []) |> Async.RunSynchronously |> Option.defaultValue (ResizeArray()) |> Seq.toList diff --git a/vsintegration/tests/unittests/DocumentDiagnosticAnalyzerTests.fs b/vsintegration/tests/unittests/DocumentDiagnosticAnalyzerTests.fs index 912738d5380..d7302afc296 100644 --- a/vsintegration/tests/unittests/DocumentDiagnosticAnalyzerTests.fs +++ b/vsintegration/tests/unittests/DocumentDiagnosticAnalyzerTests.fs @@ -24,7 +24,7 @@ type DocumentDiagnosticAnalyzerTests() = let filePath = "C:\\test.fs" let startMarker = "(*start*)" let endMarker = "(*end*)" - let options: FSharpProjectOptions = { + let projectOptions: FSharpProjectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -40,15 +40,17 @@ type DocumentDiagnosticAnalyzerTests() = let getDiagnostics (fileContents: string) = async { - let! syntacticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checker, filePath, SourceText.From(fileContents), 0, options, DiagnosticsType.Syntax) - let! semanticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checker, filePath, SourceText.From(fileContents), 0, options, DiagnosticsType.Semantic) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + let! syntacticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checker, filePath, SourceText.From(fileContents), 0, parsingOptions, projectOptions, DiagnosticsType.Syntax) + let! semanticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checker, filePath, SourceText.From(fileContents), 0, parsingOptions, projectOptions, DiagnosticsType.Semantic) return syntacticDiagnostics.AddRange(semanticDiagnostics) } |> Async.RunSynchronously member private this.VerifyNoErrors(fileContents: string, ?additionalFlags: string[]) = + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions let additionalOptions = match additionalFlags with - | None -> options - | Some(flags) -> {options with OtherOptions = Array.append options.OtherOptions flags} + | None -> projectOptions + | Some(flags) -> {projectOptions with OtherOptions = Array.append projectOptions.OtherOptions flags} let errors = getDiagnostics fileContents Assert.AreEqual(0, errors.Length, "There should be no errors generated") diff --git a/vsintegration/tests/unittests/DocumentHighlightsServiceTests.fs b/vsintegration/tests/unittests/DocumentHighlightsServiceTests.fs index 8b884c5f610..53ac885768a 100644 --- a/vsintegration/tests/unittests/DocumentHighlightsServiceTests.fs +++ b/vsintegration/tests/unittests/DocumentHighlightsServiceTests.fs @@ -37,7 +37,7 @@ open UnitTests.TestLib.LanguageService let filePath = "C:\\test.fs" -let internal options = { +let internal projectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -53,7 +53,7 @@ let internal options = { let private getSpans (sourceText: SourceText) (caretPosition: int) = let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - FSharpDocumentHighlightsService.GetDocumentHighlights(checker, documentId, sourceText, filePath, caretPosition, [], options, 0) + FSharpDocumentHighlightsService.GetDocumentHighlights(checker, documentId, sourceText, filePath, caretPosition, [], projectOptions, 0) |> Async.RunSynchronously |> Option.defaultValue [||] diff --git a/vsintegration/tests/unittests/EditorFormattingServiceTests.fs b/vsintegration/tests/unittests/EditorFormattingServiceTests.fs index 83cd4c381fd..7fa10f056c9 100644 --- a/vsintegration/tests/unittests/EditorFormattingServiceTests.fs +++ b/vsintegration/tests/unittests/EditorFormattingServiceTests.fs @@ -17,8 +17,8 @@ open Microsoft.CodeAnalysis.Formatting [] [] type EditorFormattingServiceTests() = - static let filePath = "C:\\test.fs" - static let options: FSharpProjectOptions = { + let filePath = "C:\\test.fs" + let projectOptions : FSharpProjectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -31,11 +31,12 @@ type EditorFormattingServiceTests() = ExtraProjectInfo = None Stamp = None } + //let parsingOptions: FSharpParsingOptions = - static let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - static let indentStyle = FormattingOptions.IndentStyle.Smart + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let indentStyle = FormattingOptions.IndentStyle.Smart - static let template = """ + let template = """ let foo = [ 15 ]marker1 @@ -66,8 +67,9 @@ let def = let sourceText = SourceText.From(template) let lineNumber = sourceText.Lines |> Seq.findIndex (fun line -> line.Span.Contains position) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - let changesOpt = FSharpEditorFormattingService.GetFormattingChanges(documentId, sourceText, filePath, checker, indentStyle, Some options, position) |> Async.RunSynchronously + let changesOpt = FSharpEditorFormattingService.GetFormattingChanges(documentId, sourceText, filePath, checker, indentStyle, Some (parsingOptions, projectOptions), position) |> Async.RunSynchronously match changesOpt with | None -> Assert.Fail("Expected a text change, but got None") | Some change -> diff --git a/vsintegration/tests/unittests/IndentationServiceTests.fs b/vsintegration/tests/unittests/IndentationServiceTests.fs index 9534fa99113..66f92ea534f 100644 --- a/vsintegration/tests/unittests/IndentationServiceTests.fs +++ b/vsintegration/tests/unittests/IndentationServiceTests.fs @@ -14,10 +14,12 @@ open Microsoft.VisualStudio.FSharp.Editor open Microsoft.FSharp.Compiler.SourceCodeServices open Microsoft.CodeAnalysis.Formatting +open UnitTests.TestLib.LanguageService + [][] type IndentationServiceTests() = - static let filePath = "C:\\test.fs" - static let options: FSharpProjectOptions = { + let filePath = "C:\\test.fs" + let projectOptions: FSharpProjectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -31,13 +33,13 @@ type IndentationServiceTests() = Stamp = None } - static let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - static let tabSize = 4 - static let indentStyle = FormattingOptions.IndentStyle.Smart + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let tabSize = 4 + let indentStyle = FormattingOptions.IndentStyle.Smart - static let indentComment = System.Text.RegularExpressions.Regex(@"\$\s*Indent:\s*(\d+)\s*\$") + let indentComment = System.Text.RegularExpressions.Regex(@"\$\s*Indent:\s*(\d+)\s*\$") - static let consoleProjectTemplate = " + let consoleProjectTemplate = " // Learn more about F# at http://fsharp.org // See the 'F# Tutorial' project for more help. @@ -46,20 +48,20 @@ let main argv = printfn \"%A\" argv 0 // return an integer exit code" - static let libraryProjectTemplate = " + let libraryProjectTemplate = " namespace ProjectNamespace type Class1() = member this.X = \"F#\"" - static let nestedTypesTemplate = " + let nestedTypesTemplate = " namespace testspace type testtype static member testmember = 1 " - static let autoIndentTemplate = " + let autoIndentTemplate = " let plus x y = x + y // $Indent: 4$ @@ -130,33 +132,33 @@ while true do // The follwing line should inherit that indentation too $Indent: 4$ " - static member private testCases: Object[][] = [| - [| None; 0; consoleProjectTemplate |] - [| None; 1; consoleProjectTemplate |] - [| Some(0); 2; consoleProjectTemplate |] - [| Some(0); 3; consoleProjectTemplate |] - [| Some(0); 4; consoleProjectTemplate |] - [| Some(0); 5; consoleProjectTemplate |] - [| Some(4); 6; consoleProjectTemplate |] - [| Some(4); 7; consoleProjectTemplate |] - [| Some(4); 8; consoleProjectTemplate |] + let testCases = [| + ( None, 0, consoleProjectTemplate ) + ( None, 1, consoleProjectTemplate ) + ( Some(0), 2, consoleProjectTemplate ) + ( Some(0), 3, consoleProjectTemplate ) + ( Some(0), 4, consoleProjectTemplate ) + ( Some(0), 5, consoleProjectTemplate ) + ( Some(4), 6, consoleProjectTemplate ) + ( Some(4), 7, consoleProjectTemplate ) + ( Some(4), 8, consoleProjectTemplate ) - [| None; 0; libraryProjectTemplate |] - [| None; 1; libraryProjectTemplate |] - [| Some(0); 2; libraryProjectTemplate |] - [| Some(0); 3; libraryProjectTemplate |] - [| Some(4); 4; libraryProjectTemplate |] - [| Some(4); 5; libraryProjectTemplate |] + ( None, 0, libraryProjectTemplate ) + ( None, 1, libraryProjectTemplate ) + ( Some(0), 2, libraryProjectTemplate ) + ( Some(0), 3, libraryProjectTemplate ) + ( Some(4), 4, libraryProjectTemplate ) + ( Some(4), 5, libraryProjectTemplate ) - [| None; 0; nestedTypesTemplate |] - [| None; 1; nestedTypesTemplate |] - [| Some(0); 2; nestedTypesTemplate |] - [| Some(4); 3; nestedTypesTemplate |] - [| Some(8); 4; nestedTypesTemplate |] - [| Some(8); 5; nestedTypesTemplate |] + ( None, 0, nestedTypesTemplate ) + ( None, 1, nestedTypesTemplate ) + ( Some(0), 2, nestedTypesTemplate ) + ( Some(4), 3, nestedTypesTemplate ) + ( Some(8), 4, nestedTypesTemplate ) + ( Some(8), 5, nestedTypesTemplate ) |] - static member private autoIndentTestCases = + let autoIndentTestCases = autoIndentTemplate.Split [|'\n'|] |> Array.map (fun s -> s.Trim()) |> Array.indexed @@ -165,22 +167,26 @@ while true do if m.Success then Some (line, System.Convert.ToInt32 m.Groups.[1].Value) else None ) |> Array.map (fun (lineNumber, expectedIndentation) -> - [| Some(expectedIndentation); lineNumber; autoIndentTemplate |]: Object[] ) + ( Some(expectedIndentation), lineNumber, autoIndentTemplate )) - [] - member this.TestIndentation(expectedIndentation: Option, lineNumber: int, template: string) = - let sourceText = SourceText.From(template) + member this.TestIndentation() = + for (expectedIndentation, lineNumber, template) in testCases do + let sourceText = SourceText.From(template) - let actualIndentation = FSharpIndentationService.GetDesiredIndentation(documentId, sourceText, filePath, lineNumber, tabSize, indentStyle, Some options) - match expectedIndentation with - | None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber) - | Some indentation -> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + let actualIndentation = FSharpIndentationService.GetDesiredIndentation(documentId, sourceText, filePath, lineNumber, tabSize, indentStyle, Some (parsingOptions, projectOptions)) + match expectedIndentation with + | None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber) + | Some indentation -> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber) - [] - member this.TestAutoIndentation(expectedIndentation: Option, lineNumber: int, template: string) = - let sourceText = SourceText.From(template) + member this.TestAutoIndentation() = + for (expectedIndentation, lineNumber, template) in autoIndentTestCases do + + + let sourceText = SourceText.From(template) - let actualIndentation = FSharpIndentationService.GetDesiredIndentation(documentId, sourceText, filePath, lineNumber, tabSize, indentStyle, Some options) - match expectedIndentation with - | None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber) - | Some indentation -> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + let actualIndentation = FSharpIndentationService.GetDesiredIndentation(documentId, sourceText, filePath, lineNumber, tabSize, indentStyle, Some (parsingOptions, projectOptions)) + match expectedIndentation with + | None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber) + | Some indentation -> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber) diff --git a/vsintegration/tests/unittests/QuickInfoProviderTests.fs b/vsintegration/tests/unittests/QuickInfoProviderTests.fs index 13500ea5c89..45d39459564 100644 --- a/vsintegration/tests/unittests/QuickInfoProviderTests.fs +++ b/vsintegration/tests/unittests/QuickInfoProviderTests.fs @@ -34,7 +34,7 @@ open UnitTests.TestLib.LanguageService let filePath = "C:\\test.fs" -let internal options = { +let internal projectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -97,8 +97,9 @@ Full name: System.Console" let caretPosition = fileContents.IndexOf(symbol) let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions let quickInfo = - FSharpQuickInfoProvider.ProvideQuickInfo(checker, documentId, SourceText.From(fileContents), filePath, caretPosition, options, 0) + FSharpQuickInfoProvider.ProvideQuickInfo(checker, documentId, SourceText.From(fileContents), filePath, caretPosition, parsingOptions, projectOptions, 0) |> Async.RunSynchronously let actual = quickInfo |> Option.map (fun (text, _, _, _) -> getQuickInfoText text) @@ -227,8 +228,9 @@ let res8 = abs 5.0 let caretPosition = fileContents.IndexOf(symbol) + symbol.Length - 1 let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions let quickInfo = - FSharpQuickInfoProvider.ProvideQuickInfo(checker, documentId, SourceText.From(fileContents), filePath, caretPosition, options, 0) + FSharpQuickInfoProvider.ProvideQuickInfo(checker, documentId, SourceText.From(fileContents), filePath, caretPosition, parsingOptions, projectOptions, 0) |> Async.RunSynchronously let actual = quickInfo |> Option.map (fun (text, _, _, _) -> getQuickInfoText text) diff --git a/vsintegration/tests/unittests/SignatureHelpProviderTests.fs b/vsintegration/tests/unittests/SignatureHelpProviderTests.fs index 1d1da4e85af..2280116b4fe 100644 --- a/vsintegration/tests/unittests/SignatureHelpProviderTests.fs +++ b/vsintegration/tests/unittests/SignatureHelpProviderTests.fs @@ -34,7 +34,7 @@ let filePath = "C:\\test.fs" let PathRelativeToTestAssembly p = Path.Combine(Path.GetDirectoryName(Uri( System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath), p) -let internal options = { +let internal projectOptions = { ProjectFileName = "C:\\test.fsproj" SourceFiles = [| filePath |] ReferencedProjects = [| |] @@ -145,7 +145,7 @@ type foo5 = N1.T } let triggerChar = if marker = "," then Some ',' elif marker = "(" then Some '(' elif marker = "<" then Some '<' else None - let triggered = FSharpSignatureHelpProvider.ProvideMethodsAsyncAux(checker, documentationProvider, SourceText.From(fileContents), caretPosition, options, triggerChar, filePath, 0) |> Async.RunSynchronously + let triggered = FSharpSignatureHelpProvider.ProvideMethodsAsyncAux(checker, documentationProvider, SourceText.From(fileContents), caretPosition, projectOptions, triggerChar, filePath, 0) |> Async.RunSynchronously checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() let actual = match triggered with diff --git a/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs b/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs index d8e60903aef..aca191434a8 100644 --- a/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs +++ b/vsintegration/tests/unittests/Tests.LanguageService.Completion.fs @@ -4337,7 +4337,8 @@ let x = query { for bbbb in abbbbc(*D0*) do let completions = AutoCompleteAtCursor(file) Assert.AreNotEqual(0, completions.Length, "Expected some items in the list after updating platform.") - /// FEATURE: The filename on disk and the filename in the project can differ in case. +(* +/// FEATURE: The filename on disk and the filename in the project can differ in case. [] member this.``Filenames.MayBeDifferentlyCased``() = use _guard = this.UsingNewVS() @@ -4357,7 +4358,8 @@ let x = query { for bbbb in abbbbc(*D0*) do this.AddAssemblyReference(project,"System.Deployment") let completions = AutoCompleteAtCursor(file) Assert.AreNotEqual(0, completions.Length, "Expected some items in the list after adding a reference.") - +*) + /// In this bug, a bogus flag caused the rest of flag parsing to be ignored. [] member public this.``FlagsAndSettings.Bug1969``() = diff --git a/vsintegration/update-vsintegration.cmd b/vsintegration/update-vsintegration.cmd index e7d1d85eaa5..9dcb36f92d5 100644 --- a/vsintegration/update-vsintegration.cmd +++ b/vsintegration/update-vsintegration.cmd @@ -3,30 +3,148 @@ @rem See License.txt in the project root for license information. @rem =========================================================================================================== -rem @echo off -setlocal +@rem Notes/instructions for modifications: +@rem +@rem * Do not use "::" for comments, as the line will be parsed and can create spurious +@rem errors, i.e. if it contains variables, "|" or ">" characters, esp. within "IF" +@rem and "FOR" compound statements +@rem +@rem * The coloring method uses the colors from color /h through a hacky trick with findstr. +@rem Only use filename-safe characters if you use CALL :colorEcho +@rem +@rem * Parts of this batch file require administrator permission. If such permissions aren't +@rem available, a warning will be issued and relevant parts will not be executed. +@rem +@rem * Currently, only one paramter is parsed and combinations are not possible +@rem +@rem * Installation of F# FSC compiler and FSI are done in the SHARED SDK directory. Henceforth +@rem each installation of Visual Studio 2017 will use the updated FSC.exe and the commandline +@rem FSI.exe. The in-product VS FSI plugin, syntax highlighting and IntelliSense must be +@rem installed through VSIXInstaller.exe debug\net40\bin\VisualFSharpOpenSource.vsix +@rem +@rem This procedure needs to be changed once F# supports multiple side-by-side installations +@rem at which point everything will go through VSIXInstaller.exe -if /i "%1" == "debug" goto :ok -if /i "%1" == "release" goto :ok +@echo off +setlocal EnableDelayedExpansion -echo Clobbers existing Visual Studio installation of F# bits -echo Usage: -echo update-vsintegration.cmd debug -echo update-vsintegration.cmd release -exit /b 1 +rem Count errors, warnings and succesful copies +set ERRORCOUNT=0 +set WARNCOUNT=0 +set COPYCOUNT=0 + +rem Enable colors, but can ONLY BE USED WITH PRINTING LINES THAT FIT IN A FILENAME! +for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do ( + set "DEL=%%a" +) + +if /i "%1" == "debug" ( + set ACTION=debug + set DEPLOY=yes + set BINDIR=%~dp0..\%1\net40\bin + goto :ok +) +if /i "%1" == "release" ( + set ACTION=release + set DEPLOY=yes + set BINDIR=%~dp0..\%1\net40\bin + goto :ok +) +if /i "%1" == "restore" ( + set ACTION=restore + set DEPLOY=no + set BINDIR=%~dp0..\%1 + goto :ok +) +if /i "%1" == "backup" ( + set ACTION=backup + set DEPLOY=no + set BINDIR=%~dp0..\restore + goto :ok +) + +set GOTOHELP=yes :ok -set BINDIR=%~dp0..\%1\net40\bin +set RESTOREDIR=%~dp0..\restore set TOPDIR=%~dp0.. +rem By using a token that does not exist in paths, this will resolve any ".." and "." in the path, even if path contains spaces +FOR /F "tokens=*" %%I IN ("%RESTOREDIR%") DO set RESTOREDIR=%%~fI +FOR /F "tokens=*" %%I IN ("%BINDIR%") DO set BINDIR=%%~fI +FOR /F "tokens=*" %%I IN ("%TOPDIR%") DO set TOPDIR=%%~fI + +if /i "%GOTOHELP%" == "yes" goto :help +GOTO :start + + +:help + +echo. +echo Installs or restores F# SDK bits, which applies system-wide to all Visual Studio +echo 2017 installations. After running this, each project targeting F# 4.1 will use +echo your locally built FSC.exe. It will not update other F# tools, see remarks below. +echo. +echo Requires Administrator privileges for removing/restoring strong-naming. +echo. +echo Syntax: %0 [debug^|release^|restore^|backup] +echo. +echo debug integrates debug builds of FSC, FSI ^& tools +echo release integrates release builds of FSC, FSI ^& tools +echo restore restores original SDK from an earlier backup +echo backup backups the files that would be overwritten, does not deploy anything +echo. +echo Paths used: +echo. +echo Root location: %TOPDIR% +echo Debug bin location: %TOPDIR%\debug\net40\bin +echo Release bin location: %TOPDIR%\release\net40\bin +echo Backup location: %RESTOREDIR% +echo. +echo Remarks: +echo. +echo This script should only be run after build.cmd has completed successfully. +echo. +echo Clearing the git repository may clear the backup directory. To be on the safe +echo side, you should place a copy of the backup dir outside of the git repo. +echo. +echo This batch script will only update the relevant SDK bits, and remove or restore +echo strong-naming automatically. It is recommended that you also update the F# Tools +echo by running the following two commands after a build of "build vs" or +echo "build vs debug" has completed. More instructions in DEVGUIDE.md in the root. +echo. +echo For Release builds: +echo. +echo ^> VSIXInstaller.exe /u:"VisualFSharp" +echo ^> VSIXInstaller.exe release\net40\bin\VisualFSharpOpenSource.vsix +echo. +echo For Debug builds: +echo. +echo ^> VSIXInstaller.exe /u:"VisualFSharp" +echo ^> VSIXInstaller.exe debug\net40\bin\VisualFSharpOpenSource.vsix +echo. + +exit /b 1 + +:start + +echo. +if "%DEPLOY%" == "yes" echo Starting deployment of %ACTION% bits. +if not "%DEPLOY%" == "yes" echo Starting %ACTION% +echo. + +rem This check whether we're started with administrator rights +CALL :checkPrequisites + if /i "%PROCESSOR_ARCHITECTURE%"=="x86" set X86_PROGRAMFILES=%ProgramFiles% if /I "%PROCESSOR_ARCHITECTURE%"=="AMD64" set X86_PROGRAMFILES=%ProgramFiles(x86)% set REGEXE32BIT=reg.exe if not "%OSARCH%"=="x86" set REGEXE32BIT=%WINDIR%\syswow64\reg.exe -::See https://stackoverflow.com/a/17113667/111575 on 2^>NUL for suppressing the error "ERROR: The system was unable to find the specified registry key or value." from reg.exe, this fixes #3619 +rem See https://stackoverflow.com/a/17113667/111575 on 2^>NUL for suppressing the error "ERROR: The system was unable to find the specified registry key or value." from reg.exe, this fixes #3619 +rem The delims are a TAB and a SPACE, do not normalize it! FOR /F "tokens=2* delims= " %%A IN ('%REGEXE32BIT% QUERY "HKLM\Software\WOW6432Node\Microsoft\Microsoft SDKs\NETFXSDK\4.6.2\WinSDK-NetFx40Tools" /v InstallationFolder 2^>NUL') DO SET WINSDKNETFXTOOLS=%%B if "%WINSDKNETFXTOOLS%"=="" FOR /F "tokens=2* delims= " %%A IN ('%REGEXE32BIT% QUERY "HKLM\Software\WOW6432Node\Microsoft\Microsoft SDKs\NETFXSDK\4.6.1\WinSDK-NetFx40Tools" /v InstallationFolder 2^>NUL') DO SET WINSDKNETFXTOOLS=%%B if "%WINSDKNETFXTOOLS%"=="" FOR /F "tokens=2* delims= " %%A IN ('%REGEXE32BIT% QUERY "HKLM\Software\Microsoft\Microsoft SDKs\NETFXSDK\4.6\WinSDK-NetFx40Tools" /v InstallationFolder 2^>NUL') DO SET WINSDKNETFXTOOLS=%%B @@ -43,112 +161,510 @@ set NGEN64=%windir%\Microsoft.NET\Framework64\v4.0.30319\ngen.exe set FSHARPVERSION=4.1 set FSHARPVERSION2=41 +rem The various locations of the SDK and tools + +rem SDK path, will be created if it doesn't exist set COMPILERSDKPATH=%X86_PROGRAMFILES%\Microsoft SDKs\F#\%FSHARPVERSION%\Framework\v4.0 -mkdir "%COMPILERSDKPATH%" -copy /y "%BINDIR%\fsc.exe" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\fsc.exe.config" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\FSharp.Build.dll" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\FSharp.Compiler.Private.dll" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\FSharp.Compiler.Interactive.Settings.dll" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\fsi.exe" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\fsi.exe.config" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\fsiAnyCpu.exe" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\fsiAnyCpu.exe.config" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\Microsoft.FSharp.Targets" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\Microsoft.Portable.FSharp.Targets" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\Microsoft.FSharp.NetSdk.props" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\Microsoft.FSharp.NetSdk.targets" "%COMPILERSDKPATH%" -copy /y "%TOPDIR%\vsintegration\src\SupportedRuntimes\SupportedRuntimes.xml" "%COMPILERSDKPATH%" +rem Main assemblies path, will be created if it doesn't exist set COMPILERMAINASSEMBLIESPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.%FSHARPVERSION%.0 -mkdir "%COMPILERMAINASSEMBLIESPATH%" -copy /y "%BINDIR%\FSharp.Core.dll" "%COMPILERMAINASSEMBLIESPATH%" -copy /y "%BINDIR%\FSharp.Core.optdata" "%COMPILERMAINASSEMBLIESPATH%" -copy /y "%BINDIR%\FSharp.Core.sigdata" "%COMPILERMAINASSEMBLIESPATH%" -copy /y "%BINDIR%\FSharp.Core.xml" "%COMPILERMAINASSEMBLIESPATH%" +rem The .NET Core 3.7 assemblies path, will be created if it doesn't exist set COMPILER7ASSEMBLIESPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETCore\3.7.%FSHARPVERSION2%.0 -mkdir "%COMPILER7ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable7\bin\FSharp.Core.dll" "%COMPILER7ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable7\bin\FSharp.Core.optdata" "%COMPILER7ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable7\bin\FSharp.Core.sigdata" "%COMPILER7ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable7\bin\FSharp.Core.xml" "%COMPILER7ASSEMBLIESPATH%" +rem The .NET Core 3.78 assemblies path, will be created if it doesn't exist set COMPILER78ASSEMBLIESPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETCore\3.78.%FSHARPVERSION2%.0 -mkdir "%COMPILER78ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable78\bin\FSharp.Core.dll" "%COMPILER78ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable78\bin\FSharp.Core.optdata" "%COMPILER78ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable78\bin\FSharp.Core.sigdata" "%COMPILER78ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable78\bin\FSharp.Core.xml" "%COMPILER78ASSEMBLIESPATH%" +rem The .NET Core 3.259 assemblies path, will be created if it doesn't exist set COMPILER259ASSEMBLIESPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETCore\3.259.%FSHARPVERSION2%.0 -mkdir "%COMPILER259ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable259\bin\FSharp.Core.dll" "%COMPILER259ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable259\bin\FSharp.Core.optdata" "%COMPILER259ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable259\bin\FSharp.Core.sigdata" "%COMPILER259ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable259\bin\FSharp.Core.xml" "%COMPILER259ASSEMBLIESPATH%" +rem The .NET Portable 3.47 assemblies path, will be created if it doesn't exist set COMPILER47ASSEMBLIESPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETPortable\3.47.%FSHARPVERSION2%.0 -mkdir "%COMPILER47ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable47\bin\FSharp.Core.dll" "%COMPILER47ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable47\bin\FSharp.Core.optdata" "%COMPILER47ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable47\bin\FSharp.Core.sigdata" "%COMPILER47ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable47\bin\FSharp.Core.xml" "%COMPILER47ASSEMBLIESPATH%" +rem Try to create target and backup folders, if needed +set RESTOREBASE=%RESTOREDIR% + +rem Only create backup dirs if we are backupping or restoring +rem (in the latter case, the directories should already be there, but if not, it prevents errors later on) +if "!DEPLOY!" == "no" ( + CALL :tryCreateFolder "!RESTOREBASE!\compiler_sdk" + CALL :tryCreateFolder "!RESTOREBASE!\main_assemblies" + CALL :tryCreateFolder "!RESTOREBASE!\profile_7" + CALL :tryCreateFolder "!RESTOREBASE!\profile_78" + CALL :tryCreateFolder "!RESTOREBASE!\profile_259" + CALL :tryCreateFolder "!RESTOREBASE!\profile_47" +) +CALL :tryCreateFolder "!COMPILERSDKPATH!" +CALL :tryCreateFolder "!COMPILERMAINASSEMBLIESPATH!" +CALL :tryCreateFolder "!COMPILER7ASSEMBLIESPATH!" & +CALL :tryCreateFolder "!COMPILER78ASSEMBLIESPATH!" +CALL :tryCreateFolder "!COMPILER259ASSEMBLIESPATH!" +CALL :tryCreateFolder "!COMPILER47ASSEMBLIESPATH!" + +rem If one or more directories could not be created, exit early with a non-zero error code +if "!CREATEFAILED!"=="true" CALL :exitFailDir & EXIT /B 1 + +rem Deploying main files, fsi.exe and fsc.exe and related + +echo. +CALL :colorEcho 02 "[!ACTION!] Processing files for compiler_sdk" & echo. + +set SOURCEDIR=%BINDIR% +set RESTOREDIR=!RESTOREBASE!\compiler_sdk +CALL :checkAvailability compiler_sdk +if "!BIN_AVAILABLE!" == "true" ( + CALL :backupAndOrCopy fsc.exe "!COMPILERSDKPATH!" + CALL :backupAndOrCopy fsc.exe.config "%COMPILERSDKPATH%" + CALL :backupAndOrCopy FSharp.Build.dll "%COMPILERSDKPATH%" + CALL :backupAndOrCopy FSharp.Compiler.Private.dll "%COMPILERSDKPATH%" + CALL :backupAndOrCopy FSharp.Compiler.Interactive.Settings.dll "%COMPILERSDKPATH%" + CALL :backupAndOrCopy fsi.exe "%COMPILERSDKPATH%" + CALL :backupAndOrCopy fsi.exe.config "%COMPILERSDKPATH%" + CALL :backupAndOrCopy fsiAnyCpu.exe "%COMPILERSDKPATH%" + CALL :backupAndOrCopy fsiAnyCpu.exe.config "%COMPILERSDKPATH%" + CALL :backupAndOrCopy Microsoft.FSharp.Targets "%COMPILERSDKPATH%" + CALL :backupAndOrCopy Microsoft.Portable.FSharp.Targets "%COMPILERSDKPATH%" + CALL :backupAndOrCopy Microsoft.FSharp.NetSdk.props "%COMPILERSDKPATH%" + CALL :backupAndOrCopy Microsoft.FSharp.NetSdk.targets "%COMPILERSDKPATH%" + + rem Special casing for SupportedRuntimes.xml, it has a different source directory, it's always there + set SOURCEDIR="%TOPDIR%\vsintegration\src\SupportedRuntimes" + CALL :backupAndOrCopy SupportedRuntimes.xml "%COMPILERSDKPATH%" +) + + + +rem Deploying main assemblies + +echo. +CALL :colorEcho 02 "[!ACTION!] Processing files for main_assemblies" & echo. + +set SOURCEDIR=%BINDIR% +set RESTOREDIR=!RESTOREBASE!\main_assemblies +CALL :checkAvailability main_assemblies +if "!BIN_AVAILABLE!" == "true" ( + CALL :backupAndOrCopy FSharp.Core.dll "%COMPILERMAINASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.optdata "%COMPILERMAINASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.sigdata "%COMPILERMAINASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.xml "%COMPILERMAINASSEMBLIESPATH%" +) + + +rem Deploying for .NET Core 3.7 + +echo. +CALL :colorEcho 02 "[!ACTION!] Processing files for profile_7" & echo. + +set SOURCEDIR=%BINDIR%\..\..\portable7\bin +set RESTOREDIR=!RESTOREBASE!\profile_7 +CALL :checkAvailability profile_7 +if "!BIN_AVAILABLE!" == "true" ( + CALL :backupAndOrCopy FSharp.Core.dll "%COMPILER7ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.optdata "%COMPILER7ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.sigdata "%COMPILER7ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.xml "%COMPILER7ASSEMBLIESPATH%" +) + + +rem Deploying for .NET Core 3.78 + +echo. +CALL :colorEcho 02 "[!ACTION!] Processing files for profile_78" & echo. + +set SOURCEDIR=%BINDIR%\..\..\portable78\bin +set RESTOREDIR=!RESTOREBASE!\profile_78 +CALL :checkAvailability profile_78 +if "!BIN_AVAILABLE!" == "true" ( + CALL :backupAndOrCopy FSharp.Core.dll "%COMPILER78ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.optdata "%COMPILER78ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.sigdata "%COMPILER78ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.xml "%COMPILER78ASSEMBLIESPATH%" +) + + +rem Deploying for .NET Core 3.259 + +echo. +CALL :colorEcho 02 "[!ACTION!] Processing files for profile_259" & echo. + +set SOURCEDIR=%BINDIR%\..\..\portable259\bin +set RESTOREDIR=!RESTOREBASE!\profile_259 +CALL :checkAvailability profile_259 +if "!BIN_AVAILABLE!" == "true" ( + CALL :backupAndOrCopy FSharp.Core.dll "%COMPILER259ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.optdata "%COMPILER259ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.sigdata "%COMPILER259ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.xml "%COMPILER259ASSEMBLIESPATH%" +) + + +rem Deploying for .NET Portable 3.47 + +echo. +CALL :colorEcho 02 "[!ACTION!] Processing files for profile_47" & echo. + +set SOURCEDIR=%BINDIR%\..\..\portable47\bin +set RESTOREDIR=!RESTOREBASE!\profile_47 +CALL :checkAvailability profile_47 +if "!BIN_AVAILABLE!" == "true" ( + CALL :backupAndOrCopy FSharp.Core.dll "%COMPILER47ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.optdata "%COMPILER47ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.sigdata "%COMPILER47ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.xml "%COMPILER47ASSEMBLIESPATH%" +) + +REM TODO: this was already here (2017-09-28) and was already commented out, I think (AB) that these redirects aren't necessary anymore and can be permanently removed REM echo ^^^^^ ^^^^^ > "%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.%FSHARPVERSION%.0\pub.config" -if /I "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( - REG ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\F# %FSHARPVERSION% Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.%FSHARPVERSION%.0\ - REG ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.50709\AssemblyFoldersEx\F# %FSHARPVERSION% Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.%FSHARPVERSION%.0\ -) -REG ADD "HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\F# %FSHARPVERSION% Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.%FSHARPVERSION%.0\ -REG ADD "HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.50709\AssemblyFoldersEx\F# %FSHARPVERSION% Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.%FSHARPVERSION%.0\ - -rem Disable strong-name validation for F# binaries built from open source that are signed with the microsoft key -%SN32% -Vr FSharp.Core,b03f5f7f11d50a3a -%SN32% -Vr FSharp.Build,b03f5f7f11d50a3a -%SN32% -Vr FSharp.Compiler.Interactive.Settings,b03f5f7f11d50a3a -%SN32% -Vr HostedCompilerServer,b03f5f7f11d50a3a - -%SN32% -Vr FSharp.Compiler,b03f5f7f11d50a3a -%SN32% -Vr FSharp.Compiler.Server.Shared,b03f5f7f11d50a3a -%SN32% -Vr FSharp.Editor,b03f5f7f11d50a3a -%SN32% -Vr FSharp.LanguageService,b03f5f7f11d50a3a -%SN32% -Vr FSharp.LanguageService.Base,b03f5f7f11d50a3a -%SN32% -Vr FSharp.ProjectSystem.Base,b03f5f7f11d50a3a -%SN32% -Vr FSharp.ProjectSystem.FSharp,b03f5f7f11d50a3a -%SN32% -Vr FSharp.ProjectSystem.PropertyPages,b03f5f7f11d50a3a -%SN32% -Vr FSharp.VS.FSI,b03f5f7f11d50a3a -%SN32% -Vr VisualFSharp.Unittests,b03f5f7f11d50a3a -%SN32% -Vr VisualFSharp.Salsa,b03f5f7f11d50a3a - -if /i "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( - %SN64% -Vr FSharp.Core,b03f5f7f11d50a3a - %SN64% -Vr FSharp.Build,b03f5f7f11d50a3a - %SN64% -Vr FSharp.Compiler.Interactive.Settings,b03f5f7f11d50a3a - %SN64% -Vr HostedCompilerServer,b03f5f7f11d50a3a - - %SN64% -Vr FSharp.Compiler,b03f5f7f11d50a3a - %SN64% -Vr FSharp.Compiler.Server.Shared,b03f5f7f11d50a3a - %SN64% -Vr FSharp.Editor,b03f5f7f11d50a3a - %SN64% -Vr FSharp.LanguageService,b03f5f7f11d50a3a - %SN64% -Vr FSharp.LanguageService.Base,b03f5f7f11d50a3a - %SN64% -Vr FSharp.ProjectSystem.Base,b03f5f7f11d50a3a - %SN64% -Vr FSharp.ProjectSystem.FSharp,b03f5f7f11d50a3a - %SN64% -Vr FSharp.ProjectSystem.PropertyPages,b03f5f7f11d50a3a - %SN64% -Vr FSharp.VS.FSI,b03f5f7f11d50a3a - %SN64% -Vr VisualFSharp.Unittests,b03f5f7f11d50a3a - %SN64% -Vr VisualFSharp.Salsa,b03f5f7f11d50a3a -) - -rem NGen fsc, fsi, fsiAnyCpu, and FSharp.Build.dll - -"%NGEN32%" install "%COMPILERSDKPATH%\fsc.exe" /queue:1 -"%NGEN32%" install "%COMPILERSDKPATH%\fsi.exe" /queue:1 -"%NGEN32%" install "%COMPILERSDKPATH%\fsiAnyCpu.exe" /queue:1 -"%NGEN32%" install "%COMPILERSDKPATH%\FSharp.Build.dll" /queue:1 - -if /i "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( - "%NGEN64%" install "%COMPILERSDKPATH%\fsiAnyCpu.exe" /queue:1 - "%NGEN64%" install "%COMPILERSDKPATH%\FSharp.Build.dll" /queue:1 +rem To add registry keys and to change strong-name validation requires Administrator access + +if "%DEPLOY%" == "yes" if "!ISADMIN!" == "yes" ( + echo. + CALL :colorEcho 02 "[!ACTION!] Setting or adding registry keys for open source assemblies" & echo. + if /I "!PROCESSOR_ARCHITECTURE!"=="AMD64" ( + REG ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\F# !FSHARPVERSION! Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "!X86_PROGRAMFILES!\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.!FSHARPVERSION!.0\ + REG ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.50709\AssemblyFoldersEx\F# !FSHARPVERSION! Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "!X86_PROGRAMFILES!\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.!FSHARPVERSION!.0\ + ) + REG ADD "HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\F# !FSHARPVERSION! Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "!X86_PROGRAMFILES!\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.!FSHARPVERSION!.0\ + REG ADD "HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.50709\AssemblyFoldersEx\F# !FSHARPVERSION! Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "!X86_PROGRAMFILES!\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.!FSHARPVERSION!.0\ + + rem Disable strong-name validation for F# binaries built from open source that are signed with the microsoft key + echo. + CALL :colorEcho 02 "[!ACTION!] Removing strong-name validation of F# binaries" & echo. + !SN32! -Vr FSharp.Core,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.Build,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.Compiler.Interactive.Settings,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr HostedCompilerServer,b03f5f7f11d50a3a 1>NUL 2>NUL + + !SN32! -Vr FSharp.Compiler,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.Compiler.Server.Shared,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.Editor,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.LanguageService,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.LanguageService.Base,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.ProjectSystem.Base,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.ProjectSystem.FSharp,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.ProjectSystem.PropertyPages,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.VS.FSI,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr VisualFSharp.Unittests,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr VisualFSharp.Salsa,b03f5f7f11d50a3a 1>NUL 2>NUL + + REM Do this *in addition* to the above for x64 systems + if /i "!PROCESSOR_ARCHITECTURE!"=="AMD64" ( + !SN64! -Vr FSharp.Core,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.Build,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.Compiler.Interactive.Settings,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr HostedCompilerServer,b03f5f7f11d50a3a 1>NUL 2>NUL + + !SN64! -Vr FSharp.Compiler,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.Compiler.Server.Shared,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.Editor,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.LanguageService,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.LanguageService.Base,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.ProjectSystem.Base,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.ProjectSystem.FSharp,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.ProjectSystem.PropertyPages,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.VS.FSI,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr VisualFSharp.Unittests,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr VisualFSharp.Salsa,b03f5f7f11d50a3a 1>NUL 2>NUL + ) + + rem NGen fsc, fsi, fsiAnyCpu, and FSharp.Build.dll + + echo. + CALL :colorEcho 02 "[!ACTION!] Queuing for NGEN of FSI and FSC binaries" & echo. + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\fsc.exe" + "!NGEN32!" install "!COMPILERSDKPATH!\fsc.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\fsi.exe" + "!NGEN32!" install "!COMPILERSDKPATH!\fsi.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\fsiAnyCpu.exe" + "!NGEN32!" install "!COMPILERSDKPATH!\fsiAnyCpu.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\FSharp.Build.dll" + "!NGEN32!" install "!COMPILERSDKPATH!\FSharp.Build.dll" /queue:1 1>NUL + + if /i "!PROCESSOR_ARCHITECTURE!"=="AMD64" ( + echo [!ACTION!] NGEN64 of "!COMPILERSDKPATH!\fsiAnyCpu.exe" + "!NGEN64!" install "!COMPILERSDKPATH!\fsiAnyCpu.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN64 of "!COMPILERSDKPATH!\FSharp.Build.dll" + "!NGEN64!" install "!COMPILERSDKPATH!\FSharp.Build.dll" /queue:1 1>NUL + ) +) + +if "%DEPLOY%" == "yes" if "!ISADMIN!" == "no" ( + echo. + CALL :colorEcho 0E "[!ACTION!] SKIPPED (no admin) Setting or adding registry keys for open source assemblies" & echo. + CALL :colorEcho 0E "[!ACTION!] SKIPPED (no admin) Removing strong-name validation of F# binaries" & echo. + CALL :colorEcho 02 "[!ACTION!] SKIPPED (no admin) Queuing for NGEN of FSI and FSC binaries" & echo. + SET /A WARNCOUNT+=3 +) + +rem Re-enable certain settings when restoring, NGEN the original files again, requires admin rights +if "%ACTION%" == "restore" if "!ISADMIN!" == "yes" ( + + rem Re-enable strong-name validation for F# binaries that were previously installed + echo. + CALL :colorEcho 02 "[!ACTION!] Re-enabling strong-name validation of original F# binaries" & echo. + !SN32! -Vu FSharp.Core,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.Build,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.Compiler.Interactive.Settings,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu HostedCompilerServer,b03f5f7f11d50a3a 2>NUL 1>NUL + + !SN32! -Vu FSharp.Compiler,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.Compiler.Server.Shared,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.Editor,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.LanguageService,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.LanguageService.Base,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.ProjectSystem.Base,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.ProjectSystem.FSharp,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.ProjectSystem.PropertyPages,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.VS.FSI,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu VisualFSharp.Unittests,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu VisualFSharp.Salsa,b03f5f7f11d50a3a 2>NUL 1>NUL + + REM Do this *in addition* to the above for x64 systems + if /i "!PROCESSOR_ARCHITECTURE!"=="AMD64" ( + !SN64! -Vu FSharp.Core,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.Build,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.Compiler.Interactive.Settings,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu HostedCompilerServer,b03f5f7f11d50a3a 2>NUL 1>NUL + + !SN64! -Vu FSharp.Compiler,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.Compiler.Server.Shared,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.Editor,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.LanguageService,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.LanguageService.Base,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.ProjectSystem.Base,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.ProjectSystem.FSharp,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.ProjectSystem.PropertyPages,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.VS.FSI,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu VisualFSharp.Unittests,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu VisualFSharp.Salsa,b03f5f7f11d50a3a 2>NUL 1>NUL + ) + + rem NGen fsc, fsi, fsiAnyCpu, and FSharp.Build.dll + + echo. + CALL :colorEcho 02 "[!ACTION!] Queuing for NGEN of FSI and FSC binaries" & echo. + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\fsc.exe" + "!NGEN32!" install "!COMPILERSDKPATH!\fsc.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\fsi.exe" + "!NGEN32!" install "!COMPILERSDKPATH!\fsi.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\fsiAnyCpu.exe" + "!NGEN32!" install "!COMPILERSDKPATH!\fsiAnyCpu.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\FSharp.Build.dll" + "!NGEN32!" install "!COMPILERSDKPATH!\FSharp.Build.dll" /queue:1 1>NUL + + if /i "!PROCESSOR_ARCHITECTURE!"=="AMD64" ( + echo [!ACTION!] NGEN64 of "!COMPILERSDKPATH!\fsiAnyCpu.exe" + "!NGEN64!" install "!COMPILERSDKPATH!\fsiAnyCpu.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN64 of "!COMPILERSDKPATH!\FSharp.Build.dll" + "!NGEN64!" install "!COMPILERSDKPATH!\FSharp.Build.dll" /queue:1 1>NUL + ) +) + +if "%ACTION%" == "restore" if "!ISADMIN!" == "no" ( + CALL :colorEcho 0E "[!ACTION!] SKIPPED (no admin) Re-enabling strong-name validation of original F# binaries" & echo. + CALL :colorEcho 0E "[!ACTION!] SKIPPED (no admin) Queuing for NGEN of FSI and FSC binaries" & echo. + set /A WARNCOUNT+=2 +) +GOTO :summary + +:checkAvailability +rem Checks whether a given source is available, issues a warning otherwise, SOURCEDIR must be set to the appropriate binaries + +rem This will simultaneously remove the quotes of the original param and add the filename to it, then it is surrounded by quotes again +FOR /F "usebackq tokens=*" %%I IN ('%SOURCEDIR%') DO set SOURCE="%%~fI\*" +if not exist !SOURCE! ( + rem For debug and release deploy it matters, but for restore and backup we don't care + set BIN_AVAILABLE=true + if "!DEPLOY!" == "yes" ( + echo [!ACTION!] Source bindir does not exist: !SOURCE! + CALL :colorEcho 0E "[!ACTION!] Source binaries not found, deploy of %1 skipped" & echo. & set /A WARNCOUNT+=1 + set BIN_AVAILABLE=false + ) + +) else ( + set BIN_AVAILABLE=true ) + +EXIT /B + + +:backupAndOrCopy +rem Creates a backup and copies, depending on whether debug, release, restore or backup is selected + +rem This will simultaneously remove the quotes of the original param and add the filename to it, then it is surrounded by quotes again +FOR /F "usebackq tokens=*" %%I IN ('%2') DO set TARGET="%%~fI\%1" +FOR /F "usebackq tokens=*" %%I IN ('%RESTOREDIR%') DO set BACKUP="%%~fI\%1" +FOR /F "usebackq tokens=*" %%I IN ('%SOURCEDIR%') DO set SOURCE="%%~fI\%1" + +if "%ACTION%" == "backup" ( + rem When backing up, the target becomes the source + + if not exist !TARGET! ( + rem Remove a file from the backup location if it is not part of this SDK install + DEL /f !BACKUP! 1>NUL 2>NUL + ) else ( + rem Otherwise, copy over the original + CALL :copyFile !TARGET! !BACKUP! + ) +) + +if "%ACTION%" == "restore" ( + rem When restoring, the backup location becomes the source + + if not exist !BACKUP! ( + rem If this file didn't exist in the previous installation, we should remove it to prevent confusion of left-over bits + DEL /f !TARGET! 1>NUL 2>NUL + ) else ( + rem Otherwise, copy over the original + CALL :copyFile !BACKUP! !TARGET! + ) +) + +if "%DEPLOY%" == "yes" ( + rem Deploy of debug or release build, depending on selected action + CALL :copyFile !SOURCE! !TARGET! +) + + +EXIT /B + +rem Copies a file and logs errors in red, warnings in yellow +:copyFile +FOR /F "usebackq tokens=*" %%I IN ('%1') DO set SOURCE="%%~fI" +FOR /F "usebackq tokens=*" %%I IN ('%2') DO set TARGET="%%~fI" + +echo [%ACTION%] source: !SOURCE! +echo [%ACTION%] target: !TARGET! +if EXIST !SOURCE! ( + copy /y !SOURCE! !TARGET! 1>NUL 2>copyresult.log + if "!errorlevel!" == "0" echo [!ACTION!] 1 file copied & set /A COPYCOUNT+=1 + if not "!errorlevel!" == "0" ( + set /p COPYRESULT=nul + set COPYRESULT= +) else ( + if "%ACTION%" == "backup" CALL [backup] File not found, nothing to backup + if "%ACTION%" == "restore" CALL :colorEcho 0E "[restore] File not found, not able to restore, possibly it didn't exist originally" & echo. & set /A WARNCOUNT+=1 + if "%DEPLOY%" == "yes" CALL :colorEcho 0C "[!ACTION!] File not found, not able to deploy" & echo. & set /A ERRORCOUNT+=1 +) + +EXIT /B + +rem Creates a folder, if it already exists, it will do nothing, if there's an access-denied, it will set %CREATEFAILED% to true +:tryCreateFolder + +rem Add a backslash safely, by taking care of auxiliary quotes +FOR /F "usebackq tokens=*" %%I IN ('%1') DO set FOLDER_TO_BE_CREATED="%%~fI\" + +if not exist !FOLDER_TO_BE_CREATED! ( + mkdir !FOLDER_TO_BE_CREATED! 2>NUL + if "!errorlevel!" EQU "0" ( + echo [!ACTION!] Created directory !FOLDER_TO_BE_CREATED! + ) else ( + set CREATEFAILED=true + echo Failed to create %1 + CALL :colorEcho 0C "Could not create directory, check access rights or whether a file with that name exists " + echo. + echo. + ) +) + +EXIT /B + +:summary + +echo. +if not "%ACTION%" == "restore" if not "%ACTION%" == "backup" echo Finished installing F# SDK and other bits. The following directories were updated and & echo a backup is written to %RESTOREDIR%. +if "%ACTION%" == "restore" echo Finished restoring original F# SDK and other bits. The following directories were used while & echo restoring a backup from %RESTOREDIR%. +if "%ACTION%" == "backup" echo Finished creating a backup in %RESTOREBASE%. + +echo. +echo Root location: %TOPDIR% +if "!ACTION!" == "debug" echo Debug bin location: %TOPDIR%\debug\net40\bin +if "!ACTION!" == "release" echo Release bin location: %TOPDIR%\release\net40\bin +if "!DEPLOY!" == "no" echo Backup location: %RESTOREBASE% +echo. +echo Target locations used: +echo. +echo Win SDK tools: %WINSDKNETFXTOOLS% +echo Compiler SDK path: %COMPILERSDKPATH% +echo F# compiler main assemblies: %COMPILERMAINASSEMBLIESPATH% +echo Portable profile 7: %COMPILER7ASSEMBLIESPATH% +echo Portable profile 78: %COMPILER78ASSEMBLIESPATH% +echo Portable profile 259: %COMPILER259ASSEMBLIESPATH% +echo Portable profile 47: %COMPILER47ASSEMBLIESPATH% +echo. + +rem Display success, warning, error counts + +if "%ACTION%" == "backup" SET VERB=backed up +if "%ACTION%" == "restore" SET VERB=restored +if "%DEPLOY%" == "yes" SET VERB=deployed +CALL :colorEcho 0A "A total of %COPYCOUNT% file(s) were %VERB%." & echo. + +if %ERRORCOUNT% equ 1 CALL :colorEcho 0C "%ERRORCOUNT% error reported, see log" & echo. +if %ERRORCOUNT% gtr 1 CALL :colorEcho 0C "%ERRORCOUNT% errors reported, see log" & echo. +if %ERRORCOUNT% equ 0 CALL :colorEcho 0A "No errors reported" & echo. + +if %WARNCOUNT% equ 1 CALL :colorEcho 0E "%WARNCOUNT% warning reported, see log" & echo. +if %WARNCOUNT% gtr 1 CALL :colorEcho 0E "%WARNCOUNT% warnings reported, see log" & echo. +if %WARNCOUNT% equ 0 CALL :colorEcho 0A "No warnings reported" & echo. + +rem Return non-zero error code for use-cases where this script is called from other scripts +if %ERRORCOUNT% gtr 0 EXIT /B 1 +EXIT /B 0 + +GOTO :EOF + +:exitFailDir + +echo. +CALL :colorEcho 0C "One or more directories failed to be created. No files have been copied." & echo. +echo. +echo Possible causes include: +echo - Insufficient rights to create directories in this folder +echo - A file with that name already exists +echo. +echo No error is raised if the directory exists. +echo No files were copied or backed up. +echo. + +rem Return non-zero error code for use-cases where this script is called from other scripts +EXIT /B 1 + +:checkPrequisites +rem Whether or not we have administrator rights + +SET ISADMIN=yes + +rem The error level of NET SESSION is set to 2 when you don't have administrator rights, simplest hack +net sessions 1>NUL 2>NUL +if %ERRORLEVEL% GTR 0 ( + SET ISADMIN=no + CALL :colorEcho 0E "[!ACTION!] Started without administrator access, strong-naming will not be adjusted, reg-keys not changed" & echo. + SET /A WARNCOUNT+=1 +) + +EXIT /B + + +rem See: https://stackoverflow.com/a/21666354/111575 +rem Prevent accidentally entering the colorEcho label +GOTO :EOF +:colorEcho + "%~2" +findstr /v /a:%1 /R "^$" "%~2" nul +del "%~2" > nul 2>&1i \ No newline at end of file