From 6339b2ee946f4241a4b63796c606a94e61a54d98 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 5 Apr 2023 13:47:51 +0200 Subject: [PATCH 001/222] AsyncMemoize v1 --- src/Compiler/FSharp.Compiler.Service.fsproj | 1 + src/Compiler/Facilities/AsyncMemoize.fs | 130 ++++++++++++++++++ src/Compiler/Facilities/BuildGraph.fsi | 2 + .../CompilerService/AsyncMemoize.fs | 115 ++++++++++++++++ .../FSharp.Compiler.ComponentTests.fsproj | 12 +- 5 files changed, 254 insertions(+), 6 deletions(-) create mode 100644 src/Compiler/Facilities/AsyncMemoize.fs create mode 100644 tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 537636de072..56c023a3a46 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -153,6 +153,7 @@ + --unicode --lexlib Internal.Utilities.Text.Lexing AbstractIL\illex.fsl diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs new file mode 100644 index 00000000000..d7f17f2352c --- /dev/null +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -0,0 +1,130 @@ +namespace Internal.Utilities.Collections + +open FSharp.Compiler.BuildGraph +open System.Threading +open System.Collections.Generic + +type internal Action<'TKey, 'TValue> = + | GetOrCompute of ('TKey -> NodeCode<'TValue>) * CancellationToken + | CancelRequest + | JobCompleted + +type MemoizeRequest<'TKey, 'TValue> = 'TKey * Action<'TKey, 'TValue> * AsyncReplyChannel> + +type internal Job<'TValue> = + | Running of NodeCode<'TValue> * CancellationTokenSource + | Completed of NodeCode<'TValue> + +type internal JobEvent<'TKey> = + | Started of 'TKey + | Finished of 'TKey + | Canceled of 'TKey + + member this.Key = + match this with + | Started key -> key + | Finished key -> key + | Canceled key -> key + +type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?eventLog: ResizeArray>) = + + let tok = obj () + + let cache = + MruCache<_, 'TKey, Job<'TValue>>(keepStrongly = 10, areSame = (fun (x, y) -> x = y)) + + let requestCounts = Dictionary<'TKey, int>() + + let incrRequestCount key = + requestCounts.[key] <- + if requestCounts.ContainsKey key then + requestCounts.[key] + 1 + else + 1 + + let sendAsync (inbox: MailboxProcessor<_>) key msg = + inbox.PostAndAsyncReply(fun rc -> key, msg, rc) |> Async.Ignore |> Async.Start + + let log event = + eventLog |> Option.iter (fun log -> log.Add event) + + let agent = + MailboxProcessor.Start(fun (inbox: MailboxProcessor>) -> + + let post = sendAsync inbox + + async { + while true do + + let! key, action, replyChannel = inbox.Receive() + + match action, cache.TryGet(tok, key) with + | GetOrCompute _, Some (Completed job) -> replyChannel.Reply job + | GetOrCompute (_, ct), Some (Running (job, _)) -> + + incrRequestCount key + replyChannel.Reply job + ct.Register(fun _ -> post key CancelRequest) |> ignore + + | GetOrCompute (computation, ct), None -> + + let cts = new CancellationTokenSource() + + let startedComputation = + Async.StartAsTask( + Async.AwaitNodeCode( + node { + let! result = computation key + post key JobCompleted + return result + } + ), + cancellationToken = cts.Token + ) + + log (Started key) + + let job = NodeCode.AwaitTask startedComputation + + cache.Set(tok, key, (Running(job, cts))) + + incrRequestCount key + + ct.Register(fun _ -> post key CancelRequest) |> ignore + + replyChannel.Reply job + + | CancelRequest, Some (Running (_, cts)) -> + let requestCount = requestCounts.TryGetValue key |> snd + + if requestCount > 1 then + requestCounts.[key] <- requestCount - 1 + + else + cts.Cancel() + cache.RemoveAnySimilar(tok, key) + requestCounts.Remove key |> ignore + log (Canceled key) + + | CancelRequest, None + | CancelRequest, Some (Completed _) -> () + + | JobCompleted, Some (Running (job, _)) -> + cache.Set(tok, key, (Completed job)) + requestCounts.Remove key |> ignore + log (Finished key) + + | JobCompleted, _ -> failwith "If this happens there's a bug" + + }) + + member _.Get(key, computation) = + node { + let! ct = NodeCode.CancellationToken + + let! job = + agent.PostAndAsyncReply(fun rc -> key, (GetOrCompute(computation, ct)), rc) + |> NodeCode.AwaitAsync + + return! job + } diff --git a/src/Compiler/Facilities/BuildGraph.fsi b/src/Compiler/Facilities/BuildGraph.fsi index 1bfa2f30cac..afbf9d2898b 100644 --- a/src/Compiler/Facilities/BuildGraph.fsi +++ b/src/Compiler/Facilities/BuildGraph.fsi @@ -70,6 +70,8 @@ type NodeCode = static member Parallel: computations: (NodeCode<'T> seq) -> NodeCode<'T[]> + static member AwaitAsync: computation: Async<'T> -> NodeCode<'T> + static member AwaitTask: task: Task<'T> -> NodeCode<'T> static member AwaitTask: task: Task -> NodeCode diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs new file mode 100644 index 00000000000..78123ef4efd --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -0,0 +1,115 @@ +module FSharp.Compiler.ComponentTests.CompilerService.AsyncMemoize + +open System +open System.Threading +open Xunit +open FSharp.Test +open FSharp.Compiler.BuildGraph +open Internal.Utilities.Collections +open System.Threading.Tasks + +[] +let ``Basics``() = + + let computation key = node { + do! Async.Sleep 1 |> NodeCode.AwaitAsync + return key * 2 + } + + let eventLog = ResizeArray() + + let memoize = AsyncMemoize(eventLog) + + let task = + NodeCode.Parallel(seq { + memoize.Get(5, computation) + memoize.Get(5, computation) + memoize.Get(2, computation) + memoize.Get(5, computation) + memoize.Get(3, computation) + memoize.Get(2, computation) + }) |> NodeCode.StartAsTask_ForTesting + + let result = task.Result + let expected = [| 10; 10; 4; 10; 6; 4|] + + Assert.Equal(expected, result) + + let groups = eventLog |> Seq.groupBy (fun e -> e.Key) |> Seq.toList + Assert.Equal(3, groups.Length) + for key, events in groups do + Assert.Equal array>([| Started key; Finished key |], events |> Seq.toArray) + +[] +let ``We can cancel a job`` () = + + let computation key = node { + do! Async.Sleep 1000 |> NodeCode.AwaitAsync + failwith "Should be canceled before it gets here" + return key * 2 + } + + let eventLog = ResizeArray() + let memoize = AsyncMemoize(eventLog) + + use cts1 = new CancellationTokenSource() + use cts2 = new CancellationTokenSource() + use cts3 = new CancellationTokenSource() + + let key = 1 + + let _task1 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts1.Token) + let _task2 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts2.Token) + let _task3 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts3.Token) + + Thread.Sleep 10 + + Assert.Equal array>([| Started key |], eventLog |> Seq.toArray ) + + cts1.Cancel() + cts2.Cancel() + + Assert.Equal array>([| Started key |], eventLog |> Seq.toArray ) + + cts3.Cancel() + + Thread.Sleep 10 + + Assert.Equal array>([| Started key; Canceled key |], eventLog |> Seq.toArray ) + + try + Task.WaitAll(_task1, _task2, _task3) + with :? AggregateException as ex -> + Assert.Equal(3, ex.InnerExceptions.Count) + Assert.True(ex.InnerExceptions |> Seq.forall (fun e -> e :? TaskCanceledException)) + +[] +let ``Job keeps running even if first requestor cancels`` () = + let computation key = node { + do! Async.Sleep 100 |> NodeCode.AwaitAsync + return key * 2 + } + + let eventLog = ResizeArray() + let memoize = AsyncMemoize(eventLog) + + use cts1 = new CancellationTokenSource() + use cts2 = new CancellationTokenSource() + use cts3 = new CancellationTokenSource() + + let key = 1 + + let _task1 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts1.Token) + let _task2 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts2.Token) + let _task3 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts3.Token) + + Thread.Sleep 10 + + cts1.Cancel() + cts3.Cancel() + + let result = _task2.Result + Assert.Equal(2, result) + + Assert.Equal array>([| Started key; Finished key |], eventLog |> Seq.toArray ) + diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index f3f67a4020a..2447e2a0906 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -221,7 +221,11 @@ - + + + + %(RelativeDir)TestSource\%(Filename)%(Extension) + @@ -247,11 +251,7 @@ - - - %(RelativeDir)TestSource\%(Filename)%(Extension) - - + From 1059824f747c89f92c140e786a232b6ee926a959 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 5 Apr 2023 16:31:03 +0200 Subject: [PATCH 002/222] IBackgroundCompiler --- src/Compiler/FSharp.Compiler.Service.fsproj | 4 +- src/Compiler/Service/BackgroundCompiler.fs | 1523 +++++++++++++++++++ src/Compiler/Service/TransparentCompiler.fs | 54 + src/Compiler/Service/service.fs | 1136 +------------- 4 files changed, 1581 insertions(+), 1136 deletions(-) create mode 100644 src/Compiler/Service/BackgroundCompiler.fs create mode 100644 src/Compiler/Service/TransparentCompiler.fs diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 56c023a3a46..d6e391936db 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -464,7 +464,9 @@ - + + + diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs new file mode 100644 index 00000000000..3882e06bc8d --- /dev/null +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -0,0 +1,1523 @@ +namespace FSharp.Compiler.CodeAnalysis + +open FSharp.Compiler.Text +open FSharp.Compiler.BuildGraph + +open System +open System.Diagnostics +open System.IO +open System.Reflection +open System.Reflection.Emit +open System.Threading +open Internal.Utilities.Collections +open Internal.Utilities.Library +open Internal.Utilities.Library.Extras +open FSharp.Compiler +open FSharp.Compiler.AbstractIL +open FSharp.Compiler.AbstractIL.IL +open FSharp.Compiler.AbstractIL.ILBinaryReader +open FSharp.Compiler.AbstractIL.ILDynamicAssemblyWriter +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CompilerConfig +open FSharp.Compiler.CompilerDiagnostics +open FSharp.Compiler.CompilerImports +open FSharp.Compiler.CompilerOptions +open FSharp.Compiler.DependencyManager +open FSharp.Compiler.Diagnostics +open FSharp.Compiler.Driver +open FSharp.Compiler.DiagnosticsLogger +open FSharp.Compiler.IO +open FSharp.Compiler.ParseAndCheckInputs +open FSharp.Compiler.ScriptClosure +open FSharp.Compiler.Symbols +open FSharp.Compiler.Syntax +open FSharp.Compiler.Tokenization +open FSharp.Compiler.Text +open FSharp.Compiler.Text.Range +open FSharp.Compiler.TcGlobals +open FSharp.Compiler.BuildGraph + +type SourceTextHash = int64 +type CacheStamp = int64 +type FileName = string +type FilePath = string +type ProjectPath = string +type FileVersion = int + +type internal IBackgroundCompiler = + + /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. + abstract member CheckFileInProject: + parseResults: FSharpParseFileResults * + fileName: string * + fileVersion: int * + sourceText: ISourceText * + options: FSharpProjectOptions * + userOpName: string -> + NodeCode + + /// Type-check the result obtained by parsing, but only if the antecedent type checking context is available. + abstract member CheckFileInProjectAllowingStaleCachedResults: + parseResults: FSharpParseFileResults * + fileName: string * + fileVersion: int * + sourceText: ISourceText * + options: FSharpProjectOptions * + userOpName: string -> + NodeCode + + abstract member ClearCache: options: seq * userOpName: string -> unit + + abstract member ClearCaches: unit -> unit + + abstract member DownsizeCaches: unit -> unit + + abstract member FindReferencesInFile: + fileName: string * + options: FSharpProjectOptions * + symbol: FSharp.Compiler.Symbols.FSharpSymbol * + canInvalidateProject: bool * + userOpName: string -> + NodeCode> + + abstract member GetAssemblyData: + options: FSharpProjectOptions * userOpName: string -> NodeCode + + /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) + abstract member GetBackgroundCheckResultsForFileInProject: + fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode + + /// Fetch the parse information from the background compiler (which checks w.r.t. the FileSystem API) + abstract member GetBackgroundParseResultsForFileInProject: + fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode + + abstract member GetCachedCheckFileResult: + builder: IncrementalBuilder * fileName: string * sourceText: ISourceText * options: FSharpProjectOptions -> + NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> + + abstract member GetProjectOptionsFromScript: + fileName: string * + sourceText: ISourceText * + previewEnabled: bool option * + loadedTimeStamp: System.DateTime option * + otherFlags: string array option * + useFsiAuxLib: bool option * + useSdkRefs: bool option * + sdkDirOverride: string option * + assumeDotNetFramework: bool option * + optionsStamp: int64 option * + userOpName: string -> + Async + + abstract member GetSemanticClassificationForFile: + fileName: string * options: FSharpProjectOptions * userOpName: string -> + NodeCode + + abstract member InvalidateConfiguration: options: FSharpProjectOptions * userOpName: string -> unit + + abstract member NotifyFileChanged: fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode + + abstract member NotifyProjectCleaned: options: FSharpProjectOptions * userOpName: string -> Async + + /// Parses and checks the source file and returns untyped AST and check results. + abstract member ParseAndCheckFileInProject: + fileName: string * fileVersion: int * sourceText: ISourceText * options: FSharpProjectOptions * userOpName: string -> + NodeCode + + /// Parse and typecheck the whole project. + abstract member ParseAndCheckProject: options: FSharpProjectOptions * userOpName: string -> NodeCode + + abstract member ParseFile: + fileName: string * sourceText: ISourceText * options: FSharpParsingOptions * cache: bool * userOpName: string -> + Async + + /// Try to get recent approximate type check results for a file. + abstract member TryGetRecentCheckResultsForFile: + fileName: string * options: FSharpProjectOptions * sourceText: ISourceText option * userOpName: string -> + (FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash) option + + abstract member BeforeBackgroundFileCheck: IEvent + + abstract member FileChecked: IEvent + + abstract member FileParsed: IEvent + + abstract member FrameworkImportsCache: FrameworkImportsCache + + abstract member ProjectChecked: IEvent + +type ParseCacheLockToken() = + interface LockToken + +type ScriptClosureCacheToken() = + interface LockToken + +type CheckFileCacheKey = FileName * SourceTextHash * FSharpProjectOptions +type CheckFileCacheValue = FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash * DateTime + +[] +module EnvMisc = + let braceMatchCacheSize = GetEnvInteger "FCS_BraceMatchCacheSize" 5 + let parseFileCacheSize = GetEnvInteger "FCS_ParseFileCacheSize" 2 + let checkFileInProjectCacheSize = GetEnvInteger "FCS_CheckFileInProjectCacheSize" 10 + + let projectCacheSizeDefault = GetEnvInteger "FCS_ProjectCacheSizeDefault" 3 + + let frameworkTcImportsCacheStrongSize = + GetEnvInteger "FCS_frameworkTcImportsCacheStrongSizeDefault" 8 + +[] +module Helpers = + + /// Determine whether two (fileName,options) keys are identical w.r.t. affect on checking + let AreSameForChecking2 ((fileName1: string, options1: FSharpProjectOptions), (fileName2, options2)) = + (fileName1 = fileName2) + && FSharpProjectOptions.AreSameForChecking(options1, options2) + + /// Determine whether two (fileName,options) keys should be identical w.r.t. resource usage + let AreSubsumable2 ((fileName1: string, o1: FSharpProjectOptions), (fileName2: string, o2: FSharpProjectOptions)) = + (fileName1 = fileName2) && FSharpProjectOptions.UseSameProject(o1, o2) + + /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. parsing + let AreSameForParsing ((fileName1: string, source1Hash: int64, options1), (fileName2, source2Hash, options2)) = + fileName1 = fileName2 && options1 = options2 && source1Hash = source2Hash + + let AreSimilarForParsing ((fileName1, _, _), (fileName2, _, _)) = fileName1 = fileName2 + + /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. checking + let AreSameForChecking3 ((fileName1: string, source1Hash: int64, options1: FSharpProjectOptions), (fileName2, source2Hash, options2)) = + (fileName1 = fileName2) + && FSharpProjectOptions.AreSameForChecking(options1, options2) + && source1Hash = source2Hash + + /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. resource usage + let AreSubsumable3 ((fileName1: string, _, o1: FSharpProjectOptions), (fileName2: string, _, o2: FSharpProjectOptions)) = + (fileName1 = fileName2) && FSharpProjectOptions.UseSameProject(o1, o2) + + /// If a symbol is an attribute check if given set of names contains its name without the Attribute suffix + let rec NamesContainAttribute (symbol: FSharpSymbol) names = + match symbol with + | :? FSharpMemberOrFunctionOrValue as mofov -> + mofov.DeclaringEntity + |> Option.map (fun entity -> NamesContainAttribute entity names) + |> Option.defaultValue false + | :? FSharpEntity as entity when entity.IsAttributeType && symbol.DisplayNameCore.EndsWithOrdinal "Attribute" -> + let nameWithoutAttribute = String.dropSuffix symbol.DisplayNameCore "Attribute" + names |> Set.contains nameWithoutAttribute + | _ -> false + +// There is only one instance of this type, held in FSharpChecker +type internal BackgroundCompiler + ( + legacyReferenceResolver, + projectCacheSize, + keepAssemblyContents, + keepAllBackgroundResolutions, + tryGetMetadataSnapshot, + suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + parallelReferenceResolution, + captureIdentifiersWhenParsing, + getSource: (string -> ISourceText option) option, + useChangeNotifications, + useSyntaxTreeCache + ) as self = + + let beforeFileChecked = Event() + let fileParsed = Event() + let fileChecked = Event() + let projectChecked = Event() + + // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.scriptClosureCache + /// Information about the derived script closure. + let scriptClosureCache = + MruCache( + projectCacheSize, + areSame = FSharpProjectOptions.AreSameForChecking, + areSimilar = FSharpProjectOptions.UseSameProject + ) + + let frameworkTcImportsCache = + FrameworkImportsCache(frameworkTcImportsCacheStrongSize) + + // We currently share one global dependency provider for all scripts for the FSharpChecker. + // For projects, one is used per project. + // + // Sharing one for all scripts is necessary for good performance from GetProjectOptionsFromScript, + // which requires a dependency provider to process through the project options prior to working out + // if the cached incremental builder can be used for the project. + let dependencyProviderForScripts = new DependencyProvider() + + let getProjectReferences (options: FSharpProjectOptions) userOpName = + [ + for r in options.ReferencedProjects do + + match r with + | FSharpReferencedProject.FSharpReference (nm, opts) -> + // Don't use cross-project references for FSharp.Core, since various bits of code + // require a concrete FSharp.Core to exist on-disk. The only solutions that have + // these cross-project references to FSharp.Core are VisualFSharp.sln and FSharp.sln. The ramification + // of this is that you need to build FSharp.Core to get intellisense in those projects. + + if + (try + Path.GetFileNameWithoutExtension(nm) + with _ -> + "") + <> GetFSharpCoreLibraryName() + then + { new IProjectReference with + member x.EvaluateRawContents() = + node { + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) + return! self.GetAssemblyData(opts, userOpName + ".CheckReferencedProject(" + nm + ")") + } + + member x.TryGetLogicalTimeStamp(cache) = + self.TryGetLogicalTimeStampForProject(cache, opts) + + member x.FileName = nm + } + + | FSharpReferencedProject.PEReference (nm, getStamp, delayedReader) -> + { new IProjectReference with + member x.EvaluateRawContents() = + node { + let! ilReaderOpt = delayedReader.TryGetILModuleReader() |> NodeCode.FromCancellable + + match ilReaderOpt with + | Some ilReader -> + let ilModuleDef, ilAsmRefs = ilReader.ILModuleDef, ilReader.ILAssemblyRefs + let data = RawFSharpAssemblyData(ilModuleDef, ilAsmRefs) :> IRawFSharpAssemblyData + return ProjectAssemblyDataResult.Available data + | _ -> + // Note 'false' - if a PEReference doesn't find an ILModuleReader then we don't + // continue to try to use an on-disk DLL + return ProjectAssemblyDataResult.Unavailable false + } + + member x.TryGetLogicalTimeStamp _ = getStamp () |> Some + member x.FileName = nm + } + + | FSharpReferencedProject.ILModuleReference (nm, getStamp, getReader) -> + { new IProjectReference with + member x.EvaluateRawContents() = + node { + let ilReader = getReader () + let ilModuleDef, ilAsmRefs = ilReader.ILModuleDef, ilReader.ILAssemblyRefs + let data = RawFSharpAssemblyData(ilModuleDef, ilAsmRefs) :> IRawFSharpAssemblyData + return ProjectAssemblyDataResult.Available data + } + + member x.TryGetLogicalTimeStamp _ = getStamp () |> Some + member x.FileName = nm + } + ] + + /// CreateOneIncrementalBuilder (for background type checking). Note that fsc.fs also + /// creates an incremental builder used by the command line compiler. + let CreateOneIncrementalBuilder (options: FSharpProjectOptions, userOpName) = + node { + use _ = + Activity.start "BackgroundCompiler.CreateOneIncrementalBuilder" [| Activity.Tags.project, options.ProjectFileName |] + + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CreateOneIncrementalBuilder", options.ProjectFileName) + let projectReferences = getProjectReferences options userOpName + + let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) + + let dependencyProvider = + if options.UseScriptResolutionRules then + Some dependencyProviderForScripts + else + None + + let! builderOpt, diagnostics = + IncrementalBuilder.TryCreateIncrementalBuilderForProjectOptions( + legacyReferenceResolver, + FSharpCheckerResultsSettings.defaultFSharpBinariesDir, + frameworkTcImportsCache, + loadClosure, + Array.toList options.SourceFiles, + Array.toList options.OtherOptions, + projectReferences, + options.ProjectDirectory, + options.UseScriptResolutionRules, + keepAssemblyContents, + keepAllBackgroundResolutions, + tryGetMetadataSnapshot, + suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + dependencyProvider, + parallelReferenceResolution, + captureIdentifiersWhenParsing, + getSource, + useChangeNotifications, + useSyntaxTreeCache + ) + + match builderOpt with + | None -> () + | Some builder -> + +#if !NO_TYPEPROVIDERS + // Register the behaviour that responds to CCUs being invalidated because of type + // provider Invalidate events. This invalidates the configuration in the build. + builder.ImportsInvalidatedByTypeProvider.Add(fun () -> self.InvalidateConfiguration(options, userOpName)) +#endif + + // Register the callback called just before a file is typechecked by the background builder (without recording + // errors or intellisense information). + // + // This indicates to the UI that the file type check state is dirty. If the file is open and visible then + // the UI will sooner or later request a typecheck of the file, recording errors and intellisense information. + builder.BeforeFileChecked.Add(fun file -> beforeFileChecked.Trigger(file, options)) + builder.FileParsed.Add(fun file -> fileParsed.Trigger(file, options)) + builder.FileChecked.Add(fun file -> fileChecked.Trigger(file, options)) + builder.ProjectChecked.Add(fun () -> projectChecked.Trigger options) + + return (builderOpt, diagnostics) + } + + let parseCacheLock = Lock() + + // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.parseFileInProjectCache. Most recently used cache for parsing files. + let parseFileCache = + MruCache( + parseFileCacheSize, + areSimilar = AreSimilarForParsing, + areSame = AreSameForParsing + ) + + // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.checkFileInProjectCache + // + /// Cache which holds recently seen type-checks. + /// This cache may hold out-of-date entries, in two senses + /// - there may be a more recent antecedent state available because the background build has made it available + /// - the source for the file may have changed + + // Also keyed on source. This can only be out of date if the antecedent is out of date + let checkFileInProjectCache = + MruCache>( + keepStrongly = checkFileInProjectCacheSize, + areSame = AreSameForChecking3, + areSimilar = AreSubsumable3 + ) + + // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.incrementalBuildersCache. This root typically holds more + // live information than anything else in the F# Language Service, since it holds up to 3 (projectCacheStrongSize) background project builds + // strongly. + // + /// Cache of builds keyed by options. + let gate = obj () + + let incrementalBuildersCache = + MruCache>( + keepStrongly = projectCacheSize, + keepMax = projectCacheSize, + areSame = FSharpProjectOptions.AreSameForChecking, + areSimilar = FSharpProjectOptions.UseSameProject + ) + + let tryGetBuilderNode options = + incrementalBuildersCache.TryGet(AnyCallerThread, options) + + let tryGetBuilder options : NodeCode option = + tryGetBuilderNode options |> Option.map (fun x -> x.GetOrComputeValue()) + + let tryGetSimilarBuilder options : NodeCode option = + incrementalBuildersCache.TryGetSimilar(AnyCallerThread, options) + |> Option.map (fun x -> x.GetOrComputeValue()) + + let tryGetAnyBuilder options : NodeCode option = + incrementalBuildersCache.TryGetAny(AnyCallerThread, options) + |> Option.map (fun x -> x.GetOrComputeValue()) + + let createBuilderNode (options, userOpName, ct: CancellationToken) = + lock gate (fun () -> + if ct.IsCancellationRequested then + GraphNode.FromResult(None, [||]) + else + let getBuilderNode = GraphNode(CreateOneIncrementalBuilder(options, userOpName)) + incrementalBuildersCache.Set(AnyCallerThread, options, getBuilderNode) + getBuilderNode) + + let createAndGetBuilder (options, userOpName) = + node { + let! ct = NodeCode.CancellationToken + let getBuilderNode = createBuilderNode (options, userOpName, ct) + return! getBuilderNode.GetOrComputeValue() + } + + let getOrCreateBuilder (options, userOpName) : NodeCode = + match tryGetBuilder options with + | Some getBuilder -> + node { + match! getBuilder with + | builderOpt, creationDiags when builderOpt.IsNone || not builderOpt.Value.IsReferencesInvalidated -> + return builderOpt, creationDiags + | _ -> + // The builder could be re-created, + // clear the check file caches that are associated with it. + // We must do this in order to not return stale results when references + // in the project get changed/added/removed. + parseCacheLock.AcquireLock(fun ltok -> + options.SourceFiles + |> Array.iter (fun sourceFile -> + let key = (sourceFile, 0L, options) + checkFileInProjectCache.RemoveAnySimilar(ltok, key))) + + return! createAndGetBuilder (options, userOpName) + } + | _ -> createAndGetBuilder (options, userOpName) + + let getSimilarOrCreateBuilder (options, userOpName) = + match tryGetSimilarBuilder options with + | Some res -> res + // The builder does not exist at all. Create it. + | None -> getOrCreateBuilder (options, userOpName) + + let getOrCreateBuilderWithInvalidationFlag (options, canInvalidateProject, userOpName) = + if canInvalidateProject then + getOrCreateBuilder (options, userOpName) + else + getSimilarOrCreateBuilder (options, userOpName) + + let getAnyBuilder (options, userOpName) = + match tryGetAnyBuilder options with + | Some getBuilder -> getBuilder + | _ -> getOrCreateBuilder (options, userOpName) + + static let mutable actualParseFileCount = 0 + + static let mutable actualCheckFileCount = 0 + + /// Should be a fast operation. Ensures that we have only one async lazy object per file and its hash. + let getCheckFileNode (parseResults, sourceText, fileName, options, _fileVersion, builder, tcPrior, tcInfo, creationDiags) = + + // Here we lock for the creation of the node, not its execution + parseCacheLock.AcquireLock(fun ltok -> + let key = (fileName, sourceText.GetHashCode() |> int64, options) + + match checkFileInProjectCache.TryGet(ltok, key) with + | Some res -> res + | _ -> + let res = + GraphNode( + node { + let! res = + self.CheckOneFileImplAux( + parseResults, + sourceText, + fileName, + options, + builder, + tcPrior, + tcInfo, + creationDiags + ) + + Interlocked.Increment(&actualCheckFileCount) |> ignore + return res + } + ) + + checkFileInProjectCache.Set(ltok, key, res) + res) + + member _.ParseFile(fileName: string, sourceText: ISourceText, options: FSharpParsingOptions, cache: bool, userOpName: string) = + async { + use _ = + Activity.start + "BackgroundCompiler.ParseFile" + [| + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + Activity.Tags.cache, cache.ToString() + |] + + if cache then + let hash = sourceText.GetHashCode() |> int64 + + match parseCacheLock.AcquireLock(fun ltok -> parseFileCache.TryGet(ltok, (fileName, hash, options))) with + | Some res -> return res + | None -> + Interlocked.Increment(&actualParseFileCount) |> ignore + + let parseDiagnostics, parseTree, anyErrors = + ParseAndCheckFile.parseFile ( + sourceText, + fileName, + options, + userOpName, + suggestNamesForErrors, + captureIdentifiersWhenParsing + ) + + let res = + FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, options.SourceFiles) + + parseCacheLock.AcquireLock(fun ltok -> parseFileCache.Set(ltok, (fileName, hash, options), res)) + return res + else + let parseDiagnostics, parseTree, anyErrors = + ParseAndCheckFile.parseFile (sourceText, fileName, options, userOpName, false, captureIdentifiersWhenParsing) + + return FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, options.SourceFiles) + } + + /// Fetch the parse information from the background compiler (which checks w.r.t. the FileSystem API) + member _.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName) = + node { + use _ = + Activity.start + "BackgroundCompiler.GetBackgroundParseResultsForFileInProject" + [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, userOpName |] + + let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> + let parseTree = EmptyParsedInput(fileName, (false, false)) + return FSharpParseFileResults(creationDiags, parseTree, true, [||]) + | Some builder -> + let parseTree, _, _, parseDiagnostics = builder.GetParseResultsForFile fileName + + let parseDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + builder.TcConfig.diagnosticsOptions, + false, + fileName, + parseDiagnostics, + suggestNamesForErrors + ) + + let diagnostics = [| yield! creationDiags; yield! parseDiagnostics |] + + let parseResults = + FSharpParseFileResults( + diagnostics = diagnostics, + input = parseTree, + parseHadErrors = false, + dependencyFiles = builder.AllDependenciesDeprecated + ) + + return parseResults + } + + member _.GetCachedCheckFileResult(builder: IncrementalBuilder, fileName, sourceText: ISourceText, options) = + node { + use _ = + Activity.start "BackgroundCompiler.GetCachedCheckFileResult" [| Activity.Tags.fileName, fileName |] + + let hash = sourceText.GetHashCode() |> int64 + let key = (fileName, hash, options) + + let cachedResultsOpt = + parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.TryGet(ltok, key)) + + match cachedResultsOpt with + | Some cachedResults -> + match! cachedResults.GetOrComputeValue() with + | parseResults, checkResults, _, priorTimeStamp when + (match builder.GetCheckResultsBeforeFileInProjectEvenIfStale fileName with + | None -> false + | Some (tcPrior) -> + tcPrior.ProjectTimeStamp = priorTimeStamp + && builder.AreCheckResultsBeforeFileInProjectReady(fileName)) + -> + return Some(parseResults, checkResults) + | _ -> + parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.RemoveAnySimilar(ltok, key)) + return None + | _ -> return None + } + + member private _.CheckOneFileImplAux + ( + parseResults: FSharpParseFileResults, + sourceText: ISourceText, + fileName: string, + options: FSharpProjectOptions, + builder: IncrementalBuilder, + tcPrior: PartialCheckResults, + tcInfo: TcInfo, + creationDiags: FSharpDiagnostic[] + ) : NodeCode = + + node { + // Get additional script #load closure information if applicable. + // For scripts, this will have been recorded by GetProjectOptionsFromScript. + let tcConfig = tcPrior.TcConfig + let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) + + let! checkAnswer = + FSharpCheckFileResults.CheckOneFile( + parseResults, + sourceText, + fileName, + options.ProjectFileName, + tcConfig, + tcPrior.TcGlobals, + tcPrior.TcImports, + tcInfo.tcState, + tcInfo.moduleNamesDict, + loadClosure, + tcInfo.TcDiagnostics, + options.IsIncompleteTypeCheckEnvironment, + options, + builder, + Array.ofList tcInfo.tcDependencyFiles, + creationDiags, + parseResults.Diagnostics, + keepAssemblyContents, + suggestNamesForErrors + ) + |> NodeCode.FromCancellable + + GraphNode.SetPreferredUILang tcConfig.preferredUiLang + return (parseResults, checkAnswer, sourceText.GetHashCode() |> int64, tcPrior.ProjectTimeStamp) + } + + member private bc.CheckOneFileImpl + ( + parseResults: FSharpParseFileResults, + sourceText: ISourceText, + fileName: string, + options: FSharpProjectOptions, + fileVersion: int, + builder: IncrementalBuilder, + tcPrior: PartialCheckResults, + tcInfo: TcInfo, + creationDiags: FSharpDiagnostic[] + ) = + + node { + match! bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) with + | Some (_, results) -> return FSharpCheckFileAnswer.Succeeded results + | _ -> + let lazyCheckFile = + getCheckFileNode (parseResults, sourceText, fileName, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) + + let! _, results, _, _ = lazyCheckFile.GetOrComputeValue() + return FSharpCheckFileAnswer.Succeeded results + } + + /// Type-check the result obtained by parsing, but only if the antecedent type checking context is available. + member bc.CheckFileInProjectAllowingStaleCachedResults + ( + parseResults: FSharpParseFileResults, + fileName, + fileVersion, + sourceText: ISourceText, + options, + userOpName + ) = + node { + use _ = + Activity.start + "BackgroundCompiler.CheckFileInProjectAllowingStaleCachedResults" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + |] + + let! cachedResults = + node { + let! builderOpt, creationDiags = getAnyBuilder (options, userOpName) + + match builderOpt with + | Some builder -> + match! bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) with + | Some (_, checkResults) -> return Some(builder, creationDiags, Some(FSharpCheckFileAnswer.Succeeded checkResults)) + | _ -> return Some(builder, creationDiags, None) + | _ -> return None // the builder wasn't ready + } + + match cachedResults with + | None -> return None + | Some (_, _, Some x) -> return Some x + | Some (builder, creationDiags, None) -> + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CheckFileInProjectAllowingStaleCachedResults.CacheMiss", fileName) + + match builder.GetCheckResultsBeforeFileInProjectEvenIfStale fileName with + | Some tcPrior -> + match tcPrior.TryPeekTcInfo() with + | Some tcInfo -> + let! checkResults = + bc.CheckOneFileImpl( + parseResults, + sourceText, + fileName, + options, + fileVersion, + builder, + tcPrior, + tcInfo, + creationDiags + ) + + return Some checkResults + | None -> return None + | None -> return None // the incremental builder was not up to date + } + + /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. + member bc.CheckFileInProject + ( + parseResults: FSharpParseFileResults, + fileName, + fileVersion, + sourceText: ISourceText, + options, + userOpName + ) = + node { + use _ = + Activity.start + "BackgroundCompiler.CheckFileInProject" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + |] + + let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> + return FSharpCheckFileAnswer.Succeeded(FSharpCheckFileResults.MakeEmpty(fileName, creationDiags, keepAssemblyContents)) + | Some builder -> + // Check the cache. We can only use cached results when there is no work to do to bring the background builder up-to-date + let! cachedResults = bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) + + match cachedResults with + | Some (_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults + | _ -> + let! tcPrior = builder.GetCheckResultsBeforeFileInProject fileName + let! tcInfo = tcPrior.GetOrComputeTcInfo() + + return! + bc.CheckOneFileImpl( + parseResults, + sourceText, + fileName, + options, + fileVersion, + builder, + tcPrior, + tcInfo, + creationDiags + ) + } + + /// Parses and checks the source file and returns untyped AST and check results. + member bc.ParseAndCheckFileInProject + ( + fileName: string, + fileVersion, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName + ) = + node { + use _ = + Activity.start + "BackgroundCompiler.ParseAndCheckFileInProject" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + |] + + let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> + let parseTree = EmptyParsedInput(fileName, (false, false)) + let parseResults = FSharpParseFileResults(creationDiags, parseTree, true, [||]) + return (parseResults, FSharpCheckFileAnswer.Aborted) + + | Some builder -> + let! cachedResults = bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) + + match cachedResults with + | Some (parseResults, checkResults) -> return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) + | _ -> + let! tcPrior = builder.GetCheckResultsBeforeFileInProject fileName + let! tcInfo = tcPrior.GetOrComputeTcInfo() + // Do the parsing. + let parsingOptions = + FSharpParsingOptions.FromTcConfig( + builder.TcConfig, + Array.ofList builder.SourceFiles, + options.UseScriptResolutionRules + ) + + GraphNode.SetPreferredUILang tcPrior.TcConfig.preferredUiLang + + let parseDiagnostics, parseTree, anyErrors = + ParseAndCheckFile.parseFile ( + sourceText, + fileName, + parsingOptions, + userOpName, + suggestNamesForErrors, + captureIdentifiersWhenParsing + ) + + let parseResults = + FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, builder.AllDependenciesDeprecated) + + let! checkResults = + bc.CheckOneFileImpl( + parseResults, + sourceText, + fileName, + options, + fileVersion, + builder, + tcPrior, + tcInfo, + creationDiags + ) + + return (parseResults, checkResults) + } + + member _.NotifyFileChanged(fileName, options, userOpName) = + node { + use _ = + Activity.start + "BackgroundCompiler.NotifyFileChanged" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + |] + + let! builderOpt, _ = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> return () + | Some builder -> do! builder.NotifyFileChanged(fileName, DateTime.UtcNow) + } + + /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) + member _.GetBackgroundCheckResultsForFileInProject(fileName, options, userOpName) = + node { + use _ = + Activity.start + "BackgroundCompiler.ParseAndCheckFileInProject" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + |] + + let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> + let parseTree = EmptyParsedInput(fileName, (false, false)) + let parseResults = FSharpParseFileResults(creationDiags, parseTree, true, [||]) + let typedResults = FSharpCheckFileResults.MakeEmpty(fileName, creationDiags, true) + return (parseResults, typedResults) + | Some builder -> + let parseTree, _, _, parseDiagnostics = builder.GetParseResultsForFile fileName + let! tcProj = builder.GetFullCheckResultsAfterFileInProject fileName + + let! tcInfo, tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() + + let tcResolutions = tcInfoExtras.tcResolutions + let tcSymbolUses = tcInfoExtras.tcSymbolUses + let tcOpenDeclarations = tcInfoExtras.tcOpenDeclarations + let latestCcuSigForFile = tcInfo.latestCcuSigForFile + let tcState = tcInfo.tcState + let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile + let latestImplementationFile = tcInfoExtras.latestImplFile + let tcDependencyFiles = tcInfo.tcDependencyFiles + let tcDiagnostics = tcInfo.TcDiagnostics + let diagnosticsOptions = builder.TcConfig.diagnosticsOptions + + let parseDiagnostics = + DiagnosticHelpers.CreateDiagnostics(diagnosticsOptions, false, fileName, parseDiagnostics, suggestNamesForErrors) + + let parseDiagnostics = [| yield! creationDiags; yield! parseDiagnostics |] + + let tcDiagnostics = + DiagnosticHelpers.CreateDiagnostics(diagnosticsOptions, false, fileName, tcDiagnostics, suggestNamesForErrors) + + let tcDiagnostics = [| yield! creationDiags; yield! tcDiagnostics |] + + let parseResults = + FSharpParseFileResults( + diagnostics = parseDiagnostics, + input = parseTree, + parseHadErrors = false, + dependencyFiles = builder.AllDependenciesDeprecated + ) + + let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) + + let typedResults = + FSharpCheckFileResults.Make( + fileName, + options.ProjectFileName, + tcProj.TcConfig, + tcProj.TcGlobals, + options.IsIncompleteTypeCheckEnvironment, + builder, + options, + Array.ofList tcDependencyFiles, + creationDiags, + parseResults.Diagnostics, + tcDiagnostics, + keepAssemblyContents, + Option.get latestCcuSigForFile, + tcState.Ccu, + tcProj.TcImports, + tcEnvAtEnd.AccessRights, + tcResolutions, + tcSymbolUses, + tcEnvAtEnd.NameEnv, + loadClosure, + latestImplementationFile, + tcOpenDeclarations + ) + + return (parseResults, typedResults) + } + + member _.FindReferencesInFile + ( + fileName: string, + options: FSharpProjectOptions, + symbol: FSharpSymbol, + canInvalidateProject: bool, + userOpName: string + ) = + node { + use _ = + Activity.start + "BackgroundCompiler.FindReferencesInFile" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + "symbol", symbol.FullName + |] + + let! builderOpt, _ = getOrCreateBuilderWithInvalidationFlag (options, canInvalidateProject, userOpName) + + match builderOpt with + | None -> return Seq.empty + | Some builder -> + if builder.ContainsFile fileName then + let! checkResults = builder.GetFullCheckResultsAfterFileInProject fileName + let! keyStoreOpt = checkResults.GetOrComputeItemKeyStoreIfEnabled() + + match keyStoreOpt with + | None -> return Seq.empty + | Some reader -> return reader.FindAll symbol.Item + else + return Seq.empty + } + + member _.GetSemanticClassificationForFile(fileName: string, options: FSharpProjectOptions, userOpName: string) = + node { + use _ = + Activity.start + "BackgroundCompiler.GetSemanticClassificationForFile" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, userOpName + |] + + let! builderOpt, _ = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> return None + | Some builder -> + let! checkResults = builder.GetFullCheckResultsAfterFileInProject fileName + let! scopt = checkResults.GetOrComputeSemanticClassificationIfEnabled() + + match scopt with + | None -> return None + | Some sc -> return Some(sc.GetView()) + } + + /// Try to get recent approximate type check results for a file. + member _.TryGetRecentCheckResultsForFile + ( + fileName: string, + options: FSharpProjectOptions, + sourceText: ISourceText option, + _userOpName: string + ) = + use _ = + Activity.start + "BackgroundCompiler.GetSemanticClassificationForFile" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.fileName, fileName + Activity.Tags.userOpName, _userOpName + |] + + match sourceText with + | Some sourceText -> + let hash = sourceText.GetHashCode() |> int64 + + let resOpt = + parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.TryGet(ltok, (fileName, hash, options))) + + match resOpt with + | Some res -> + match res.TryPeekValue() with + | ValueSome (a, b, c, _) -> Some(a, b, c) + | ValueNone -> None + | None -> None + | None -> None + + /// Parse and typecheck the whole project (the implementation, called recursively as project graph is evaluated) + member private _.ParseAndCheckProjectImpl(options, userOpName) = + node { + let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> + let emptyResults = + FSharpCheckProjectResults(options.ProjectFileName, None, keepAssemblyContents, creationDiags, None) + + return emptyResults + | Some builder -> + let! tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt = builder.GetFullCheckResultsAndImplementationsForProject() + let diagnosticsOptions = tcProj.TcConfig.diagnosticsOptions + let fileName = DummyFileNameForRangesWithoutASpecificLocation + + // Although we do not use 'tcInfoExtras', computing it will make sure we get an extra info. + let! tcInfo, _tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() + + let topAttribs = tcInfo.topAttribs + let tcState = tcInfo.tcState + let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile + let tcDiagnostics = tcInfo.TcDiagnostics + let tcDependencyFiles = tcInfo.tcDependencyFiles + + let tcDiagnostics = + DiagnosticHelpers.CreateDiagnostics(diagnosticsOptions, true, fileName, tcDiagnostics, suggestNamesForErrors) + + let diagnostics = [| yield! creationDiags; yield! tcDiagnostics |] + + let getAssemblyData () = + match tcAssemblyDataOpt with + | ProjectAssemblyDataResult.Available data -> Some data + | _ -> None + + let details = + (tcProj.TcGlobals, + tcProj.TcImports, + tcState.Ccu, + tcState.CcuSig, + Choice1Of2 builder, + topAttribs, + getAssemblyData, + ilAssemRef, + tcEnvAtEnd.AccessRights, + tcAssemblyExprOpt, + Array.ofList tcDependencyFiles, + options) + + let results = + FSharpCheckProjectResults( + options.ProjectFileName, + Some tcProj.TcConfig, + keepAssemblyContents, + diagnostics, + Some details + ) + + return results + } + + member _.GetAssemblyData(options, userOpName) = + node { + use _ = + Activity.start + "BackgroundCompiler.GetAssemblyData" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.userOpName, userOpName + |] + + let! builderOpt, _ = getOrCreateBuilder (options, userOpName) + + match builderOpt with + | None -> return ProjectAssemblyDataResult.Unavailable true + | Some builder -> + let! _, _, tcAssemblyDataOpt, _ = builder.GetCheckResultsAndImplementationsForProject() + return tcAssemblyDataOpt + } + + /// Get the timestamp that would be on the output if fully built immediately + member private _.TryGetLogicalTimeStampForProject(cache, options) = + match tryGetBuilderNode options with + | Some lazyWork -> + match lazyWork.TryPeekValue() with + | ValueSome (Some builder, _) -> Some(builder.GetLogicalTimeStampForProject(cache)) + | _ -> None + | _ -> None + + /// Parse and typecheck the whole project. + member bc.ParseAndCheckProject(options, userOpName) = + use _ = + Activity.start + "BackgroundCompiler.ParseAndCheckProject" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.userOpName, userOpName + |] + + bc.ParseAndCheckProjectImpl(options, userOpName) + + member _.GetProjectOptionsFromScript + ( + fileName, + sourceText, + previewEnabled, + loadedTimeStamp, + otherFlags, + useFsiAuxLib: bool option, + useSdkRefs: bool option, + sdkDirOverride: string option, + assumeDotNetFramework: bool option, + optionsStamp: int64 option, + _userOpName + ) = + use _ = + Activity.start + "BackgroundCompiler.GetProjectOptionsFromScript" + [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, _userOpName |] + + cancellable { + use diagnostics = new DiagnosticsScope() + + // Do we add a reference to FSharp.Compiler.Interactive.Settings by default? + let useFsiAuxLib = defaultArg useFsiAuxLib true + let useSdkRefs = defaultArg useSdkRefs true + let reduceMemoryUsage = ReduceMemoryFlag.Yes + let previewEnabled = defaultArg previewEnabled false + + // Do we assume .NET Framework references for scripts? + let assumeDotNetFramework = defaultArg assumeDotNetFramework true + + let extraFlags = + if previewEnabled then + [| "--langversion:preview" |] + else + [||] + + let otherFlags = defaultArg otherFlags extraFlags + + let useSimpleResolution = + otherFlags |> Array.exists (fun x -> x = "--simpleresolution") + + let loadedTimeStamp = defaultArg loadedTimeStamp DateTime.MaxValue // Not 'now', we don't want to force reloading + + let applyCompilerOptions tcConfigB = + let fsiCompilerOptions = GetCoreFsiCompilerOptions tcConfigB + ParseCompilerOptions(ignore, fsiCompilerOptions, Array.toList otherFlags) + + let loadClosure = + LoadClosure.ComputeClosureOfScriptText( + legacyReferenceResolver, + FSharpCheckerResultsSettings.defaultFSharpBinariesDir, + fileName, + sourceText, + CodeContext.Editing, + useSimpleResolution, + useFsiAuxLib, + useSdkRefs, + sdkDirOverride, + Lexhelp.LexResourceManager(), + applyCompilerOptions, + assumeDotNetFramework, + tryGetMetadataSnapshot, + reduceMemoryUsage, + dependencyProviderForScripts + ) + + let otherFlags = + [| + yield "--noframework" + yield "--warn:3" + yield! otherFlags + for r in loadClosure.References do + yield "-r:" + fst r + for code, _ in loadClosure.NoWarns do + yield "--nowarn:" + code + |] + + let options = + { + ProjectFileName = fileName + ".fsproj" // Make a name that is unique in this directory. + ProjectId = None + SourceFiles = loadClosure.SourceFiles |> List.map fst |> List.toArray + OtherOptions = otherFlags + ReferencedProjects = [||] + IsIncompleteTypeCheckEnvironment = false + UseScriptResolutionRules = true + LoadTime = loadedTimeStamp + UnresolvedReferences = Some(FSharpUnresolvedReferencesSet(loadClosure.UnresolvedReferences)) + OriginalLoadReferences = loadClosure.OriginalLoadReferences + Stamp = optionsStamp + } + + scriptClosureCache.Set(AnyCallerThread, options, loadClosure) // Save the full load closure for later correlation. + + let diags = + loadClosure.LoadClosureRootFileDiagnostics + |> List.map (fun (exn, isError) -> FSharpDiagnostic.CreateFromException(exn, isError, range.Zero, false)) + + return options, (diags @ diagnostics.Diagnostics) + } + |> Cancellable.toAsync + + member bc.InvalidateConfiguration(options: FSharpProjectOptions, userOpName) = + use _ = + Activity.start + "BackgroundCompiler.InvalidateConfiguration" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.userOpName, userOpName + |] + + if incrementalBuildersCache.ContainsSimilarKey(AnyCallerThread, options) then + parseCacheLock.AcquireLock(fun ltok -> + for sourceFile in options.SourceFiles do + checkFileInProjectCache.RemoveAnySimilar(ltok, (sourceFile, 0L, options))) + + let _ = createBuilderNode (options, userOpName, CancellationToken.None) + () + + member bc.ClearCache(options: seq, _userOpName) = + use _ = + Activity.start "BackgroundCompiler.ClearCache" [| Activity.Tags.userOpName, _userOpName |] + + lock gate (fun () -> + options + |> Seq.iter (fun options -> incrementalBuildersCache.RemoveAnySimilar(AnyCallerThread, options))) + + member _.NotifyProjectCleaned(options: FSharpProjectOptions, userOpName) = + use _ = + Activity.start + "BackgroundCompiler.NotifyProjectCleaned" + [| + Activity.Tags.project, options.ProjectFileName + Activity.Tags.userOpName, userOpName + |] + + async { + let! ct = Async.CancellationToken + // If there was a similar entry (as there normally will have been) then re-establish an empty builder . This + // is a somewhat arbitrary choice - it will have the effect of releasing memory associated with the previous + // builder, but costs some time. + if incrementalBuildersCache.ContainsSimilarKey(AnyCallerThread, options) then + let _ = createBuilderNode (options, userOpName, ct) + () + } + + member _.BeforeBackgroundFileCheck = beforeFileChecked.Publish + + member _.FileParsed = fileParsed.Publish + + member _.FileChecked = fileChecked.Publish + + member _.ProjectChecked = projectChecked.Publish + + member _.ClearCaches() = + use _ = Activity.startNoTags "BackgroundCompiler.ClearCaches" + + lock gate (fun () -> + parseCacheLock.AcquireLock(fun ltok -> + checkFileInProjectCache.Clear(ltok) + parseFileCache.Clear(ltok)) + + incrementalBuildersCache.Clear(AnyCallerThread) + frameworkTcImportsCache.Clear() + scriptClosureCache.Clear AnyCallerThread) + + member _.DownsizeCaches() = + use _ = Activity.startNoTags "BackgroundCompiler.DownsizeCaches" + + lock gate (fun () -> + parseCacheLock.AcquireLock(fun ltok -> + checkFileInProjectCache.Resize(ltok, newKeepStrongly = 1) + parseFileCache.Resize(ltok, newKeepStrongly = 1)) + + incrementalBuildersCache.Resize(AnyCallerThread, newKeepStrongly = 1, newKeepMax = 1) + frameworkTcImportsCache.Downsize() + scriptClosureCache.Resize(AnyCallerThread, newKeepStrongly = 1, newKeepMax = 1)) + + member _.FrameworkImportsCache = frameworkTcImportsCache + + static member ActualParseFileCount = actualParseFileCount + + static member ActualCheckFileCount = actualCheckFileCount + + interface IBackgroundCompiler with + member _.BeforeBackgroundFileCheck = self.BeforeBackgroundFileCheck + + member _.CheckFileInProject + ( + parseResults: FSharpParseFileResults, + fileName: string, + fileVersion: int, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + self.CheckFileInProject(parseResults, fileName, fileVersion, sourceText, options, userOpName) + + member _.CheckFileInProjectAllowingStaleCachedResults + ( + parseResults: FSharpParseFileResults, + fileName: string, + fileVersion: int, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + self.CheckFileInProjectAllowingStaleCachedResults(parseResults, fileName, fileVersion, sourceText, options, userOpName) + + member _.ClearCache(options: seq, userOpName: string) : unit = self.ClearCache(options, userOpName) + member _.ClearCaches() : unit = self.ClearCaches() + member _.DownsizeCaches() : unit = self.DownsizeCaches() + member _.FileChecked: IEvent = self.FileChecked + member _.FileParsed: IEvent = self.FileParsed + + member _.FindReferencesInFile + ( + fileName: string, + options: FSharpProjectOptions, + symbol: FSharpSymbol, + canInvalidateProject: bool, + userOpName: string + ) : NodeCode> = + self.FindReferencesInFile(fileName, options, symbol, canInvalidateProject, userOpName) + + member _.FrameworkImportsCache: FrameworkImportsCache = self.FrameworkImportsCache + + member _.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = + self.GetAssemblyData(options, userOpName) + + member _.GetBackgroundCheckResultsForFileInProject + ( + fileName: string, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + self.GetBackgroundCheckResultsForFileInProject(fileName, options, userOpName) + + member _.GetBackgroundParseResultsForFileInProject + ( + fileName: string, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + self.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName) + + member _.GetCachedCheckFileResult + ( + builder: IncrementalBuilder, + fileName: string, + sourceText: ISourceText, + options: FSharpProjectOptions + ) : NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> = + self.GetCachedCheckFileResult(builder, fileName, sourceText, options) + + member _.GetProjectOptionsFromScript + ( + fileName: string, + sourceText: ISourceText, + previewEnabled: bool option, + loadedTimeStamp: DateTime option, + otherFlags: string array option, + useFsiAuxLib: bool option, + useSdkRefs: bool option, + sdkDirOverride: string option, + assumeDotNetFramework: bool option, + optionsStamp: int64 option, + userOpName: string + ) : Async = + self.GetProjectOptionsFromScript( + fileName, + sourceText, + previewEnabled, + loadedTimeStamp, + otherFlags, + useFsiAuxLib, + useSdkRefs, + sdkDirOverride, + assumeDotNetFramework, + optionsStamp, + userOpName + ) + + member _.GetSemanticClassificationForFile + ( + fileName: string, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + self.GetSemanticClassificationForFile(fileName, options, userOpName) + + member _.InvalidateConfiguration(options: FSharpProjectOptions, userOpName: string) : unit = + self.InvalidateConfiguration(options, userOpName) + + member _.NotifyFileChanged(fileName: string, options: FSharpProjectOptions, userOpName: string) : NodeCode = + self.NotifyFileChanged(fileName, options, userOpName) + + member _.NotifyProjectCleaned(options: FSharpProjectOptions, userOpName: string) : Async = + self.NotifyProjectCleaned(options, userOpName) + + member _.ParseAndCheckFileInProject + ( + fileName: string, + fileVersion: int, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + self.ParseAndCheckFileInProject(fileName, fileVersion, sourceText, options, userOpName) + + member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = + self.ParseAndCheckProject(options, userOpName) + + member _.ParseFile + ( + fileName: string, + sourceText: ISourceText, + options: FSharpParsingOptions, + cache: bool, + userOpName: string + ) : Async = + self.ParseFile(fileName, sourceText, options, cache, userOpName) + + member _.ProjectChecked: IEvent = self.ProjectChecked + + member _.TryGetRecentCheckResultsForFile + ( + fileName: string, + options: FSharpProjectOptions, + sourceText: ISourceText option, + userOpName: string + ) : (FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash) option = + self.TryGetRecentCheckResultsForFile(fileName, options, sourceText, userOpName) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs new file mode 100644 index 00000000000..28d5996b5b0 --- /dev/null +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -0,0 +1,54 @@ +namespace FSharp.Compiler.CodeAnalysis + + +type internal TransparentCompiler() = + + interface IBackgroundCompiler with + member this.BeforeBackgroundFileCheck: IEvent = + raise (System.NotImplementedException()) + member this.CheckFileInProject(parseResults: FSharpParseFileResults, fileName: string, fileVersion: int, sourceText: FSharp.Compiler.Text.ISourceText, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = + raise (System.NotImplementedException()) + member this.CheckFileInProjectAllowingStaleCachedResults(parseResults: FSharpParseFileResults, fileName: string, fileVersion: int, sourceText: FSharp.Compiler.Text.ISourceText, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = + raise (System.NotImplementedException()) + member this.ClearCache(options: seq, userOpName: string): unit = + raise (System.NotImplementedException()) + member this.ClearCaches(): unit = + raise (System.NotImplementedException()) + member this.DownsizeCaches(): unit = + raise (System.NotImplementedException()) + member this.FileChecked: IEvent = + raise (System.NotImplementedException()) + member this.FileParsed: IEvent = + raise (System.NotImplementedException()) + member this.FindReferencesInFile(fileName: string, options: FSharpProjectOptions, symbol: FSharp.Compiler.Symbols.FSharpSymbol, canInvalidateProject: bool, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode> = + raise (System.NotImplementedException()) + member this.FrameworkImportsCache: FrameworkImportsCache = + raise (System.NotImplementedException()) + member this.GetAssemblyData(options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = + raise (System.NotImplementedException()) + member this.GetBackgroundCheckResultsForFileInProject(fileName: string, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = + raise (System.NotImplementedException()) + member this.GetBackgroundParseResultsForFileInProject(fileName: string, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = + raise (System.NotImplementedException()) + member this.GetCachedCheckFileResult(builder: IncrementalBuilder, fileName: string, sourceText: FSharp.Compiler.Text.ISourceText, options: FSharpProjectOptions): FSharp.Compiler.BuildGraph.NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> = + raise (System.NotImplementedException()) + member this.GetProjectOptionsFromScript(fileName: string, sourceText: FSharp.Compiler.Text.ISourceText, previewEnabled: bool option, loadedTimeStamp: System.DateTime option, otherFlags: string array option, useFsiAuxLib: bool option, useSdkRefs: bool option, sdkDirOverride: string option, assumeDotNetFramework: bool option, optionsStamp: int64 option, userOpName: string): Async = + raise (System.NotImplementedException()) + member this.GetSemanticClassificationForFile(fileName: string, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = + raise (System.NotImplementedException()) + member this.InvalidateConfiguration(options: FSharpProjectOptions, userOpName: string): unit = + raise (System.NotImplementedException()) + member this.NotifyFileChanged(fileName: string, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = + raise (System.NotImplementedException()) + member this.NotifyProjectCleaned(options: FSharpProjectOptions, userOpName: string): Async = + raise (System.NotImplementedException()) + member this.ParseAndCheckFileInProject(fileName: string, fileVersion: int, sourceText: FSharp.Compiler.Text.ISourceText, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = + raise (System.NotImplementedException()) + member this.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = + raise (System.NotImplementedException()) + member this.ParseFile(fileName: string, sourceText: FSharp.Compiler.Text.ISourceText, options: FSharpParsingOptions, cache: bool, userOpName: string): Async = + raise (System.NotImplementedException()) + member this.ProjectChecked: IEvent = + raise (System.NotImplementedException()) + member this.TryGetRecentCheckResultsForFile(fileName: string, options: FSharpProjectOptions, sourceText: FSharp.Compiler.Text.ISourceText option, userOpName: string): (FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash) option = + raise (System.NotImplementedException()) \ No newline at end of file diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index cf5487e25df..8b6a9c1766a 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -36,19 +36,6 @@ open FSharp.Compiler.Text.Range open FSharp.Compiler.TcGlobals open FSharp.Compiler.BuildGraph -[] -module EnvMisc = - let braceMatchCacheSize = GetEnvInteger "FCS_BraceMatchCacheSize" 5 - let parseFileCacheSize = GetEnvInteger "FCS_ParseFileCacheSize" 2 - let checkFileInProjectCacheSize = GetEnvInteger "FCS_CheckFileInProjectCacheSize" 10 - - let projectCacheSizeDefault = GetEnvInteger "FCS_ProjectCacheSizeDefault" 3 - let frameworkTcImportsCacheStrongSize = GetEnvInteger "FCS_frameworkTcImportsCacheStrongSizeDefault" 8 - -//---------------------------------------------------------------------------- -// BackgroundCompiler -// - [] type DocumentSource = | FileSystem @@ -58,46 +45,6 @@ type DocumentSource = [] type IsResultObsolete = IsResultObsolete of (unit -> bool) -[] -module Helpers = - - /// Determine whether two (fileName,options) keys are identical w.r.t. affect on checking - let AreSameForChecking2 ((fileName1: string, options1: FSharpProjectOptions), (fileName2, options2)) = - (fileName1 = fileName2) - && FSharpProjectOptions.AreSameForChecking(options1, options2) - - /// Determine whether two (fileName,options) keys should be identical w.r.t. resource usage - let AreSubsumable2 ((fileName1: string, o1: FSharpProjectOptions), (fileName2: string, o2: FSharpProjectOptions)) = - (fileName1 = fileName2) && FSharpProjectOptions.UseSameProject(o1, o2) - - /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. parsing - let AreSameForParsing ((fileName1: string, source1Hash: int64, options1), (fileName2, source2Hash, options2)) = - fileName1 = fileName2 && options1 = options2 && source1Hash = source2Hash - - let AreSimilarForParsing ((fileName1, _, _), (fileName2, _, _)) = fileName1 = fileName2 - - /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. checking - let AreSameForChecking3 ((fileName1: string, source1Hash: int64, options1: FSharpProjectOptions), (fileName2, source2Hash, options2)) = - (fileName1 = fileName2) - && FSharpProjectOptions.AreSameForChecking(options1, options2) - && source1Hash = source2Hash - - /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. resource usage - let AreSubsumable3 ((fileName1: string, _, o1: FSharpProjectOptions), (fileName2: string, _, o2: FSharpProjectOptions)) = - (fileName1 = fileName2) && FSharpProjectOptions.UseSameProject(o1, o2) - - /// If a symbol is an attribute check if given set of names contains its name without the Attribute suffix - let rec NamesContainAttribute (symbol: FSharpSymbol) names = - match symbol with - | :? FSharpMemberOrFunctionOrValue as mofov -> - mofov.DeclaringEntity - |> Option.map (fun entity -> NamesContainAttribute entity names) - |> Option.defaultValue false - | :? FSharpEntity as entity when entity.IsAttributeType && symbol.DisplayNameCore.EndsWithOrdinal "Attribute" -> - let nameWithoutAttribute = String.dropSuffix symbol.DisplayNameCore "Attribute" - names |> Set.contains nameWithoutAttribute - | _ -> false - module CompileHelpers = let mkCompilationDiagnosticsHandlers () = let diagnostics = ResizeArray<_>() @@ -164,1088 +111,6 @@ module CompileHelpers = Console.SetError error | None -> () -type SourceTextHash = int64 -type CacheStamp = int64 -type FileName = string -type FilePath = string -type ProjectPath = string -type FileVersion = int - -type ParseCacheLockToken() = - interface LockToken - -type ScriptClosureCacheToken() = - interface LockToken - -type CheckFileCacheKey = FileName * SourceTextHash * FSharpProjectOptions -type CheckFileCacheValue = FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash * DateTime - -// There is only one instance of this type, held in FSharpChecker -type BackgroundCompiler - ( - legacyReferenceResolver, - projectCacheSize, - keepAssemblyContents, - keepAllBackgroundResolutions, - tryGetMetadataSnapshot, - suggestNamesForErrors, - keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification, - enablePartialTypeChecking, - parallelReferenceResolution, - captureIdentifiersWhenParsing, - getSource: (string -> ISourceText option) option, - useChangeNotifications, - useSyntaxTreeCache - ) as self = - - let beforeFileChecked = Event() - let fileParsed = Event() - let fileChecked = Event() - let projectChecked = Event() - - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.scriptClosureCache - /// Information about the derived script closure. - let scriptClosureCache = - MruCache( - projectCacheSize, - areSame = FSharpProjectOptions.AreSameForChecking, - areSimilar = FSharpProjectOptions.UseSameProject - ) - - let frameworkTcImportsCache = FrameworkImportsCache(frameworkTcImportsCacheStrongSize) - - // We currently share one global dependency provider for all scripts for the FSharpChecker. - // For projects, one is used per project. - // - // Sharing one for all scripts is necessary for good performance from GetProjectOptionsFromScript, - // which requires a dependency provider to process through the project options prior to working out - // if the cached incremental builder can be used for the project. - let dependencyProviderForScripts = new DependencyProvider() - - let getProjectReferences (options: FSharpProjectOptions) userOpName = - [ - for r in options.ReferencedProjects do - - match r with - | FSharpReferencedProject.FSharpReference (nm, opts) -> - // Don't use cross-project references for FSharp.Core, since various bits of code - // require a concrete FSharp.Core to exist on-disk. The only solutions that have - // these cross-project references to FSharp.Core are VisualFSharp.sln and FSharp.sln. The ramification - // of this is that you need to build FSharp.Core to get intellisense in those projects. - - if - (try - Path.GetFileNameWithoutExtension(nm) - with _ -> - "") - <> GetFSharpCoreLibraryName() - then - { new IProjectReference with - member x.EvaluateRawContents() = - node { - Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) - return! self.GetAssemblyData(opts, userOpName + ".CheckReferencedProject(" + nm + ")") - } - - member x.TryGetLogicalTimeStamp(cache) = - self.TryGetLogicalTimeStampForProject(cache, opts) - - member x.FileName = nm - } - - | FSharpReferencedProject.PEReference (nm, getStamp, delayedReader) -> - { new IProjectReference with - member x.EvaluateRawContents() = - node { - let! ilReaderOpt = delayedReader.TryGetILModuleReader() |> NodeCode.FromCancellable - - match ilReaderOpt with - | Some ilReader -> - let ilModuleDef, ilAsmRefs = ilReader.ILModuleDef, ilReader.ILAssemblyRefs - let data = RawFSharpAssemblyData(ilModuleDef, ilAsmRefs) :> IRawFSharpAssemblyData - return ProjectAssemblyDataResult.Available data - | _ -> - // Note 'false' - if a PEReference doesn't find an ILModuleReader then we don't - // continue to try to use an on-disk DLL - return ProjectAssemblyDataResult.Unavailable false - } - - member x.TryGetLogicalTimeStamp _ = getStamp () |> Some - member x.FileName = nm - } - - | FSharpReferencedProject.ILModuleReference (nm, getStamp, getReader) -> - { new IProjectReference with - member x.EvaluateRawContents() = - node { - let ilReader = getReader () - let ilModuleDef, ilAsmRefs = ilReader.ILModuleDef, ilReader.ILAssemblyRefs - let data = RawFSharpAssemblyData(ilModuleDef, ilAsmRefs) :> IRawFSharpAssemblyData - return ProjectAssemblyDataResult.Available data - } - - member x.TryGetLogicalTimeStamp _ = getStamp () |> Some - member x.FileName = nm - } - ] - - /// CreateOneIncrementalBuilder (for background type checking). Note that fsc.fs also - /// creates an incremental builder used by the command line compiler. - let CreateOneIncrementalBuilder (options: FSharpProjectOptions, userOpName) = - node { - use _ = - Activity.start "BackgroundCompiler.CreateOneIncrementalBuilder" [| Activity.Tags.project, options.ProjectFileName |] - - Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CreateOneIncrementalBuilder", options.ProjectFileName) - let projectReferences = getProjectReferences options userOpName - - let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) - - let dependencyProvider = - if options.UseScriptResolutionRules then - Some dependencyProviderForScripts - else - None - - let! builderOpt, diagnostics = - IncrementalBuilder.TryCreateIncrementalBuilderForProjectOptions( - legacyReferenceResolver, - FSharpCheckerResultsSettings.defaultFSharpBinariesDir, - frameworkTcImportsCache, - loadClosure, - Array.toList options.SourceFiles, - Array.toList options.OtherOptions, - projectReferences, - options.ProjectDirectory, - options.UseScriptResolutionRules, - keepAssemblyContents, - keepAllBackgroundResolutions, - tryGetMetadataSnapshot, - suggestNamesForErrors, - keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification, - enablePartialTypeChecking, - dependencyProvider, - parallelReferenceResolution, - captureIdentifiersWhenParsing, - getSource, - useChangeNotifications, - useSyntaxTreeCache - ) - - match builderOpt with - | None -> () - | Some builder -> - -#if !NO_TYPEPROVIDERS - // Register the behaviour that responds to CCUs being invalidated because of type - // provider Invalidate events. This invalidates the configuration in the build. - builder.ImportsInvalidatedByTypeProvider.Add(fun () -> self.InvalidateConfiguration(options, userOpName)) -#endif - - // Register the callback called just before a file is typechecked by the background builder (without recording - // errors or intellisense information). - // - // This indicates to the UI that the file type check state is dirty. If the file is open and visible then - // the UI will sooner or later request a typecheck of the file, recording errors and intellisense information. - builder.BeforeFileChecked.Add(fun file -> beforeFileChecked.Trigger(file, options)) - builder.FileParsed.Add(fun file -> fileParsed.Trigger(file, options)) - builder.FileChecked.Add(fun file -> fileChecked.Trigger(file, options)) - builder.ProjectChecked.Add(fun () -> projectChecked.Trigger options) - - return (builderOpt, diagnostics) - } - - let parseCacheLock = Lock() - - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.parseFileInProjectCache. Most recently used cache for parsing files. - let parseFileCache = - MruCache(parseFileCacheSize, areSimilar = AreSimilarForParsing, areSame = AreSameForParsing) - - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.checkFileInProjectCache - // - /// Cache which holds recently seen type-checks. - /// This cache may hold out-of-date entries, in two senses - /// - there may be a more recent antecedent state available because the background build has made it available - /// - the source for the file may have changed - - // Also keyed on source. This can only be out of date if the antecedent is out of date - let checkFileInProjectCache = - MruCache>( - keepStrongly = checkFileInProjectCacheSize, - areSame = AreSameForChecking3, - areSimilar = AreSubsumable3 - ) - - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.incrementalBuildersCache. This root typically holds more - // live information than anything else in the F# Language Service, since it holds up to 3 (projectCacheStrongSize) background project builds - // strongly. - // - /// Cache of builds keyed by options. - let gate = obj () - - let incrementalBuildersCache = - MruCache>( - keepStrongly = projectCacheSize, - keepMax = projectCacheSize, - areSame = FSharpProjectOptions.AreSameForChecking, - areSimilar = FSharpProjectOptions.UseSameProject - ) - - let tryGetBuilderNode options = - incrementalBuildersCache.TryGet(AnyCallerThread, options) - - let tryGetBuilder options : NodeCode option = - tryGetBuilderNode options |> Option.map (fun x -> x.GetOrComputeValue()) - - let tryGetSimilarBuilder options : NodeCode option = - incrementalBuildersCache.TryGetSimilar(AnyCallerThread, options) - |> Option.map (fun x -> x.GetOrComputeValue()) - - let tryGetAnyBuilder options : NodeCode option = - incrementalBuildersCache.TryGetAny(AnyCallerThread, options) - |> Option.map (fun x -> x.GetOrComputeValue()) - - let createBuilderNode (options, userOpName, ct: CancellationToken) = - lock gate (fun () -> - if ct.IsCancellationRequested then - GraphNode.FromResult(None, [||]) - else - let getBuilderNode = GraphNode(CreateOneIncrementalBuilder(options, userOpName)) - incrementalBuildersCache.Set(AnyCallerThread, options, getBuilderNode) - getBuilderNode) - - let createAndGetBuilder (options, userOpName) = - node { - let! ct = NodeCode.CancellationToken - let getBuilderNode = createBuilderNode (options, userOpName, ct) - return! getBuilderNode.GetOrComputeValue() - } - - let getOrCreateBuilder (options, userOpName) : NodeCode = - match tryGetBuilder options with - | Some getBuilder -> - node { - match! getBuilder with - | builderOpt, creationDiags when builderOpt.IsNone || not builderOpt.Value.IsReferencesInvalidated -> return builderOpt, creationDiags - | _ -> - // The builder could be re-created, - // clear the check file caches that are associated with it. - // We must do this in order to not return stale results when references - // in the project get changed/added/removed. - parseCacheLock.AcquireLock(fun ltok -> - options.SourceFiles - |> Array.iter (fun sourceFile -> - let key = (sourceFile, 0L, options) - checkFileInProjectCache.RemoveAnySimilar(ltok, key))) - - return! createAndGetBuilder (options, userOpName) - } - | _ -> createAndGetBuilder (options, userOpName) - - let getSimilarOrCreateBuilder (options, userOpName) = - match tryGetSimilarBuilder options with - | Some res -> res - // The builder does not exist at all. Create it. - | None -> getOrCreateBuilder (options, userOpName) - - let getOrCreateBuilderWithInvalidationFlag (options, canInvalidateProject, userOpName) = - if canInvalidateProject then - getOrCreateBuilder (options, userOpName) - else - getSimilarOrCreateBuilder (options, userOpName) - - let getAnyBuilder (options, userOpName) = - match tryGetAnyBuilder options with - | Some getBuilder -> getBuilder - | _ -> getOrCreateBuilder (options, userOpName) - - static let mutable actualParseFileCount = 0 - - static let mutable actualCheckFileCount = 0 - - /// Should be a fast operation. Ensures that we have only one async lazy object per file and its hash. - let getCheckFileNode (parseResults, sourceText, fileName, options, _fileVersion, builder, tcPrior, tcInfo, creationDiags) = - - // Here we lock for the creation of the node, not its execution - parseCacheLock.AcquireLock(fun ltok -> - let key = (fileName, sourceText.GetHashCode() |> int64, options) - - match checkFileInProjectCache.TryGet(ltok, key) with - | Some res -> res - | _ -> - let res = - GraphNode( - node { - let! res = self.CheckOneFileImplAux(parseResults, sourceText, fileName, options, builder, tcPrior, tcInfo, creationDiags) - Interlocked.Increment(&actualCheckFileCount) |> ignore - return res - } - ) - - checkFileInProjectCache.Set(ltok, key, res) - res) - - member _.ParseFile(fileName: string, sourceText: ISourceText, options: FSharpParsingOptions, cache: bool, userOpName: string) = - async { - use _ = - Activity.start - "BackgroundCompiler.ParseFile" - [| - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - Activity.Tags.cache, cache.ToString() - |] - - if cache then - let hash = sourceText.GetHashCode() |> int64 - - match parseCacheLock.AcquireLock(fun ltok -> parseFileCache.TryGet(ltok, (fileName, hash, options))) with - | Some res -> return res - | None -> - Interlocked.Increment(&actualParseFileCount) |> ignore - - let parseDiagnostics, parseTree, anyErrors = - ParseAndCheckFile.parseFile (sourceText, fileName, options, userOpName, suggestNamesForErrors, captureIdentifiersWhenParsing) - - let res = FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, options.SourceFiles) - parseCacheLock.AcquireLock(fun ltok -> parseFileCache.Set(ltok, (fileName, hash, options), res)) - return res - else - let parseDiagnostics, parseTree, anyErrors = - ParseAndCheckFile.parseFile (sourceText, fileName, options, userOpName, false, captureIdentifiersWhenParsing) - - return FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, options.SourceFiles) - } - - /// Fetch the parse information from the background compiler (which checks w.r.t. the FileSystem API) - member _.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName) = - node { - use _ = - Activity.start - "BackgroundCompiler.GetBackgroundParseResultsForFileInProject" - [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, userOpName |] - - let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> - let parseTree = EmptyParsedInput(fileName, (false, false)) - return FSharpParseFileResults(creationDiags, parseTree, true, [||]) - | Some builder -> - let parseTree, _, _, parseDiagnostics = builder.GetParseResultsForFile fileName - - let parseDiagnostics = - DiagnosticHelpers.CreateDiagnostics(builder.TcConfig.diagnosticsOptions, false, fileName, parseDiagnostics, suggestNamesForErrors) - - let diagnostics = [| yield! creationDiags; yield! parseDiagnostics |] - - let parseResults = - FSharpParseFileResults( - diagnostics = diagnostics, - input = parseTree, - parseHadErrors = false, - dependencyFiles = builder.AllDependenciesDeprecated - ) - - return parseResults - } - - member _.GetCachedCheckFileResult(builder: IncrementalBuilder, fileName, sourceText: ISourceText, options) = - node { - use _ = - Activity.start "BackgroundCompiler.GetCachedCheckFileResult" [| Activity.Tags.fileName, fileName |] - - let hash = sourceText.GetHashCode() |> int64 - let key = (fileName, hash, options) - let cachedResultsOpt = parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.TryGet(ltok, key)) - - match cachedResultsOpt with - | Some cachedResults -> - match! cachedResults.GetOrComputeValue() with - | parseResults, checkResults, _, priorTimeStamp when - (match builder.GetCheckResultsBeforeFileInProjectEvenIfStale fileName with - | None -> false - | Some (tcPrior) -> - tcPrior.ProjectTimeStamp = priorTimeStamp - && builder.AreCheckResultsBeforeFileInProjectReady(fileName)) - -> - return Some(parseResults, checkResults) - | _ -> - parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.RemoveAnySimilar(ltok, key)) - return None - | _ -> return None - } - - member private _.CheckOneFileImplAux - ( - parseResults: FSharpParseFileResults, - sourceText: ISourceText, - fileName: string, - options: FSharpProjectOptions, - builder: IncrementalBuilder, - tcPrior: PartialCheckResults, - tcInfo: TcInfo, - creationDiags: FSharpDiagnostic[] - ) : NodeCode = - - node { - // Get additional script #load closure information if applicable. - // For scripts, this will have been recorded by GetProjectOptionsFromScript. - let tcConfig = tcPrior.TcConfig - let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) - - let! checkAnswer = - FSharpCheckFileResults.CheckOneFile( - parseResults, - sourceText, - fileName, - options.ProjectFileName, - tcConfig, - tcPrior.TcGlobals, - tcPrior.TcImports, - tcInfo.tcState, - tcInfo.moduleNamesDict, - loadClosure, - tcInfo.TcDiagnostics, - options.IsIncompleteTypeCheckEnvironment, - options, - builder, - Array.ofList tcInfo.tcDependencyFiles, - creationDiags, - parseResults.Diagnostics, - keepAssemblyContents, - suggestNamesForErrors - ) - |> NodeCode.FromCancellable - - GraphNode.SetPreferredUILang tcConfig.preferredUiLang - return (parseResults, checkAnswer, sourceText.GetHashCode() |> int64, tcPrior.ProjectTimeStamp) - } - - member private bc.CheckOneFileImpl - ( - parseResults: FSharpParseFileResults, - sourceText: ISourceText, - fileName: string, - options: FSharpProjectOptions, - fileVersion: int, - builder: IncrementalBuilder, - tcPrior: PartialCheckResults, - tcInfo: TcInfo, - creationDiags: FSharpDiagnostic[] - ) = - - node { - match! bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) with - | Some (_, results) -> return FSharpCheckFileAnswer.Succeeded results - | _ -> - let lazyCheckFile = - getCheckFileNode (parseResults, sourceText, fileName, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) - - let! _, results, _, _ = lazyCheckFile.GetOrComputeValue() - return FSharpCheckFileAnswer.Succeeded results - } - - /// Type-check the result obtained by parsing, but only if the antecedent type checking context is available. - member bc.CheckFileInProjectAllowingStaleCachedResults - ( - parseResults: FSharpParseFileResults, - fileName, - fileVersion, - sourceText: ISourceText, - options, - userOpName - ) = - node { - use _ = - Activity.start - "BackgroundCompiler.CheckFileInProjectAllowingStaleCachedResults" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - |] - - let! cachedResults = - node { - let! builderOpt, creationDiags = getAnyBuilder (options, userOpName) - - match builderOpt with - | Some builder -> - match! bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) with - | Some (_, checkResults) -> return Some(builder, creationDiags, Some(FSharpCheckFileAnswer.Succeeded checkResults)) - | _ -> return Some(builder, creationDiags, None) - | _ -> return None // the builder wasn't ready - } - - match cachedResults with - | None -> return None - | Some (_, _, Some x) -> return Some x - | Some (builder, creationDiags, None) -> - Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CheckFileInProjectAllowingStaleCachedResults.CacheMiss", fileName) - - match builder.GetCheckResultsBeforeFileInProjectEvenIfStale fileName with - | Some tcPrior -> - match tcPrior.TryPeekTcInfo() with - | Some tcInfo -> - let! checkResults = - bc.CheckOneFileImpl(parseResults, sourceText, fileName, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) - - return Some checkResults - | None -> return None - | None -> return None // the incremental builder was not up to date - } - - /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. - member bc.CheckFileInProject(parseResults: FSharpParseFileResults, fileName, fileVersion, sourceText: ISourceText, options, userOpName) = - node { - use _ = - Activity.start - "BackgroundCompiler.CheckFileInProject" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - |] - - let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> return FSharpCheckFileAnswer.Succeeded(FSharpCheckFileResults.MakeEmpty(fileName, creationDiags, keepAssemblyContents)) - | Some builder -> - // Check the cache. We can only use cached results when there is no work to do to bring the background builder up-to-date - let! cachedResults = bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) - - match cachedResults with - | Some (_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults - | _ -> - let! tcPrior = builder.GetCheckResultsBeforeFileInProject fileName - let! tcInfo = tcPrior.GetOrComputeTcInfo() - return! bc.CheckOneFileImpl(parseResults, sourceText, fileName, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) - } - - /// Parses and checks the source file and returns untyped AST and check results. - member bc.ParseAndCheckFileInProject(fileName: string, fileVersion, sourceText: ISourceText, options: FSharpProjectOptions, userOpName) = - node { - use _ = - Activity.start - "BackgroundCompiler.ParseAndCheckFileInProject" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - |] - - let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> - let parseTree = EmptyParsedInput(fileName, (false, false)) - let parseResults = FSharpParseFileResults(creationDiags, parseTree, true, [||]) - return (parseResults, FSharpCheckFileAnswer.Aborted) - - | Some builder -> - let! cachedResults = bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) - - match cachedResults with - | Some (parseResults, checkResults) -> return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) - | _ -> - let! tcPrior = builder.GetCheckResultsBeforeFileInProject fileName - let! tcInfo = tcPrior.GetOrComputeTcInfo() - // Do the parsing. - let parsingOptions = - FSharpParsingOptions.FromTcConfig(builder.TcConfig, Array.ofList builder.SourceFiles, options.UseScriptResolutionRules) - - GraphNode.SetPreferredUILang tcPrior.TcConfig.preferredUiLang - - let parseDiagnostics, parseTree, anyErrors = - ParseAndCheckFile.parseFile ( - sourceText, - fileName, - parsingOptions, - userOpName, - suggestNamesForErrors, - captureIdentifiersWhenParsing - ) - - let parseResults = - FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, builder.AllDependenciesDeprecated) - - let! checkResults = - bc.CheckOneFileImpl(parseResults, sourceText, fileName, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) - - return (parseResults, checkResults) - } - - member _.NotifyFileChanged(fileName, options, userOpName) = - node { - use _ = - Activity.start - "BackgroundCompiler.NotifyFileChanged" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - |] - - let! builderOpt, _ = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> return () - | Some builder -> do! builder.NotifyFileChanged(fileName, DateTime.UtcNow) - } - - /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) - member _.GetBackgroundCheckResultsForFileInProject(fileName, options, userOpName) = - node { - use _ = - Activity.start - "BackgroundCompiler.ParseAndCheckFileInProject" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - |] - - let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> - let parseTree = EmptyParsedInput(fileName, (false, false)) - let parseResults = FSharpParseFileResults(creationDiags, parseTree, true, [||]) - let typedResults = FSharpCheckFileResults.MakeEmpty(fileName, creationDiags, true) - return (parseResults, typedResults) - | Some builder -> - let parseTree, _, _, parseDiagnostics = builder.GetParseResultsForFile fileName - let! tcProj = builder.GetFullCheckResultsAfterFileInProject fileName - - let! tcInfo, tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() - - let tcResolutions = tcInfoExtras.tcResolutions - let tcSymbolUses = tcInfoExtras.tcSymbolUses - let tcOpenDeclarations = tcInfoExtras.tcOpenDeclarations - let latestCcuSigForFile = tcInfo.latestCcuSigForFile - let tcState = tcInfo.tcState - let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile - let latestImplementationFile = tcInfoExtras.latestImplFile - let tcDependencyFiles = tcInfo.tcDependencyFiles - let tcDiagnostics = tcInfo.TcDiagnostics - let diagnosticsOptions = builder.TcConfig.diagnosticsOptions - - let parseDiagnostics = - DiagnosticHelpers.CreateDiagnostics(diagnosticsOptions, false, fileName, parseDiagnostics, suggestNamesForErrors) - - let parseDiagnostics = [| yield! creationDiags; yield! parseDiagnostics |] - - let tcDiagnostics = - DiagnosticHelpers.CreateDiagnostics(diagnosticsOptions, false, fileName, tcDiagnostics, suggestNamesForErrors) - - let tcDiagnostics = [| yield! creationDiags; yield! tcDiagnostics |] - - let parseResults = - FSharpParseFileResults( - diagnostics = parseDiagnostics, - input = parseTree, - parseHadErrors = false, - dependencyFiles = builder.AllDependenciesDeprecated - ) - - let loadClosure = scriptClosureCache.TryGet(AnyCallerThread, options) - - let typedResults = - FSharpCheckFileResults.Make( - fileName, - options.ProjectFileName, - tcProj.TcConfig, - tcProj.TcGlobals, - options.IsIncompleteTypeCheckEnvironment, - builder, - options, - Array.ofList tcDependencyFiles, - creationDiags, - parseResults.Diagnostics, - tcDiagnostics, - keepAssemblyContents, - Option.get latestCcuSigForFile, - tcState.Ccu, - tcProj.TcImports, - tcEnvAtEnd.AccessRights, - tcResolutions, - tcSymbolUses, - tcEnvAtEnd.NameEnv, - loadClosure, - latestImplementationFile, - tcOpenDeclarations - ) - - return (parseResults, typedResults) - } - - member _.FindReferencesInFile - ( - fileName: string, - options: FSharpProjectOptions, - symbol: FSharpSymbol, - canInvalidateProject: bool, - userOpName: string - ) = - node { - use _ = - Activity.start - "BackgroundCompiler.FindReferencesInFile" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - "symbol", symbol.FullName - |] - - let! builderOpt, _ = getOrCreateBuilderWithInvalidationFlag (options, canInvalidateProject, userOpName) - - match builderOpt with - | None -> return Seq.empty - | Some builder -> - if builder.ContainsFile fileName then - let! checkResults = builder.GetFullCheckResultsAfterFileInProject fileName - let! keyStoreOpt = checkResults.GetOrComputeItemKeyStoreIfEnabled() - - match keyStoreOpt with - | None -> return Seq.empty - | Some reader -> return reader.FindAll symbol.Item - else - return Seq.empty - } - - member _.GetSemanticClassificationForFile(fileName: string, options: FSharpProjectOptions, userOpName: string) = - node { - use _ = - Activity.start - "BackgroundCompiler.GetSemanticClassificationForFile" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, userOpName - |] - - let! builderOpt, _ = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> return None - | Some builder -> - let! checkResults = builder.GetFullCheckResultsAfterFileInProject fileName - let! scopt = checkResults.GetOrComputeSemanticClassificationIfEnabled() - - match scopt with - | None -> return None - | Some sc -> return Some(sc.GetView()) - } - - /// Try to get recent approximate type check results for a file. - member _.TryGetRecentCheckResultsForFile(fileName: string, options: FSharpProjectOptions, sourceText: ISourceText option, _userOpName: string) = - use _ = - Activity.start - "BackgroundCompiler.GetSemanticClassificationForFile" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.fileName, fileName - Activity.Tags.userOpName, _userOpName - |] - - match sourceText with - | Some sourceText -> - let hash = sourceText.GetHashCode() |> int64 - - let resOpt = - parseCacheLock.AcquireLock(fun ltok -> checkFileInProjectCache.TryGet(ltok, (fileName, hash, options))) - - match resOpt with - | Some res -> - match res.TryPeekValue() with - | ValueSome (a, b, c, _) -> Some(a, b, c) - | ValueNone -> None - | None -> None - | None -> None - - /// Parse and typecheck the whole project (the implementation, called recursively as project graph is evaluated) - member private _.ParseAndCheckProjectImpl(options, userOpName) = - node { - let! builderOpt, creationDiags = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> - let emptyResults = - FSharpCheckProjectResults(options.ProjectFileName, None, keepAssemblyContents, creationDiags, None) - - return emptyResults - | Some builder -> - let! tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt = builder.GetFullCheckResultsAndImplementationsForProject() - let diagnosticsOptions = tcProj.TcConfig.diagnosticsOptions - let fileName = DummyFileNameForRangesWithoutASpecificLocation - - // Although we do not use 'tcInfoExtras', computing it will make sure we get an extra info. - let! tcInfo, _tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() - - let topAttribs = tcInfo.topAttribs - let tcState = tcInfo.tcState - let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile - let tcDiagnostics = tcInfo.TcDiagnostics - let tcDependencyFiles = tcInfo.tcDependencyFiles - - let tcDiagnostics = - DiagnosticHelpers.CreateDiagnostics(diagnosticsOptions, true, fileName, tcDiagnostics, suggestNamesForErrors) - - let diagnostics = [| yield! creationDiags; yield! tcDiagnostics |] - - let getAssemblyData () = - match tcAssemblyDataOpt with - | ProjectAssemblyDataResult.Available data -> Some data - | _ -> None - - let details = - (tcProj.TcGlobals, - tcProj.TcImports, - tcState.Ccu, - tcState.CcuSig, - Choice1Of2 builder, - topAttribs, - getAssemblyData, - ilAssemRef, - tcEnvAtEnd.AccessRights, - tcAssemblyExprOpt, - Array.ofList tcDependencyFiles, - options) - - let results = - FSharpCheckProjectResults(options.ProjectFileName, Some tcProj.TcConfig, keepAssemblyContents, diagnostics, Some details) - - return results - } - - member _.GetAssemblyData(options, userOpName) = - node { - use _ = - Activity.start - "BackgroundCompiler.GetAssemblyData" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.userOpName, userOpName - |] - - let! builderOpt, _ = getOrCreateBuilder (options, userOpName) - - match builderOpt with - | None -> return ProjectAssemblyDataResult.Unavailable true - | Some builder -> - let! _, _, tcAssemblyDataOpt, _ = builder.GetCheckResultsAndImplementationsForProject() - return tcAssemblyDataOpt - } - - /// Get the timestamp that would be on the output if fully built immediately - member private _.TryGetLogicalTimeStampForProject(cache, options) = - match tryGetBuilderNode options with - | Some lazyWork -> - match lazyWork.TryPeekValue() with - | ValueSome (Some builder, _) -> Some(builder.GetLogicalTimeStampForProject(cache)) - | _ -> None - | _ -> None - - /// Parse and typecheck the whole project. - member bc.ParseAndCheckProject(options, userOpName) = - use _ = - Activity.start - "BackgroundCompiler.ParseAndCheckProject" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.userOpName, userOpName - |] - - bc.ParseAndCheckProjectImpl(options, userOpName) - - member _.GetProjectOptionsFromScript - ( - fileName, - sourceText, - previewEnabled, - loadedTimeStamp, - otherFlags, - useFsiAuxLib: bool option, - useSdkRefs: bool option, - sdkDirOverride: string option, - assumeDotNetFramework: bool option, - optionsStamp: int64 option, - _userOpName - ) = - use _ = - Activity.start - "BackgroundCompiler.GetProjectOptionsFromScript" - [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, _userOpName |] - - cancellable { - use diagnostics = new DiagnosticsScope() - - // Do we add a reference to FSharp.Compiler.Interactive.Settings by default? - let useFsiAuxLib = defaultArg useFsiAuxLib true - let useSdkRefs = defaultArg useSdkRefs true - let reduceMemoryUsage = ReduceMemoryFlag.Yes - let previewEnabled = defaultArg previewEnabled false - - // Do we assume .NET Framework references for scripts? - let assumeDotNetFramework = defaultArg assumeDotNetFramework true - - let extraFlags = - if previewEnabled then - [| "--langversion:preview" |] - else - [||] - - let otherFlags = defaultArg otherFlags extraFlags - - let useSimpleResolution = otherFlags |> Array.exists (fun x -> x = "--simpleresolution") - - let loadedTimeStamp = defaultArg loadedTimeStamp DateTime.MaxValue // Not 'now', we don't want to force reloading - - let applyCompilerOptions tcConfigB = - let fsiCompilerOptions = GetCoreFsiCompilerOptions tcConfigB - ParseCompilerOptions(ignore, fsiCompilerOptions, Array.toList otherFlags) - - let loadClosure = - LoadClosure.ComputeClosureOfScriptText( - legacyReferenceResolver, - FSharpCheckerResultsSettings.defaultFSharpBinariesDir, - fileName, - sourceText, - CodeContext.Editing, - useSimpleResolution, - useFsiAuxLib, - useSdkRefs, - sdkDirOverride, - Lexhelp.LexResourceManager(), - applyCompilerOptions, - assumeDotNetFramework, - tryGetMetadataSnapshot, - reduceMemoryUsage, - dependencyProviderForScripts - ) - - let otherFlags = - [| - yield "--noframework" - yield "--warn:3" - yield! otherFlags - for r in loadClosure.References do - yield "-r:" + fst r - for code, _ in loadClosure.NoWarns do - yield "--nowarn:" + code - |] - - let options = - { - ProjectFileName = fileName + ".fsproj" // Make a name that is unique in this directory. - ProjectId = None - SourceFiles = loadClosure.SourceFiles |> List.map fst |> List.toArray - OtherOptions = otherFlags - ReferencedProjects = [||] - IsIncompleteTypeCheckEnvironment = false - UseScriptResolutionRules = true - LoadTime = loadedTimeStamp - UnresolvedReferences = Some(FSharpUnresolvedReferencesSet(loadClosure.UnresolvedReferences)) - OriginalLoadReferences = loadClosure.OriginalLoadReferences - Stamp = optionsStamp - } - - scriptClosureCache.Set(AnyCallerThread, options, loadClosure) // Save the full load closure for later correlation. - - let diags = - loadClosure.LoadClosureRootFileDiagnostics - |> List.map (fun (exn, isError) -> FSharpDiagnostic.CreateFromException(exn, isError, range.Zero, false)) - - return options, (diags @ diagnostics.Diagnostics) - } - |> Cancellable.toAsync - - member bc.InvalidateConfiguration(options: FSharpProjectOptions, userOpName) = - use _ = - Activity.start - "BackgroundCompiler.InvalidateConfiguration" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.userOpName, userOpName - |] - - if incrementalBuildersCache.ContainsSimilarKey(AnyCallerThread, options) then - parseCacheLock.AcquireLock(fun ltok -> - for sourceFile in options.SourceFiles do - checkFileInProjectCache.RemoveAnySimilar(ltok, (sourceFile, 0L, options))) - - let _ = createBuilderNode (options, userOpName, CancellationToken.None) - () - - member bc.ClearCache(options: seq, _userOpName) = - use _ = Activity.start "BackgroundCompiler.ClearCache" [| Activity.Tags.userOpName, _userOpName |] - - lock gate (fun () -> - options - |> Seq.iter (fun options -> incrementalBuildersCache.RemoveAnySimilar(AnyCallerThread, options))) - - member _.NotifyProjectCleaned(options: FSharpProjectOptions, userOpName) = - use _ = - Activity.start - "BackgroundCompiler.NotifyProjectCleaned" - [| - Activity.Tags.project, options.ProjectFileName - Activity.Tags.userOpName, userOpName - |] - - async { - let! ct = Async.CancellationToken - // If there was a similar entry (as there normally will have been) then re-establish an empty builder . This - // is a somewhat arbitrary choice - it will have the effect of releasing memory associated with the previous - // builder, but costs some time. - if incrementalBuildersCache.ContainsSimilarKey(AnyCallerThread, options) then - let _ = createBuilderNode (options, userOpName, ct) - () - } - - member _.BeforeBackgroundFileCheck = beforeFileChecked.Publish - - member _.FileParsed = fileParsed.Publish - - member _.FileChecked = fileChecked.Publish - - member _.ProjectChecked = projectChecked.Publish - - member _.ClearCaches() = - use _ = Activity.startNoTags "BackgroundCompiler.ClearCaches" - - lock gate (fun () -> - parseCacheLock.AcquireLock(fun ltok -> - checkFileInProjectCache.Clear(ltok) - parseFileCache.Clear(ltok)) - - incrementalBuildersCache.Clear(AnyCallerThread) - frameworkTcImportsCache.Clear() - scriptClosureCache.Clear AnyCallerThread) - - member _.DownsizeCaches() = - use _ = Activity.startNoTags "BackgroundCompiler.DownsizeCaches" - - lock gate (fun () -> - parseCacheLock.AcquireLock(fun ltok -> - checkFileInProjectCache.Resize(ltok, newKeepStrongly = 1) - parseFileCache.Resize(ltok, newKeepStrongly = 1)) - - incrementalBuildersCache.Resize(AnyCallerThread, newKeepStrongly = 1, newKeepMax = 1) - frameworkTcImportsCache.Downsize() - scriptClosureCache.Resize(AnyCallerThread, newKeepStrongly = 1, newKeepMax = 1)) - - member _.FrameworkImportsCache = frameworkTcImportsCache - - static member ActualParseFileCount = actualParseFileCount - - static member ActualCheckFileCount = actualCheckFileCount - [] // There is typically only one instance of this type in an IDE process. type FSharpChecker @@ -1283,6 +148,7 @@ type FSharpChecker useChangeNotifications, useSyntaxTreeCache ) + :> IBackgroundCompiler static let globalInstance = lazy FSharpChecker.Create() From 55f784abedb0b273eeff66e4c91f1cbb77b0c86a Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 12 Apr 2023 12:07:11 +0200 Subject: [PATCH 003/222] wip --- src/Compiler/Facilities/AsyncMemoize.fs | 1 + src/Compiler/Service/BackgroundCompiler.fs | 2 +- src/Compiler/Service/FSharpCheckerResults.fs | 129 ++++++- src/Compiler/Service/FSharpCheckerResults.fsi | 105 +++++- src/Compiler/Service/TransparentCompiler.fs | 345 +++++++++++++++--- src/Compiler/Service/service.fs | 58 ++- src/Compiler/Service/service.fsi | 4 +- .../ProjectGeneration.fs | 6 +- 8 files changed, 575 insertions(+), 75 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index d7f17f2352c..960d2d0eeda 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -110,6 +110,7 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?eventLog: Resiz | CancelRequest, Some (Completed _) -> () | JobCompleted, Some (Running (job, _)) -> + // TODO: should we re-wrap the result? cache.Set(tok, key, (Completed job)) requestCounts.Remove key |> ignore log (Finished key) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 3882e06bc8d..227a0b670c5 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -671,7 +671,7 @@ type internal BackgroundCompiler tcInfo.TcDiagnostics, options.IsIncompleteTypeCheckEnvironment, options, - builder, + Some builder, Array.ofList tcInfo.tcDependencyFiles, creationDiags, parseResults.Diagnostics, diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 7582e109849..a70274d731e 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -58,6 +58,7 @@ open FSharp.Compiler.BuildGraph open Internal.Utilities open Internal.Utilities.Collections open FSharp.Compiler.AbstractIL.ILBinaryReader +open System.Threading.Tasks type FSharpUnresolvedReferencesSet = FSharpUnresolvedReferencesSet of UnresolvedAssemblyReference list @@ -114,6 +115,130 @@ type internal DelayedILModuleReader = } | _ -> cancellable.Return(Some this.result) + + +type FSharpFileKey = string * string + +type FSharpProjectSnapshotKey = + { + ProjectFileName: string + SourceFiles: FSharpFileKey list + OtherOptions: string list + ReferencedProjects: FSharpProjectSnapshotKey list + + // Do we need these? + IsIncompleteTypeCheckEnvironment: bool + UseScriptResolutionRules: bool + } + +[] +type FSharpFileSnapshot = { + FileName: string + Version: string + GetSource: unit -> Task +} with + member this.Key = this.FileName, this.Version + override this.Equals(o) = + match o with + | :? FSharpFileSnapshot as o -> o.FileName = this.FileName && o.Version = this.Version + | _ -> false + + override this.GetHashCode() = this.Key.GetHashCode() + + + +[] +type FSharpProjectSnapshot = + { + ProjectFileName: string + ProjectId: string option + SourceFiles: FSharpFileSnapshot list + OtherOptions: string list + ReferencedProjects: FSharpReferencedProjectSnapshot list + IsIncompleteTypeCheckEnvironment: bool + UseScriptResolutionRules: bool + LoadTime: DateTime + UnresolvedReferences: FSharpUnresolvedReferencesSet option + OriginalLoadReferences: (range * string * string) list + Stamp: int64 option + } + static member UseSameProject(options1, options2) = + match options1.ProjectId, options2.ProjectId with + | Some (projectId1), Some (projectId2) when + not (String.IsNullOrWhiteSpace(projectId1)) + && not (String.IsNullOrWhiteSpace(projectId2)) + -> + projectId1 = projectId2 + | Some _, Some _ + | None, None -> options1.ProjectFileName = options2.ProjectFileName + | _ -> false + + static member AreSameForChecking(options1, options2) = + match options1.Stamp, options2.Stamp with + | Some x, Some y -> (x = y) + | _ -> + FSharpProjectSnapshot.UseSameProject(options1, options2) + && options1.SourceFiles = options2.SourceFiles + && options1.OtherOptions = options2.OtherOptions + && options1.UnresolvedReferences = options2.UnresolvedReferences + && options1.OriginalLoadReferences = options2.OriginalLoadReferences + && options1.ReferencedProjects.Length = options2.ReferencedProjects.Length + && (options1.ReferencedProjects, options2.ReferencedProjects) + ||> List.forall2 (fun r1 r2 -> + match r1, r2 with + | FSharpReferencedProjectSnapshot.FSharpReference (n1, a), FSharpReferencedProjectSnapshot.FSharpReference (n2, b) -> + n1 = n2 && FSharpProjectSnapshot.AreSameForChecking(a, b)) + + && options1.LoadTime = options2.LoadTime + + member po.ProjectDirectory = Path.GetDirectoryName(po.ProjectFileName) + + member this.Key = { + ProjectFileName = this.ProjectFileName + SourceFiles = this.SourceFiles |> List.map (fun x -> x.Key) + OtherOptions = this.OtherOptions + ReferencedProjects = this.ReferencedProjects |> List.map (function FSharpReference (_, x) -> x.Key) + IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = this.UseScriptResolutionRules + } + + override this.ToString() = + "FSharpProjectSnapshot(" + this.ProjectFileName + ")" + +and [] public FSharpReferencedProjectSnapshot = + internal + | FSharpReference of projectOutputFile: string * options: FSharpProjectSnapshot + //| PEReference of projectOutputFile: string * getStamp: (unit -> DateTime) * delayedReader: DelayedILModuleReader + //| ILModuleReference of + // projectOutputFile: string * + // getStamp: (unit -> DateTime) * + // getReader: (unit -> ILModuleReader) + + /// + /// The fully qualified path to the output of the referenced project. This should be the same value as the -r + /// reference in the project options for this referenced project. + /// + member this.OutputFile = match this with FSharpReference (projectOutputFile, _) -> projectOutputFile + + /// + /// Creates a reference for an F# project. The physical data for it is stored/cached inside of the compiler service. + /// + /// The fully qualified path to the output of the referenced project. This should be the same value as the -r reference in the project options for this referenced project. + /// The Project Options for this F# project + static member CreateFSharp(projectOutputFile, options: FSharpProjectSnapshot) = FSharpReference (projectOutputFile, options) + + override this.Equals(o) = + match o with + | :? FSharpReferencedProjectSnapshot as o -> + match this, o with + | FSharpReference (projectOutputFile1, options1), FSharpReference (projectOutputFile2, options2) -> + projectOutputFile1 = projectOutputFile2 && options1 = options2 + + | _ -> false + + override this.GetHashCode() = this.OutputFile.GetHashCode() + + [] type FSharpReferencedProject = | FSharpReference of projectOutputFile: string * options: FSharpProjectOptions @@ -2996,7 +3121,7 @@ type FSharpCheckFileResults backgroundDiagnostics: (PhasedDiagnostic * FSharpDiagnosticSeverity)[], isIncompleteTypeCheckEnvironment: bool, projectOptions: FSharpProjectOptions, - builder: IncrementalBuilder, + builder: IncrementalBuilder option, dependencyFiles: string[], creationErrors: FSharpDiagnostic[], parseErrors: FSharpDiagnostic[], @@ -3025,7 +3150,7 @@ type FSharpCheckFileResults FSharpCheckFileResults.JoinErrors(isIncompleteTypeCheckEnvironment, creationErrors, parseErrors, tcErrors) let results = - FSharpCheckFileResults(mainInputFileName, errors, Some tcFileInfo, dependencyFiles, Some builder, keepAssemblyContents) + FSharpCheckFileResults(mainInputFileName, errors, Some tcFileInfo, dependencyFiles, builder, keepAssemblyContents) return results } diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 5b14cdf9bc7..0294123474c 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -39,6 +39,108 @@ type internal DelayedILModuleReader = /// Unused in this API type public FSharpUnresolvedReferencesSet = internal FSharpUnresolvedReferencesSet of UnresolvedAssemblyReference list + +type FSharpFileKey = string * string + +type FSharpProjectSnapshotKey = + { + ProjectFileName: string + SourceFiles: FSharpFileKey list + OtherOptions: string list + ReferencedProjects: FSharpProjectSnapshotKey list + + // Do we need these? + IsIncompleteTypeCheckEnvironment: bool + UseScriptResolutionRules: bool + } + +[] +type FSharpFileSnapshot = { + FileName: string + Version: string + GetSource: unit -> System.Threading.Tasks.Task +} with member Key: FSharpFileKey + +[] +type FSharpProjectSnapshot = + { + // Note that this may not reduce to just the project directory, because there may be two projects in the same directory. + ProjectFileName: string + + /// This is the unique identifier for the project, it is case sensitive. If it's None, will key off of ProjectFileName in our caching. + ProjectId: string option + + /// The files in the project + SourceFiles: FSharpFileSnapshot list + + /// Additional command line argument options for the project. These can include additional files and references. + OtherOptions: string list + + /// The command line arguments for the other projects referenced by this project, indexed by the + /// exact text used in the "-r:" reference in FSharpProjectOptions. + ReferencedProjects: FSharpReferencedProjectSnapshot list + + /// When true, the typechecking environment is known a priori to be incomplete, for + /// example when a .fs file is opened outside of a project. In this case, the number of error + /// messages reported is reduced. + IsIncompleteTypeCheckEnvironment: bool + + /// When true, use the reference resolution rules for scripts rather than the rules for compiler. + UseScriptResolutionRules: bool + + /// Timestamp of project/script load, used to differentiate between different instances of a project load. + /// This ensures that a complete reload of the project or script type checking + /// context occurs on project or script unload/reload. + LoadTime: DateTime + + /// Unused in this API and should be 'None' when used as user-specified input + UnresolvedReferences: FSharpUnresolvedReferencesSet option + + /// Unused in this API and should be '[]' when used as user-specified input + OriginalLoadReferences: (range * string * string) list + + /// An optional stamp to uniquely identify this set of options + /// If two sets of options both have stamps, then they are considered equal + /// if and only if the stamps are equal + Stamp: int64 option + } + + /// Whether the two parse options refer to the same project. + static member internal UseSameProject: options1: FSharpProjectSnapshot * options2: FSharpProjectSnapshot -> bool + + /// Compare two options sets with respect to the parts of the options that are important to building. + static member internal AreSameForChecking: options1: FSharpProjectSnapshot * options2: FSharpProjectSnapshot -> bool + + /// Compute the project directory. + member internal ProjectDirectory: string + + member Key: FSharpProjectSnapshotKey + + +and [] public FSharpReferencedProjectSnapshot = + internal + | FSharpReference of projectOutputFile: string * options: FSharpProjectSnapshot + //| PEReference of projectOutputFile: string * version: string * delayedReader: DelayedILModuleReader + //| ILModuleReference of + // projectOutputFile: string * + // getStamp: (unit -> DateTime) * + // getReader: (unit -> ILModuleReader) + + /// + /// The fully qualified path to the output of the referenced project. This should be the same value as the -r + /// reference in the project options for this referenced project. + /// + member OutputFile: string + + /// + /// Creates a reference for an F# project. The physical data for it is stored/cached inside of the compiler service. + /// + /// The fully qualified path to the output of the referenced project. This should be the same value as the -r reference in the project options for this referenced project. + /// The Project Options for this F# project + static member CreateFSharp: projectOutputFile: string * options: FSharpProjectSnapshot -> FSharpReferencedProjectSnapshot + + + /// A set of information describing a project or script build configuration. type public FSharpProjectOptions = { @@ -137,6 +239,7 @@ and [] public FSharpReferencedProject = projectOutputFile: string * getStamp: (unit -> DateTime) * getReader: (unit -> ILModuleReader) -> FSharpReferencedProject + /// Represents the use of an F# symbol from F# source code [] type public FSharpSymbolUse = @@ -467,7 +570,7 @@ type public FSharpCheckFileResults = backgroundDiagnostics: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] * isIncompleteTypeCheckEnvironment: bool * projectOptions: FSharpProjectOptions * - builder: IncrementalBuilder * + builder: IncrementalBuilder option * dependencyFiles: string[] * creationErrors: FSharpDiagnostic[] * parseErrors: FSharpDiagnostic[] * diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 28d5996b5b0..98c1fd5b8f5 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1,54 +1,303 @@ namespace FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.Text +open FSharp.Compiler.BuildGraph +open FSharp.Compiler.Symbols +open FSharp.Compiler.CompilerConfig +open FSharp.Compiler.Diagnostics +open System +open FSharp.Compiler +open Internal.Utilities.Collections +open FSharp.Compiler.ParseAndCheckInputs + + + +type internal FSharpFile = { + Range: range + Source: FSharpFileSnapshot + IsLastCompiland: bool + IsExe: bool +} + +/// Things we need to start parsing and checking files for a given project snapshot +type BootstrapInfo = { + TcConfig: TcConfig + SourceFiles: FSharpFile list +} + + +type internal TransparentCompiler + ( + legacyReferenceResolver, + projectCacheSize, + keepAssemblyContents, + keepAllBackgroundResolutions, + tryGetMetadataSnapshot, + suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + parallelReferenceResolution, + captureIdentifiersWhenParsing, + getSource: (string -> ISourceText option) option, + useChangeNotifications, + useSyntaxTreeCache + ) = + + // Is having just one of these ok? + let lexResourceManager = Lexhelp.LexResourceManager() + + let ParseFileCache = AsyncMemoize() + let ParseAndCheckFileInProjectCache = AsyncMemoize() + + + // use this to process not-yet-implemented tasks + let backgroundCompiler = + BackgroundCompiler( + legacyReferenceResolver, + projectCacheSize, + keepAssemblyContents, + keepAllBackgroundResolutions, + tryGetMetadataSnapshot, + suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + parallelReferenceResolution, + captureIdentifiersWhenParsing, + getSource, + useChangeNotifications, + useSyntaxTreeCache + ) + :> IBackgroundCompiler + + + let ComputeParseFile (file: FSharpFile) (projectSnapshot: FSharpProjectSnapshot) userOpName _key = node { + + return () + + } + + + let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) _key = + node { + + let! bootstrapInfoOpt, creationDiags = getBootstrapInfo projectSnapshot // probably cache + + match bootstrapInfoOpt with + | None -> + let parseTree = EmptyParsedInput(fileName, (false, false)) + let parseResults = FSharpParseFileResults(creationDiags, parseTree, true, [||]) + return (parseResults, FSharpCheckFileAnswer.Aborted) + + | Some bootstrapInfo -> + + + + + // Do the parsing. + let parsingOptions = + FSharpParsingOptions.FromTcConfig( + bootstrapInfo.TcConfig, + projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName) |> Array.ofSeq, + projectSnapshot.UseScriptResolutionRules + ) + + // TODO: what is this? + // GraphNode.SetPreferredUILang tcPrior.TcConfig.preferredUiLang + + let parseDiagnostics, parseTree, anyErrors = + ParseAndCheckFile.parseFile ( + sourceText, + fileName, + parsingOptions, + userOpName, + suggestNamesForErrors, + captureIdentifiersWhenParsing + ) + + // TODO: check if we need this in parse results + let dependencyFiles = [||] + + let parseResults = + FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, dependencyFiles) + + let! checkResults = + bc.CheckOneFileImpl( + parseResults, + sourceText, + fileName, + options, + fileVersion, + builder, + tcPrior, + tcInfo, + creationDiags + ) + + return (parseResults, checkResults) + } + + member _.ParseAndCheckFileInProject + ( + fileName: string, + projectSnapshot: FSharpProjectSnapshot, + userOpName: string + ) : NodeCode = node { + ignore userOpName // TODO + let key = fileName, projectSnapshot.Key + return! ParseAndCheckFileInProjectCache.Get(key, ComputeParseAndCheckFileInProject fileName projectSnapshot) + } -type internal TransparentCompiler() = interface IBackgroundCompiler with member this.BeforeBackgroundFileCheck: IEvent = - raise (System.NotImplementedException()) - member this.CheckFileInProject(parseResults: FSharpParseFileResults, fileName: string, fileVersion: int, sourceText: FSharp.Compiler.Text.ISourceText, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = - raise (System.NotImplementedException()) - member this.CheckFileInProjectAllowingStaleCachedResults(parseResults: FSharpParseFileResults, fileName: string, fileVersion: int, sourceText: FSharp.Compiler.Text.ISourceText, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = - raise (System.NotImplementedException()) - member this.ClearCache(options: seq, userOpName: string): unit = - raise (System.NotImplementedException()) - member this.ClearCaches(): unit = - raise (System.NotImplementedException()) - member this.DownsizeCaches(): unit = - raise (System.NotImplementedException()) - member this.FileChecked: IEvent = - raise (System.NotImplementedException()) - member this.FileParsed: IEvent = - raise (System.NotImplementedException()) - member this.FindReferencesInFile(fileName: string, options: FSharpProjectOptions, symbol: FSharp.Compiler.Symbols.FSharpSymbol, canInvalidateProject: bool, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode> = - raise (System.NotImplementedException()) - member this.FrameworkImportsCache: FrameworkImportsCache = - raise (System.NotImplementedException()) - member this.GetAssemblyData(options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = - raise (System.NotImplementedException()) - member this.GetBackgroundCheckResultsForFileInProject(fileName: string, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = - raise (System.NotImplementedException()) - member this.GetBackgroundParseResultsForFileInProject(fileName: string, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = - raise (System.NotImplementedException()) - member this.GetCachedCheckFileResult(builder: IncrementalBuilder, fileName: string, sourceText: FSharp.Compiler.Text.ISourceText, options: FSharpProjectOptions): FSharp.Compiler.BuildGraph.NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> = - raise (System.NotImplementedException()) - member this.GetProjectOptionsFromScript(fileName: string, sourceText: FSharp.Compiler.Text.ISourceText, previewEnabled: bool option, loadedTimeStamp: System.DateTime option, otherFlags: string array option, useFsiAuxLib: bool option, useSdkRefs: bool option, sdkDirOverride: string option, assumeDotNetFramework: bool option, optionsStamp: int64 option, userOpName: string): Async = - raise (System.NotImplementedException()) - member this.GetSemanticClassificationForFile(fileName: string, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = - raise (System.NotImplementedException()) - member this.InvalidateConfiguration(options: FSharpProjectOptions, userOpName: string): unit = - raise (System.NotImplementedException()) - member this.NotifyFileChanged(fileName: string, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = - raise (System.NotImplementedException()) - member this.NotifyProjectCleaned(options: FSharpProjectOptions, userOpName: string): Async = - raise (System.NotImplementedException()) - member this.ParseAndCheckFileInProject(fileName: string, fileVersion: int, sourceText: FSharp.Compiler.Text.ISourceText, options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = - raise (System.NotImplementedException()) - member this.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string): FSharp.Compiler.BuildGraph.NodeCode = - raise (System.NotImplementedException()) - member this.ParseFile(fileName: string, sourceText: FSharp.Compiler.Text.ISourceText, options: FSharpParsingOptions, cache: bool, userOpName: string): Async = - raise (System.NotImplementedException()) - member this.ProjectChecked: IEvent = - raise (System.NotImplementedException()) - member this.TryGetRecentCheckResultsForFile(fileName: string, options: FSharpProjectOptions, sourceText: FSharp.Compiler.Text.ISourceText option, userOpName: string): (FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash) option = - raise (System.NotImplementedException()) \ No newline at end of file + backgroundCompiler.BeforeBackgroundFileCheck + + member _.CheckFileInProject + ( + parseResults: FSharpParseFileResults, + fileName: string, + fileVersion: int, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + backgroundCompiler.CheckFileInProject(parseResults, fileName, fileVersion, sourceText, options, userOpName) + + member _.CheckFileInProjectAllowingStaleCachedResults + ( + parseResults: FSharpParseFileResults, + fileName: string, + fileVersion: int, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + backgroundCompiler.CheckFileInProjectAllowingStaleCachedResults(parseResults, fileName, fileVersion, sourceText, options, userOpName) + + member _.ClearCache(options: seq, userOpName: string) : unit = backgroundCompiler.ClearCache(options, userOpName) + member _.ClearCaches() : unit = backgroundCompiler.ClearCaches() + member _.DownsizeCaches() : unit = backgroundCompiler.DownsizeCaches() + member _.FileChecked: IEvent = backgroundCompiler.FileChecked + member _.FileParsed: IEvent = backgroundCompiler.FileParsed + + member _.FindReferencesInFile + ( + fileName: string, + options: FSharpProjectOptions, + symbol: FSharpSymbol, + canInvalidateProject: bool, + userOpName: string + ) : NodeCode> = + backgroundCompiler.FindReferencesInFile(fileName, options, symbol, canInvalidateProject, userOpName) + + member _.FrameworkImportsCache: FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache + + member _.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = + backgroundCompiler.GetAssemblyData(options, userOpName) + + member _.GetBackgroundCheckResultsForFileInProject + ( + fileName: string, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + backgroundCompiler.GetBackgroundCheckResultsForFileInProject(fileName, options, userOpName) + + member _.GetBackgroundParseResultsForFileInProject + ( + fileName: string, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + backgroundCompiler.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName) + + member _.GetCachedCheckFileResult + ( + builder: IncrementalBuilder, + fileName: string, + sourceText: ISourceText, + options: FSharpProjectOptions + ) : NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> = + backgroundCompiler.GetCachedCheckFileResult(builder, fileName, sourceText, options) + + member _.GetProjectOptionsFromScript + ( + fileName: string, + sourceText: ISourceText, + previewEnabled: bool option, + loadedTimeStamp: DateTime option, + otherFlags: string array option, + useFsiAuxLib: bool option, + useSdkRefs: bool option, + sdkDirOverride: string option, + assumeDotNetFramework: bool option, + optionsStamp: int64 option, + userOpName: string + ) : Async = + backgroundCompiler.GetProjectOptionsFromScript( + fileName, + sourceText, + previewEnabled, + loadedTimeStamp, + otherFlags, + useFsiAuxLib, + useSdkRefs, + sdkDirOverride, + assumeDotNetFramework, + optionsStamp, + userOpName + ) + + member _.GetSemanticClassificationForFile + ( + fileName: string, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + backgroundCompiler.GetSemanticClassificationForFile(fileName, options, userOpName) + + member _.InvalidateConfiguration(options: FSharpProjectOptions, userOpName: string) : unit = + backgroundCompiler.InvalidateConfiguration(options, userOpName) + + member _.NotifyFileChanged(fileName: string, options: FSharpProjectOptions, userOpName: string) : NodeCode = + backgroundCompiler.NotifyFileChanged(fileName, options, userOpName) + + member _.NotifyProjectCleaned(options: FSharpProjectOptions, userOpName: string) : Async = + backgroundCompiler.NotifyProjectCleaned(options, userOpName) + + member _.ParseAndCheckFileInProject + ( + fileName: string, + fileVersion: int, + sourceText: ISourceText, + options: FSharpProjectOptions, + userOpName: string + ) : NodeCode = + + backgroundCompiler.ParseAndCheckFileInProject(fileName, fileVersion, sourceText, options, userOpName) + + member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = + backgroundCompiler.ParseAndCheckProject(options, userOpName) + + member _.ParseFile + ( + fileName: string, + sourceText: ISourceText, + options: FSharpParsingOptions, + cache: bool, + userOpName: string + ) : Async = + backgroundCompiler.ParseFile(fileName, sourceText, options, cache, userOpName) + + member _.ProjectChecked: IEvent = backgroundCompiler.ProjectChecked + + member _.TryGetRecentCheckResultsForFile + ( + fileName: string, + options: FSharpProjectOptions, + sourceText: ISourceText option, + userOpName: string + ) : (FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash) option = + backgroundCompiler.TryGetRecentCheckResultsForFile(fileName, options, sourceText, userOpName) diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 8b6a9c1766a..bd7501463d1 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -128,27 +128,43 @@ type FSharpChecker captureIdentifiersWhenParsing, getSource, useChangeNotifications, - useSyntaxTreeCache + useSyntaxTreeCache, + useTransparentCompiler ) = let backgroundCompiler = - BackgroundCompiler( - legacyReferenceResolver, - projectCacheSize, - keepAssemblyContents, - keepAllBackgroundResolutions, - tryGetMetadataSnapshot, - suggestNamesForErrors, - keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification, - enablePartialTypeChecking, - parallelReferenceResolution, - captureIdentifiersWhenParsing, - getSource, - useChangeNotifications, - useSyntaxTreeCache - ) - :> IBackgroundCompiler + if useTransparentCompiler = Some true then + TransparentCompiler( + legacyReferenceResolver, + projectCacheSize, + keepAssemblyContents, + keepAllBackgroundResolutions, + tryGetMetadataSnapshot, + suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + parallelReferenceResolution, + captureIdentifiersWhenParsing, + getSource, + useChangeNotifications, + useSyntaxTreeCache) :> IBackgroundCompiler + else + BackgroundCompiler( + legacyReferenceResolver, + projectCacheSize, + keepAssemblyContents, + keepAllBackgroundResolutions, + tryGetMetadataSnapshot, + suggestNamesForErrors, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + parallelReferenceResolution, + captureIdentifiersWhenParsing, + getSource, + useChangeNotifications, + useSyntaxTreeCache) :> IBackgroundCompiler static let globalInstance = lazy FSharpChecker.Create() @@ -192,7 +208,8 @@ type FSharpChecker ?parallelReferenceResolution: bool, ?captureIdentifiersWhenParsing: bool, ?documentSource: DocumentSource, - ?useSyntaxTreeCache: bool + ?useSyntaxTreeCache: bool, + ?useTransparentCompiler: bool ) = use _ = Activity.startNoTags "FSharpChecker.Create" @@ -243,7 +260,8 @@ type FSharpChecker | Some (DocumentSource.Custom f) -> Some f | _ -> None), useChangeNotifications, - useSyntaxTreeCache + useSyntaxTreeCache, + useTransparentCompiler ) member _.ReferenceResolver = legacyReferenceResolver diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index 5d482b3cbed..497baf4d347 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -42,6 +42,7 @@ type public FSharpChecker = /// When set to true we create a set of all identifiers for each parsed file which can be used to speed up finding references. /// Default: FileSystem. You can use Custom source to provide a function that will return the source for a given file path instead of reading it from the file system. Note that with this option the FSharpChecker will also not monitor the file system for file changes. It will expect to be notified of changes via the NotifyFileChanged method. /// Default: true. Indicates whether to keep parsing results in a cache. + /// Default: false. Indicates whether we use a new experimental background compiler. static member Create: ?projectCacheSize: int * ?keepAssemblyContents: bool * @@ -55,7 +56,8 @@ type public FSharpChecker = ?parallelReferenceResolution: bool * ?captureIdentifiersWhenParsing: bool * [] ?documentSource: DocumentSource * - [] ?useSyntaxTreeCache: bool -> + [] ?useSyntaxTreeCache: bool * + [] ?useTransparentCompiler: bool -> FSharpChecker /// diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 578a39aa17d..3f14fa601a1 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -468,7 +468,8 @@ module Helpers = enableBackgroundItemKeyStoreAndSemanticClassification = true, enablePartialTypeChecking = true, captureIdentifiersWhenParsing = true, - documentSource = DocumentSource.Custom getSource) + documentSource = DocumentSource.Custom getSource, + useTransparentCompiler = true) let options = let baseOptions, _ = @@ -564,7 +565,8 @@ type ProjectWorkflowBuilder enablePartialTypeChecking = true, captureIdentifiersWhenParsing = true, documentSource = (if useGetSource then DocumentSource.Custom getSource else DocumentSource.FileSystem), - useSyntaxTreeCache = defaultArg useSyntaxTreeCache false + useSyntaxTreeCache = defaultArg useSyntaxTreeCache false, + useTransparentCompiler = true )) let mapProjectAsync f workflow = From 9fe9e221f7a860e028ed57fa5dc43ae1f2887dbd Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Fri, 14 Apr 2023 12:30:38 +0200 Subject: [PATCH 004/222] wip --- src/Compiler/Service/FSharpCheckerResults.fs | 17 + src/Compiler/Service/FSharpCheckerResults.fsi | 5 + src/Compiler/Service/TransparentCompiler.fs | 325 ++++++++++++++++-- 3 files changed, 318 insertions(+), 29 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index a70274d731e..63b9e1893d0 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -202,6 +202,8 @@ type FSharpProjectSnapshot = UseScriptResolutionRules = this.UseScriptResolutionRules } + member this.SourceFileNames = this.SourceFiles |> List.map (fun x -> x.FileName) + override this.ToString() = "FSharpProjectSnapshot(" + this.ProjectFileName + ")" @@ -327,6 +329,21 @@ and FSharpProjectOptions = override this.ToString() = "FSharpProjectOptions(" + this.ProjectFileName + ")" +type FSharpProjectSnapshot with + member this.ToOptions (): FSharpProjectOptions = { + ProjectFileName = this.ProjectFileName + ProjectId = this.ProjectId + SourceFiles = this.SourceFiles |> Seq.map (fun x -> x.FileName) |> Seq.toArray + OtherOptions = this.OtherOptions |> List.toArray + ReferencedProjects = this.ReferencedProjects |> Seq.map (function FSharpReference (name, opts) -> FSharpReferencedProject.FSharpReference (name, opts.ToOptions())) |> Seq.toArray + IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = this.UseScriptResolutionRules + LoadTime = this.LoadTime + UnresolvedReferences = this.UnresolvedReferences + OriginalLoadReferences = this.OriginalLoadReferences + Stamp = this.Stamp + } + [] module internal FSharpCheckerResultsSettings = diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 0294123474c..6dfe65ca2b5 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -114,6 +114,8 @@ type FSharpProjectSnapshot = /// Compute the project directory. member internal ProjectDirectory: string + member SourceFileNames: string list + member Key: FSharpProjectSnapshotKey @@ -239,6 +241,9 @@ and [] public FSharpReferencedProject = projectOutputFile: string * getStamp: (unit -> DateTime) * getReader: (unit -> ILModuleReader) -> FSharpReferencedProject +type FSharpProjectSnapshot with + member ToOptions: unit -> FSharpProjectOptions + /// Represents the use of an F# symbol from F# source code [] diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 98c1fd5b8f5..01cc8bfaccb 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -9,6 +9,16 @@ open System open FSharp.Compiler open Internal.Utilities.Collections open FSharp.Compiler.ParseAndCheckInputs +open FSharp.Compiler.ScriptClosure +open FSharp.Compiler.AbstractIL.ILBinaryReader +open FSharp.Compiler.Text.Range +open FSharp.Compiler.AbstractIL.IL +open FSharp.Compiler.ConstraintSolver +open System.Diagnostics +open System.IO +open FSharp.Compiler.CompilerOptions +open FSharp.Compiler.Xml +open FSharp.Compiler.CompilerImports @@ -49,7 +59,7 @@ type internal TransparentCompiler let ParseFileCache = AsyncMemoize() let ParseAndCheckFileInProjectCache = AsyncMemoize() - + let FrameworkImportsCache = AsyncMemoize() // use this to process not-yet-implemented tasks let backgroundCompiler = @@ -71,18 +81,296 @@ type internal TransparentCompiler ) :> IBackgroundCompiler + let getProjectReferences (project: FSharpProjectSnapshot) userOpName = + [ + for r in project.ReferencedProjects do + + match r with + | FSharpReferencedProjectSnapshot.FSharpReference (nm, opts) -> + // Don't use cross-project references for FSharp.Core, since various bits of code + // require a concrete FSharp.Core to exist on-disk. The only solutions that have + // these cross-project references to FSharp.Core are VisualFSharp.sln and FSharp.sln. The ramification + // of this is that you need to build FSharp.Core to get intellisense in those projects. + + if + (try + Path.GetFileNameWithoutExtension(nm) + with _ -> + "") + <> GetFSharpCoreLibraryName() + then + { new IProjectReference with + member x.EvaluateRawContents() = + node { + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) + return! backgroundCompiler.GetAssemblyData(opts.ToOptions(), userOpName + ".CheckReferencedProject(" + nm + ")") + } + + member x.TryGetLogicalTimeStamp(cache) = + // TODO: + None + + member x.FileName = nm + } + ] + + + let ComputeFrameworkImports (tcConfig: TcConfig) _key = node { + let tcConfigP = TcConfigProvider.Constant tcConfig + return! TcImports.BuildFrameworkTcImports (tcConfigP, frameworkDLLs, nonFrameworkResolutions) + } + + + let ComputeBootstapInfo (projectSnapshot: FSharpProjectSnapshot) = + node { + + let useSimpleResolutionSwitch = "--simpleresolution" + let commandLineArgs = projectSnapshot.OtherOptions + let defaultFSharpBinariesDir = FSharpCheckerResultsSettings.defaultFSharpBinariesDir + let useScriptResolutionRules = projectSnapshot.UseScriptResolutionRules + + let projectReferences = getProjectReferences projectSnapshot "ComputeBootstapInfo" + let sourceFiles = projectSnapshot.SourceFileNames + + // TODO: script support + let loadClosureOpt: LoadClosure option = None + + let tcConfigB, sourceFiles = + + let getSwitchValue switchString = + match commandLineArgs |> List.tryFindIndex(fun s -> s.StartsWithOrdinal switchString) with + | Some idx -> Some(commandLineArgs[idx].Substring(switchString.Length)) + | _ -> None + + let sdkDirOverride = + match loadClosureOpt with + | None -> None + | Some loadClosure -> loadClosure.SdkDirOverride + + // see also fsc.fs: runFromCommandLineToImportingAssemblies(), as there are many similarities to where the PS creates a tcConfigB + let tcConfigB = + TcConfigBuilder.CreateNew(legacyReferenceResolver, + defaultFSharpBinariesDir, + implicitIncludeDir=projectSnapshot.ProjectDirectory, + reduceMemoryUsage=ReduceMemoryFlag.Yes, + isInteractive=useScriptResolutionRules, + isInvalidationSupported=true, + defaultCopyFSharpCore=CopyFSharpCoreFlag.No, + tryGetMetadataSnapshot=tryGetMetadataSnapshot, + sdkDirOverride=sdkDirOverride, + rangeForErrors=range0) + + tcConfigB.primaryAssembly <- + match loadClosureOpt with + | None -> PrimaryAssembly.Mscorlib + | Some loadClosure -> + if loadClosure.UseDesktopFramework then + PrimaryAssembly.Mscorlib + else + PrimaryAssembly.System_Runtime + + tcConfigB.resolutionEnvironment <- (LegacyResolutionEnvironment.EditingOrCompilation true) + + tcConfigB.conditionalDefines <- + let define = if useScriptResolutionRules then "INTERACTIVE" else "COMPILED" + define :: tcConfigB.conditionalDefines + + tcConfigB.projectReferences <- projectReferences + + tcConfigB.useSimpleResolution <- (getSwitchValue useSimpleResolutionSwitch) |> Option.isSome + + // Apply command-line arguments and collect more source files if they are in the arguments + let sourceFilesNew = ApplyCommandLineArgs(tcConfigB, sourceFiles, commandLineArgs) + + // Never open PDB files for the language service, even if --standalone is specified + tcConfigB.openDebugInformationForLaterStaticLinking <- false + + tcConfigB.xmlDocInfoLoader <- + { new IXmlDocumentationInfoLoader with + /// Try to load xml documentation associated with an assembly by the same file path with the extension ".xml". + member _.TryLoad(assemblyFileName) = + let xmlFileName = Path.ChangeExtension(assemblyFileName, ".xml") + + // REVIEW: File IO - Will eventually need to change this to use a file system interface of some sort. + XmlDocumentationInfo.TryCreateFromFile(xmlFileName) + } + |> Some + + tcConfigB.parallelReferenceResolution <- parallelReferenceResolution + tcConfigB.captureIdentifiersWhenParsing <- captureIdentifiersWhenParsing + + tcConfigB, sourceFilesNew + + // If this is a builder for a script, re-apply the settings inferred from the + // script and its load closure to the configuration. + // + // NOTE: it would probably be cleaner and more accurate to re-run the load closure at this point. + let setupConfigFromLoadClosure () = + match loadClosureOpt with + | Some loadClosure -> + let dllReferences = + [for reference in tcConfigB.referencedDLLs do + // If there's (one or more) resolutions of closure references then yield them all + match loadClosure.References |> List.tryFind (fun (resolved, _)->resolved=reference.Text) with + | Some (resolved, closureReferences) -> + for closureReference in closureReferences do + yield AssemblyReference(closureReference.originalReference.Range, resolved, None) + | None -> yield reference] + tcConfigB.referencedDLLs <- [] + tcConfigB.primaryAssembly <- (if loadClosure.UseDesktopFramework then PrimaryAssembly.Mscorlib else PrimaryAssembly.System_Runtime) + // Add one by one to remove duplicates + dllReferences |> List.iter (fun dllReference -> + tcConfigB.AddReferencedAssemblyByPath(dllReference.Range, dllReference.Text)) + tcConfigB.knownUnresolvedReferences <- loadClosure.UnresolvedReferences + | None -> () + + setupConfigFromLoadClosure() + + let tcConfig = TcConfig.Create(tcConfigB, validate=true) + let outfile, _, assemblyName = tcConfigB.DecideNames sourceFiles + + // Resolve assemblies and create the framework TcImports. This is done when constructing the + // builder itself, rather than as an incremental task. This caches a level of "system" references. No type providers are + // included in these references. + let! tcGlobals, frameworkTcImports, nonFrameworkResolutions, unresolvedReferences = frameworkTcImportsCache.Get(tcConfig) + + // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. + // This is ok because not much can actually go wrong here. + let diagnosticsLogger = CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) + + // Get the names and time stamps of all the non-framework referenced assemblies, which will act + // as inputs to one of the nodes in the build. + // + // This operation is done when constructing the builder itself, rather than as an incremental task. + let nonFrameworkAssemblyInputs = + // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. + // This is ok because not much can actually go wrong here. + let diagnosticsLogger = CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) + // Return the disposable object that cleans up + use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) + + [ for r in nonFrameworkResolutions do + let fileName = r.resolvedPath + yield (Choice1Of2 fileName, (fun (cache: TimeStampCache) -> cache.GetFileTimeStamp fileName)) + + for pr in projectReferences do + yield Choice2Of2 pr, (fun (cache: TimeStampCache) -> cache.GetProjectReferenceTimeStamp pr) ] + + // Start importing + + let tcConfigP = TcConfigProvider.Constant tcConfig + let beforeFileChecked = Event() + let fileChecked = Event() + +#if !NO_TYPEPROVIDERS + let importsInvalidatedByTypeProvider = Event() +#endif + + // Check for the existence of loaded sources and prepend them to the sources list if present. + let sourceFiles = tcConfig.GetAvailableLoadedSources() @ (sourceFiles |>List.map (fun s -> rangeStartup, s)) + + // Mark up the source files with an indicator flag indicating if they are the last source file in the project + let sourceFiles = + let flags, isExe = tcConfig.ComputeCanContainEntryPoint(sourceFiles |> List.map snd) + ((sourceFiles, flags) ||> List.map2 (fun (m, nm) flag -> (m, nm, (flag, isExe)))) + + let basicDependencies = + [ for UnresolvedAssemblyReference(referenceText, _) in unresolvedReferences do + // Exclude things that are definitely not a file name + if not(FileSystem.IsInvalidPathShim referenceText) then + let file = if FileSystem.IsPathRootedShim referenceText then referenceText else Path.Combine(projectDirectory, referenceText) + yield file + + for r in nonFrameworkResolutions do + yield r.resolvedPath ] + + let allDependencies = + [| yield! basicDependencies + for _, f, _ in sourceFiles do + yield f |] + + // For scripts, the dependency provider is already available. + // For projects create a fresh one for the project. + let dependencyProvider = + match dependencyProvider with + | None -> new DependencyProvider() + | Some dependencyProvider -> dependencyProvider + + let defaultTimeStamp = DateTime.UtcNow + + let! initialBoundModel = + CombineImportedAssembliesTask( + assemblyName, + tcConfig, + tcConfigP, + tcGlobals, + frameworkTcImports, + nonFrameworkResolutions, + unresolvedReferences, + dependencyProvider, + loadClosureOpt, + basicDependencies, + keepAssemblyContents, + keepAllBackgroundResolutions, + keepAllBackgroundSymbolUses, + enableBackgroundItemKeyStoreAndSemanticClassification, + enablePartialTypeChecking, + beforeFileChecked, + fileChecked +#if !NO_TYPEPROVIDERS + ,importsInvalidatedByTypeProvider +#endif + ) + + let getFSharpSource fileName = + getSource + |> Option.map(fun getSource -> + let timeStamp = DateTime.UtcNow + let getTimeStamp = fun () -> timeStamp + let getSourceText() = getSource fileName + FSharpSource.Create(fileName, getTimeStamp, getSourceText)) + |> Option.defaultWith(fun () -> FSharpSource.CreateFromFile(fileName)) + + let sourceFiles = + sourceFiles + |> List.map (fun (m, fileName, isLastCompiland) -> + { Range = m; Source = getFSharpSource fileName; Flags = isLastCompiland } ) + + return (), () + } + + + let ComputeParseFile (file: FSharpFile) (projectSnapshot: FSharpProjectSnapshot) bootstrapInfo userOpName _key = node { + + let parsingOptions = + FSharpParsingOptions.FromTcConfig( + bootstrapInfo.TcConfig, + projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName) |> Array.ofSeq, + projectSnapshot.UseScriptResolutionRules + ) + + // TODO: what is this? + // GraphNode.SetPreferredUILang tcPrior.TcConfig.preferredUiLang - let ComputeParseFile (file: FSharpFile) (projectSnapshot: FSharpProjectSnapshot) userOpName _key = node { + let! sourceText = file.Source.GetSource() |> NodeCode.AwaitTask - return () + return ParseAndCheckFile.parseFile ( + sourceText, + file.Source.FileName, + parsingOptions, + userOpName, + suggestNamesForErrors, + captureIdentifiersWhenParsing + ) } - let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) _key = + let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) userOpName _key = node { - let! bootstrapInfoOpt, creationDiags = getBootstrapInfo projectSnapshot // probably cache + let! bootstrapInfoOpt, creationDiags = ComputeBootstapInfo projectSnapshot // probably cache match bootstrapInfoOpt with | None -> @@ -92,31 +380,10 @@ type internal TransparentCompiler | Some bootstrapInfo -> + let file = bootstrapInfo.SourceFiles |> List.find (fun f -> f.Source.FileName = fileName) + let! parseDiagnostics, parseTree, anyErrors = ParseFileCache.Get(file.Source.Key, ComputeParseFile file projectSnapshot bootstrapInfo userOpName) - - - // Do the parsing. - let parsingOptions = - FSharpParsingOptions.FromTcConfig( - bootstrapInfo.TcConfig, - projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName) |> Array.ofSeq, - projectSnapshot.UseScriptResolutionRules - ) - - // TODO: what is this? - // GraphNode.SetPreferredUILang tcPrior.TcConfig.preferredUiLang - - let parseDiagnostics, parseTree, anyErrors = - ParseAndCheckFile.parseFile ( - sourceText, - fileName, - parsingOptions, - userOpName, - suggestNamesForErrors, - captureIdentifiersWhenParsing - ) - - // TODO: check if we need this in parse results + // TODO: check if we really need this in parse results let dependencyFiles = [||] let parseResults = From beae07e39fdce0f581f057ab72e68133316587d9 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 18 Apr 2023 11:54:54 +0200 Subject: [PATCH 005/222] wip --- src/Compiler/Service/IncrementalBuild.fsi | 3 + src/Compiler/Service/TransparentCompiler.fs | 85 ++++++++++++++------- 2 files changed, 60 insertions(+), 28 deletions(-) diff --git a/src/Compiler/Service/IncrementalBuild.fsi b/src/Compiler/Service/IncrementalBuild.fsi index 9ffc66a2fdb..fc095cda82a 100644 --- a/src/Compiler/Service/IncrementalBuild.fsi +++ b/src/Compiler/Service/IncrementalBuild.fsi @@ -23,6 +23,9 @@ open FSharp.Compiler.Text open FSharp.Compiler.TypedTree open FSharp.Compiler.BuildGraph + +type FrameworkImportsCacheKey = FrameworkImportsCacheKey of resolvedpath: string list * assemblyName: string * targetFrameworkDirectories: string list * fsharpBinaries: string * langVersion: decimal + /// Lookup the global static cache for building the FrameworkTcImports type internal FrameworkImportsCache = new: size: int -> FrameworkImportsCache diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 01cc8bfaccb..d82b2b79acb 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1,25 +1,29 @@ namespace FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.Text +open System +open System.Diagnostics +open System.IO + +open Internal.Utilities.Collections +open Internal.Utilities.Library + +open FSharp.Compiler +open FSharp.Compiler.AbstractIL.IL +open FSharp.Compiler.AbstractIL.ILBinaryReader open FSharp.Compiler.BuildGraph -open FSharp.Compiler.Symbols +open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.CompilerConfig +open FSharp.Compiler.CompilerImports +open FSharp.Compiler.CompilerOptions +open FSharp.Compiler.DependencyManager open FSharp.Compiler.Diagnostics -open System -open FSharp.Compiler -open Internal.Utilities.Collections -open FSharp.Compiler.ParseAndCheckInputs +open FSharp.Compiler.DiagnosticsLogger +open FSharp.Compiler.IO open FSharp.Compiler.ScriptClosure -open FSharp.Compiler.AbstractIL.ILBinaryReader +open FSharp.Compiler.Symbols +open FSharp.Compiler.Text open FSharp.Compiler.Text.Range -open FSharp.Compiler.AbstractIL.IL -open FSharp.Compiler.ConstraintSolver -open System.Diagnostics -open System.IO -open FSharp.Compiler.CompilerOptions open FSharp.Compiler.Xml -open FSharp.Compiler.CompilerImports - type internal FSharpFile = { @@ -61,6 +65,14 @@ type internal TransparentCompiler let ParseAndCheckFileInProjectCache = AsyncMemoize() let FrameworkImportsCache = AsyncMemoize() + // We currently share one global dependency provider for all scripts for the FSharpChecker. + // For projects, one is used per project. + // + // Sharing one for all scripts is necessary for good performance from GetProjectOptionsFromScript, + // which requires a dependency provider to process through the project options prior to working out + // if the cached incremental builder can be used for the project. + let dependencyProviderForScripts = new DependencyProvider() + // use this to process not-yet-implemented tasks let backgroundCompiler = BackgroundCompiler( @@ -115,13 +127,13 @@ type internal TransparentCompiler ] - let ComputeFrameworkImports (tcConfig: TcConfig) _key = node { + let ComputeFrameworkImports (tcConfig: TcConfig) frameworkDLLs nonFrameworkResolutions _key = node { let tcConfigP = TcConfigProvider.Constant tcConfig return! TcImports.BuildFrameworkTcImports (tcConfigP, frameworkDLLs, nonFrameworkResolutions) } - let ComputeBootstapInfo (projectSnapshot: FSharpProjectSnapshot) = + let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) = node { let useSimpleResolutionSwitch = "--simpleresolution" @@ -129,7 +141,7 @@ type internal TransparentCompiler let defaultFSharpBinariesDir = FSharpCheckerResultsSettings.defaultFSharpBinariesDir let useScriptResolutionRules = projectSnapshot.UseScriptResolutionRules - let projectReferences = getProjectReferences projectSnapshot "ComputeBootstapInfo" + let projectReferences = getProjectReferences projectSnapshot "ComputeBootstrapInfo" let sourceFiles = projectSnapshot.SourceFileNames // TODO: script support @@ -137,7 +149,7 @@ type internal TransparentCompiler let tcConfigB, sourceFiles = - let getSwitchValue switchString = + let getSwitchValue (switchString: string) = match commandLineArgs |> List.tryFindIndex(fun s -> s.StartsWithOrdinal switchString) with | Some idx -> Some(commandLineArgs[idx].Substring(switchString.Length)) | _ -> None @@ -229,10 +241,29 @@ type internal TransparentCompiler let tcConfig = TcConfig.Create(tcConfigB, validate=true) let outfile, _, assemblyName = tcConfigB.DecideNames sourceFiles - // Resolve assemblies and create the framework TcImports. This is done when constructing the - // builder itself, rather than as an incremental task. This caches a level of "system" references. No type providers are + // Resolve assemblies and create the framework TcImports. This caches a level of "system" references. No type providers are // included in these references. - let! tcGlobals, frameworkTcImports, nonFrameworkResolutions, unresolvedReferences = frameworkTcImportsCache.Get(tcConfig) + + let frameworkDLLs, nonFrameworkResolutions, unresolvedReferences = TcAssemblyResolutions.SplitNonFoundationalResolutions(tcConfig) + + let frameworkDLLsKey = + frameworkDLLs + |> List.map (fun ar->ar.resolvedPath) // The cache key. Just the minimal data. + |> List.sort // Sort to promote cache hits. + + // Prepare the frameworkTcImportsCache + // + // The data elements in this key are very important. There should be nothing else in the TcConfig that logically affects + // the import of a set of framework DLLs into F# CCUs. That is, the F# CCUs that result from a set of DLLs (including + // FSharp.Core.dll and mscorlib.dll) must be logically invariant of all the other compiler configuration parameters. + let key = + FrameworkImportsCacheKey(frameworkDLLsKey, + tcConfig.primaryAssembly.Name, + tcConfig.GetTargetFrameworkDirectories(), + tcConfig.fsharpBinariesDir, + tcConfig.langVersion.SpecifiedVersion) + + let! tcGlobals, frameworkTcImports = FrameworkImportsCache.Get(key, ComputeFrameworkImports tcConfig frameworkDLLs nonFrameworkResolutions) // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. // This is ok because not much can actually go wrong here. @@ -279,7 +310,7 @@ type internal TransparentCompiler [ for UnresolvedAssemblyReference(referenceText, _) in unresolvedReferences do // Exclude things that are definitely not a file name if not(FileSystem.IsInvalidPathShim referenceText) then - let file = if FileSystem.IsPathRootedShim referenceText then referenceText else Path.Combine(projectDirectory, referenceText) + let file = if FileSystem.IsPathRootedShim referenceText then referenceText else Path.Combine(projectSnapshot.ProjectDirectory, referenceText) yield file for r in nonFrameworkResolutions do @@ -293,11 +324,9 @@ type internal TransparentCompiler // For scripts, the dependency provider is already available. // For projects create a fresh one for the project. let dependencyProvider = - match dependencyProvider with - | None -> new DependencyProvider() - | Some dependencyProvider -> dependencyProvider - - let defaultTimeStamp = DateTime.UtcNow + if projectSnapshot.UseScriptResolutionRules + then dependencyProviderForScripts + else new DependencyProvider() let! initialBoundModel = CombineImportedAssembliesTask( @@ -370,7 +399,7 @@ type internal TransparentCompiler let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) userOpName _key = node { - let! bootstrapInfoOpt, creationDiags = ComputeBootstapInfo projectSnapshot // probably cache + let! bootstrapInfoOpt, creationDiags = ComputeBootstrapInfo projectSnapshot // probably cache match bootstrapInfoOpt with | None -> From 0af5d0ef775ac74740d1389327df8b754e2df3fc Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Fri, 21 Apr 2023 12:32:42 +0200 Subject: [PATCH 006/222] wip --- src/Compiler/Driver/GraphChecking/Graph.fs | 46 ++- src/Compiler/Driver/GraphChecking/Graph.fsi | 2 + src/Compiler/Service/FSharpCheckerResults.fs | 5 +- src/Compiler/Service/FSharpCheckerResults.fsi | 3 + src/Compiler/Service/TransparentCompiler.fs | 328 +++++++++++++----- src/Compiler/Service/service.fs | 1 + .../FSharp.Compiler.ComponentTests.fsproj | 1 + .../TypeChecks/Graph/GraphOperations.fs | 30 ++ 8 files changed, 324 insertions(+), 92 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs diff --git a/src/Compiler/Driver/GraphChecking/Graph.fs b/src/Compiler/Driver/GraphChecking/Graph.fs index 0e776181a63..4a2cbb29661 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fs +++ b/src/Compiler/Driver/GraphChecking/Graph.fs @@ -27,26 +27,36 @@ module internal Graph = |> Array.map (fun (KeyValue (k, v)) -> k, v) |> readOnlyDict - let transitive<'Node when 'Node: equality> (graph: Graph<'Node>) : Graph<'Node> = - /// Find transitive dependencies of a single node. - let transitiveDeps (node: 'Node) = - let visited = HashSet<'Node>() + /// Find transitive dependencies of a single node. + let transitiveDeps (node: 'Node) (graph: Graph<'Node>) = + let visited = HashSet<'Node>() - let rec dfs (node: 'Node) = - graph[node] - // Add direct dependencies. - // Use HashSet.Add return value semantics to filter out those that were added previously. - |> Array.filter visited.Add - |> Array.iter dfs + let rec dfs (node: 'Node) = + graph[node] + // Add direct dependencies. + // Use HashSet.Add return value semantics to filter out those that were added previously. + |> Array.filter visited.Add + |> Array.iter dfs - dfs node - visited |> Seq.toArray + dfs node + visited |> Seq.toArray + let transitive<'Node when 'Node: equality> (graph: Graph<'Node>) : Graph<'Node> = graph.Keys |> Seq.toArray - |> Array.Parallel.map (fun node -> node, transitiveDeps node) + |> Array.Parallel.map (fun node -> node, graph |> transitiveDeps node) |> readOnlyDict + /// Get subgraph of the given graph that contains only nodes that are reachable from the given node. + let subGraphFor node graph = + let allDeps = graph |> transitiveDeps node + let relevant n = n = node || allDeps |> Array.contains n + graph + |> Seq.choose (fun (KeyValue (src, deps)) -> + if relevant src then Some (src, deps |> Array.filter relevant) else None) + |> make + + /// Create a reverse of the graph let reverse (originalGraph: Graph<'Node>) : Graph<'Node> = originalGraph @@ -59,6 +69,16 @@ module internal Graph = |> readOnlyDict |> addIfMissing originalGraph.Keys + let cutLeaves (graph: Graph<'Node>) = + let notLeaves = set [ for (KeyValue (node, deps)) in graph do if deps.Length > 0 then node ] + let leaves = + set [ for (KeyValue (node, deps)) in graph do + if deps.Length = 0 then node + yield! deps |> Array.filter (notLeaves.Contains >> not) ] + leaves, seq { for (KeyValue (node, deps)) in graph do + if deps.Length > 0 then + node, deps |> Array.filter (leaves.Contains >> not) } |> make + let printCustom (graph: Graph<'Node>) (nodePrinter: 'Node -> string) : unit = printfn "Graph:" let join (xs: string[]) = System.String.Join(", ", xs) diff --git a/src/Compiler/Driver/GraphChecking/Graph.fsi b/src/Compiler/Driver/GraphChecking/Graph.fsi index 95542470d8a..c6afc3ee768 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fsi +++ b/src/Compiler/Driver/GraphChecking/Graph.fsi @@ -13,6 +13,8 @@ module internal Graph = /// Create a transitive closure of the graph in O(n^2) time (but parallelize it). /// The resulting graph contains edge A -> C iff the input graph contains a (directed) non-zero length path from A to C. val transitive<'Node when 'Node: equality> : graph: Graph<'Node> -> Graph<'Node> + /// Get a sub-graph of the graph containing only the nodes reachable from the given node. + val subGraphFor: node: 'Node -> graph: Graph<'Node> -> Graph<'Node> when 'Node: equality /// Create a reverse of the graph. val reverse<'Node when 'Node: equality> : originalGraph: Graph<'Node> -> Graph<'Node> /// Print the contents of the graph to the standard output. diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index ae8778b9135..4a07c23c66e 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -146,7 +146,6 @@ type FSharpFileSnapshot = { override this.GetHashCode() = this.Key.GetHashCode() - [] type FSharpProjectSnapshot = { @@ -193,6 +192,10 @@ type FSharpProjectSnapshot = member po.ProjectDirectory = Path.GetDirectoryName(po.ProjectFileName) + member this.UpTo fileName = + let fileIndex = this.SourceFiles |> List.findIndex (fun x -> x.FileName = fileName) + { this with SourceFiles = this.SourceFiles[..fileIndex] } + member this.Key = { ProjectFileName = this.ProjectFileName SourceFiles = this.SourceFiles |> List.map (fun x -> x.Key) diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 361665a68da..3fe91074124 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -116,6 +116,9 @@ type FSharpProjectSnapshot = member SourceFileNames: string list + /// A snapshot of the same project but only up to the given file (including). + member UpTo: fileName: string -> FSharpProjectSnapshot + member Key: FSharpProjectSnapshotKey diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index d82b2b79acb..9ee1aa8b5d6 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1,4 +1,4 @@ -namespace FSharp.Compiler.CodeAnalysis +namespace FSharp.Compiler.CodeAnalysis.TransparentCompiler open System open System.Diagnostics @@ -21,9 +21,14 @@ open FSharp.Compiler.DiagnosticsLogger open FSharp.Compiler.IO open FSharp.Compiler.ScriptClosure open FSharp.Compiler.Symbols +open FSharp.Compiler.TcGlobals open FSharp.Compiler.Text open FSharp.Compiler.Text.Range open FSharp.Compiler.Xml +open System.Threading.Tasks +open FSharp.Compiler.ParseAndCheckInputs +open FSharp.Compiler.GraphChecking +open FSharp.Compiler.Syntax type internal FSharpFile = { @@ -34,12 +39,15 @@ type internal FSharpFile = { } /// Things we need to start parsing and checking files for a given project snapshot -type BootstrapInfo = { +type internal BootstrapInfo = { TcConfig: TcConfig + TcImports: TcImports + TcGlobals: TcGlobals + InitialTcInfo: TcInfo SourceFiles: FSharpFile list + LoadClosure: LoadClosure option } - type internal TransparentCompiler ( legacyReferenceResolver, @@ -59,11 +67,14 @@ type internal TransparentCompiler ) = // Is having just one of these ok? - let lexResourceManager = Lexhelp.LexResourceManager() + let _lexResourceManager = Lexhelp.LexResourceManager() let ParseFileCache = AsyncMemoize() let ParseAndCheckFileInProjectCache = AsyncMemoize() let FrameworkImportsCache = AsyncMemoize() + let BootstrapInfoCache = AsyncMemoize() + let TcPriorCache = AsyncMemoize() + let DependencyGraphForLastFileCache = AsyncMemoize() // We currently share one global dependency provider for all scripts for the FSharpChecker. // For projects, one is used per project. @@ -132,17 +143,92 @@ type internal TransparentCompiler return! TcImports.BuildFrameworkTcImports (tcConfigP, frameworkDLLs, nonFrameworkResolutions) } + // Link all the assemblies together and produce the input typecheck accumulator + let CombineImportedAssembliesTask ( + assemblyName, + tcConfig: TcConfig, + tcConfigP, + tcGlobals, + frameworkTcImports, + nonFrameworkResolutions, + unresolvedReferences, + dependencyProvider, + loadClosureOpt: LoadClosure option, + basicDependencies +#if !NO_TYPEPROVIDERS + ,importsInvalidatedByTypeProvider: Event +#endif + ) = - let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) = - node { + node { + let diagnosticsLogger = CompilationDiagnosticLogger("CombineImportedAssembliesTask", tcConfig.diagnosticsOptions) + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) + let! tcImports = + node { + try + let! tcImports = TcImports.BuildNonFrameworkTcImports(tcConfigP, frameworkTcImports, nonFrameworkResolutions, unresolvedReferences, dependencyProvider) +#if !NO_TYPEPROVIDERS + tcImports.GetCcusExcludingBase() |> Seq.iter (fun ccu -> + // When a CCU reports an invalidation, merge them together and just report a + // general "imports invalidated". This triggers a rebuild. + // + // We are explicit about what the handler closure captures to help reason about the + // lifetime of captured objects, especially in case the type provider instance gets leaked + // or keeps itself alive mistakenly, e.g. via some global state in the type provider instance. + // + // The handler only captures + // 1. a weak reference to the importsInvalidated event. + // + // The IncrementalBuilder holds the strong reference the importsInvalidated event. + // + // In the invalidation handler we use a weak reference to allow the IncrementalBuilder to + // be collected if, for some reason, a TP instance is not disposed or not GC'd. + let capturedImportsInvalidated = WeakReference<_>(importsInvalidatedByTypeProvider) + ccu.Deref.InvalidateEvent.Add(fun _ -> + match capturedImportsInvalidated.TryGetTarget() with + | true, tg -> tg.Trigger() + | _ -> ())) +#endif + return tcImports + with exn -> + Debug.Assert(false, sprintf "Could not BuildAllReferencedDllTcImports %A" exn) + diagnosticsLogger.Warning exn + return frameworkTcImports + } + + let tcInitial, openDecls0 = GetInitialTcEnv (assemblyName, rangeStartup, tcConfig, tcImports, tcGlobals) + let tcState = GetInitialTcState (rangeStartup, assemblyName, tcConfig, tcGlobals, tcImports, tcInitial, openDecls0) + let loadClosureErrors = + [ match loadClosureOpt with + | None -> () + | Some loadClosure -> + for inp in loadClosure.Inputs do + yield! inp.MetaCommandDiagnostics ] + + let initialErrors = Array.append (Array.ofList loadClosureErrors) (diagnosticsLogger.GetDiagnostics()) + let tcInfo = + { + tcState=tcState + tcEnvAtEndOfFile=tcInitial + topAttribs=None + latestCcuSigForFile=None + tcDiagnosticsRev = [ initialErrors ] + moduleNamesDict = Map.empty + tcDependencyFiles = basicDependencies + sigNameOpt = None + } + return tcImports, tcInfo + } + + let ComputeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = + node { let useSimpleResolutionSwitch = "--simpleresolution" let commandLineArgs = projectSnapshot.OtherOptions let defaultFSharpBinariesDir = FSharpCheckerResultsSettings.defaultFSharpBinariesDir let useScriptResolutionRules = projectSnapshot.UseScriptResolutionRules let projectReferences = getProjectReferences projectSnapshot "ComputeBootstrapInfo" - let sourceFiles = projectSnapshot.SourceFileNames // TODO: script support let loadClosureOpt: LoadClosure option = None @@ -192,7 +278,7 @@ type internal TransparentCompiler tcConfigB.useSimpleResolution <- (getSwitchValue useSimpleResolutionSwitch) |> Option.isSome // Apply command-line arguments and collect more source files if they are in the arguments - let sourceFilesNew = ApplyCommandLineArgs(tcConfigB, sourceFiles, commandLineArgs) + let sourceFilesNew = ApplyCommandLineArgs(tcConfigB, projectSnapshot.SourceFileNames, commandLineArgs) // Never open PDB files for the language service, even if --standalone is specified tcConfigB.openDebugInformationForLaterStaticLinking <- false @@ -239,7 +325,7 @@ type internal TransparentCompiler setupConfigFromLoadClosure() let tcConfig = TcConfig.Create(tcConfigB, validate=true) - let outfile, _, assemblyName = tcConfigB.DecideNames sourceFiles + let _outfile, _, assemblyName = tcConfigB.DecideNames sourceFiles // Resolve assemblies and create the framework TcImports. This caches a level of "system" references. No type providers are // included in these references. @@ -270,36 +356,33 @@ type internal TransparentCompiler let diagnosticsLogger = CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) - // Get the names and time stamps of all the non-framework referenced assemblies, which will act - // as inputs to one of the nodes in the build. - // - // This operation is done when constructing the builder itself, rather than as an incremental task. - let nonFrameworkAssemblyInputs = - // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. - // This is ok because not much can actually go wrong here. - let diagnosticsLogger = CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) - // Return the disposable object that cleans up - use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) - - [ for r in nonFrameworkResolutions do - let fileName = r.resolvedPath - yield (Choice1Of2 fileName, (fun (cache: TimeStampCache) -> cache.GetFileTimeStamp fileName)) - - for pr in projectReferences do - yield Choice2Of2 pr, (fun (cache: TimeStampCache) -> cache.GetProjectReferenceTimeStamp pr) ] - - // Start importing + // TODO: might need to put something like this somewhere + //// Get the names and time stamps of all the non-framework referenced assemblies, which will act + //// as inputs to one of the nodes in the build. + //// + //// This operation is done when constructing the builder itself, rather than as an incremental task. + //let nonFrameworkAssemblyInputs = + // // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. + // // This is ok because not much can actually go wrong here. + // let diagnosticsLogger = CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) + // // Return the disposable object that cleans up + // use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) + + // [ for r in nonFrameworkResolutions do + // let fileName = r.resolvedPath + // yield (Choice1Of2 fileName, (fun (cache: TimeStampCache) -> cache.GetFileTimeStamp fileName)) + + // for pr in projectReferences do + // yield Choice2Of2 pr, (fun (cache: TimeStampCache) -> cache.GetProjectReferenceTimeStamp pr) ] let tcConfigP = TcConfigProvider.Constant tcConfig - let beforeFileChecked = Event() - let fileChecked = Event() -#if !NO_TYPEPROVIDERS + #if !NO_TYPEPROVIDERS let importsInvalidatedByTypeProvider = Event() -#endif + #endif // Check for the existence of loaded sources and prepend them to the sources list if present. - let sourceFiles = tcConfig.GetAvailableLoadedSources() @ (sourceFiles |>List.map (fun s -> rangeStartup, s)) + let sourceFiles = tcConfig.GetAvailableLoadedSources() @ (sourceFiles |> List.map (fun s -> rangeStartup, s)) // Mark up the source files with an indicator flag indicating if they are the last source file in the project let sourceFiles = @@ -314,12 +397,7 @@ type internal TransparentCompiler yield file for r in nonFrameworkResolutions do - yield r.resolvedPath ] - - let allDependencies = - [| yield! basicDependencies - for _, f, _ in sourceFiles do - yield f |] + yield r.resolvedPath ] // For scripts, the dependency provider is already available. // For projects create a fresh one for the project. @@ -328,7 +406,7 @@ type internal TransparentCompiler then dependencyProviderForScripts else new DependencyProvider() - let! initialBoundModel = + let! tcImports, initialTcInfo = CombineImportedAssembliesTask( assemblyName, tcConfig, @@ -339,36 +417,67 @@ type internal TransparentCompiler unresolvedReferences, dependencyProvider, loadClosureOpt, - basicDependencies, - keepAssemblyContents, - keepAllBackgroundResolutions, - keepAllBackgroundSymbolUses, - enableBackgroundItemKeyStoreAndSemanticClassification, - enablePartialTypeChecking, - beforeFileChecked, - fileChecked -#if !NO_TYPEPROVIDERS + basicDependencies + #if !NO_TYPEPROVIDERS ,importsInvalidatedByTypeProvider -#endif + #endif ) - let getFSharpSource fileName = - getSource - |> Option.map(fun getSource -> - let timeStamp = DateTime.UtcNow - let getTimeStamp = fun () -> timeStamp - let getSourceText() = getSource fileName - FSharpSource.Create(fileName, getTimeStamp, getSourceText)) - |> Option.defaultWith(fun () -> FSharpSource.CreateFromFile(fileName)) + let fileSnapshots = Map [ for f in projectSnapshot.SourceFiles -> f.FileName, f ] let sourceFiles = sourceFiles - |> List.map (fun (m, fileName, isLastCompiland) -> - { Range = m; Source = getFSharpSource fileName; Flags = isLastCompiland } ) - - return (), () + |> List.map (fun (m, fileName, (isLastCompiland, isExe)) -> + let source = + fileSnapshots.TryFind fileName + |> Option.defaultWith (fun () -> + // TODO: does this commonly happen? + { + FileName = fileName + Version = (FileSystem.GetLastWriteTimeShim fileName).Ticks.ToString() + GetSource = (fun () -> fileName |> File.ReadAllText |> SourceText.ofString |> Task.FromResult) + }) + { Range = m; Source = source; IsLastCompiland = isLastCompiland; IsExe = isExe }) + + return Some { + TcConfig = tcConfig + TcImports = tcImports + TcGlobals = tcGlobals + InitialTcInfo = initialTcInfo + SourceFiles = sourceFiles + LoadClosure = loadClosureOpt + } } + let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) _key = + node { + + // Trap and report diagnostics from creation. + let delayedLogger = CapturingDiagnosticsLogger("IncrementalBuilderCreation") + use _ = new CompilationGlobalsScope(delayedLogger, BuildPhase.Parameter) + + let! bootstrapInfoOpt = + node { + try + return! ComputeBootstrapInfoInner projectSnapshot + with exn -> + errorRecoveryNoRange exn + return None + } + + let diagnostics = + match bootstrapInfoOpt with + | Some bootstrapInfo -> + let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions + let diagnosticsLogger = CompilationDiagnosticLogger("IncrementalBuilderCreation", diagnosticsOptions) + delayedLogger.CommitDelayedDiagnostics diagnosticsLogger + diagnosticsLogger.GetDiagnostics() + | _ -> + Array.ofList delayedLogger.Diagnostics + |> Array.map (fun (diagnostic, severity) -> + FSharpDiagnostic.CreateFromException(diagnostic, severity, range.Zero, suggestNamesForErrors)) + return bootstrapInfoOpt, diagnostics + } let ComputeParseFile (file: FSharpFile) (projectSnapshot: FSharpProjectSnapshot) bootstrapInfo userOpName _key = node { @@ -383,8 +492,7 @@ type internal TransparentCompiler // GraphNode.SetPreferredUILang tcPrior.TcConfig.preferredUiLang let! sourceText = file.Source.GetSource() |> NodeCode.AwaitTask - - return ParseAndCheckFile.parseFile ( + let diagnostics, parsedInput, anyErrors = ParseAndCheckFile.parseFile( sourceText, file.Source.FileName, parsingOptions, @@ -392,14 +500,64 @@ type internal TransparentCompiler suggestNamesForErrors, captureIdentifiersWhenParsing ) - + return diagnostics, parsedInput, anyErrors, sourceText } + let ComputeDependencyGraphForLastFile parsedInputs (tcConfig: TcConfig) _key = + node { + let sourceFiles: FileInProject array = + parsedInputs + |> Seq.toArray + |> Array.mapi (fun idx (input: ParsedInput) -> + { + Idx = idx + FileName = input.FileName + ParsedInput = input + }) + + let filePairs = FilePairMap(sourceFiles) + + // TODO: we will probably want to cache and re-use larger graphs if available + let graph = + DependencyResolution.mkGraph tcConfig.compilingFSharpCore filePairs sourceFiles + |> Graph.subGraphFor (sourceFiles |> Array.last).Idx + + return graph, filePairs + } + + // Type check everything that is needed to check given file + let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) userOpName _key: NodeCode = + node { + + // parse required files + let files = seq { + yield! bootstrapInfo.SourceFiles |> Seq.takeWhile ((<>) file) + file + } + + let! parsedInputs = + files + |> Seq.map (fun f -> + node { + let! _diagnostics, parsedInput, _anyErrors, _sourceText = ParseFileCache.Get((f.Source.Key, f.IsLastCompiland, f.IsExe), ComputeParseFile f projectSnapshot bootstrapInfo userOpName) + // TODO: Do we need to do something here when we get parse errors? + return parsedInput + }) + |> NodeCode.Parallel + + // compute dependency graph + let graphKey = projectSnapshot.UpTo(file.Source.FileName).SourceFileNames + let! _graph, _filePairs = DependencyGraphForLastFileCache.Get(graphKey, ComputeDependencyGraphForLastFile parsedInputs bootstrapInfo.TcConfig) + + + + return bootstrapInfo.InitialTcInfo + } let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) userOpName _key = node { - let! bootstrapInfoOpt, creationDiags = ComputeBootstrapInfo projectSnapshot // probably cache + let! bootstrapInfoOpt, creationDiags = BootstrapInfoCache.Get(projectSnapshot.Key, ComputeBootstrapInfo projectSnapshot) // probably cache match bootstrapInfoOpt with | None -> @@ -410,7 +568,11 @@ type internal TransparentCompiler | Some bootstrapInfo -> let file = bootstrapInfo.SourceFiles |> List.find (fun f -> f.Source.FileName = fileName) - let! parseDiagnostics, parseTree, anyErrors = ParseFileCache.Get(file.Source.Key, ComputeParseFile file projectSnapshot bootstrapInfo userOpName) + + let priorSnapshot = projectSnapshot.UpTo fileName + let! tcInfo = TcPriorCache.Get(priorSnapshot.Key, ComputeTcPrior file bootstrapInfo priorSnapshot userOpName) + + let! parseDiagnostics, parseTree, anyErrors, sourceText = ParseFileCache.Get((file.Source.Key, file.IsLastCompiland, file.IsExe), ComputeParseFile file projectSnapshot bootstrapInfo userOpName) // TODO: check if we really need this in parse results let dependencyFiles = [||] @@ -419,19 +581,30 @@ type internal TransparentCompiler FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, dependencyFiles) let! checkResults = - bc.CheckOneFileImpl( + FSharpCheckFileResults.CheckOneFile( parseResults, sourceText, fileName, - options, - fileVersion, - builder, - tcPrior, - tcInfo, - creationDiags + projectSnapshot.ProjectFileName, + bootstrapInfo.TcConfig, + bootstrapInfo.TcGlobals, + bootstrapInfo.TcImports, + tcInfo.tcState, + tcInfo.moduleNamesDict, + bootstrapInfo.LoadClosure, + tcInfo.TcDiagnostics, + projectSnapshot.IsIncompleteTypeCheckEnvironment, + projectSnapshot.ToOptions(), + None, + Array.ofList tcInfo.tcDependencyFiles, + creationDiags, + parseResults.Diagnostics, + keepAssemblyContents, + suggestNamesForErrors ) + |> NodeCode.FromCancellable - return (parseResults, checkResults) + return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) } member _.ParseAndCheckFileInProject @@ -439,10 +612,9 @@ type internal TransparentCompiler fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string - ) : NodeCode = node { - ignore userOpName // TODO + ) = node { let key = fileName, projectSnapshot.Key - return! ParseAndCheckFileInProjectCache.Get(key, ComputeParseAndCheckFileInProject fileName projectSnapshot) + return! ParseAndCheckFileInProjectCache.Get(key, ComputeParseAndCheckFileInProject fileName projectSnapshot userOpName) } diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index bd7501463d1..22d9e69da7d 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -17,6 +17,7 @@ open FSharp.Compiler.AbstractIL.IL open FSharp.Compiler.AbstractIL.ILBinaryReader open FSharp.Compiler.AbstractIL.ILDynamicAssemblyWriter open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CodeAnalysis.TransparentCompiler open FSharp.Compiler.CompilerConfig open FSharp.Compiler.CompilerDiagnostics open FSharp.Compiler.CompilerImports diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 9470772e261..bc4a072aee6 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -197,6 +197,7 @@ + diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs new file mode 100644 index 00000000000..0c18032e33b --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs @@ -0,0 +1,30 @@ +module FSharp.Compiler.ComponentTests.TypeChecks.Graph.GraphOperations + +open Xunit +open FSharp.Compiler.GraphChecking + + +[] +let ``Transform graph to layers of leaves`` () = + + let g = Graph.make [ + 'B', [|'A'|] + 'C', [|'A'|] + 'E', [|'A'; 'B'; 'C'|] + 'F', [|'C'; 'D'|] + ] + + //let layers = g |> Graph.leaves |> Seq.toList + + let _expected = [ + [|'A'; 'D'|] + [|'B'; 'C'|] + [|'E'; 'F'|] + ] + + let _x = Graph.reverse g + + () + + //Assert.Equal(expected, layers) + From a0d4e854642da9d6e64a289abe26a65675ecb8a8 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 26 Apr 2023 12:33:24 +0200 Subject: [PATCH 007/222] wip --- src/Compiler/Driver/GraphChecking/Graph.fs | 9 ++ src/Compiler/Driver/GraphChecking/Graph.fsi | 2 + src/Compiler/Driver/ParseAndCheckInputs.fs | 6 +- src/Compiler/Service/FSharpCheckerResults.fs | 5 +- src/Compiler/Service/FSharpCheckerResults.fsi | 3 + src/Compiler/Service/IncrementalBuild.fs | 8 +- src/Compiler/Service/TransparentCompiler.fs | 110 ++++++++++++++++-- .../TypeChecks/Graph/GraphOperations.fs | 70 +++++++++-- .../ProjectGeneration.fs | 13 +++ .../LanguageService/LanguageService.fs | 3 +- 10 files changed, 201 insertions(+), 28 deletions(-) diff --git a/src/Compiler/Driver/GraphChecking/Graph.fs b/src/Compiler/Driver/GraphChecking/Graph.fs index 4a2cbb29661..c3e6de9731c 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fs +++ b/src/Compiler/Driver/GraphChecking/Graph.fs @@ -69,6 +69,7 @@ module internal Graph = |> readOnlyDict |> addIfMissing originalGraph.Keys + /// Returns leaves of the graph and the remaining graph without the leaves. let cutLeaves (graph: Graph<'Node>) = let notLeaves = set [ for (KeyValue (node, deps)) in graph do if deps.Length > 0 then node ] let leaves = @@ -79,6 +80,14 @@ module internal Graph = if deps.Length > 0 then node, deps |> Array.filter (leaves.Contains >> not) } |> make + /// Returns layers of leaves repeatedly removed from the graph until there's nothing left + let leafSequence (graph: Graph<'Node>) = + let rec loop (graph: Graph<'Node>) acc = + match graph |> cutLeaves with + | leaves, _ when leaves.IsEmpty -> acc + | leaves, graph -> seq { yield! acc; leaves } |> loop graph + loop graph Seq.empty + let printCustom (graph: Graph<'Node>) (nodePrinter: 'Node -> string) : unit = printfn "Graph:" let join (xs: string[]) = System.String.Join(", ", xs) diff --git a/src/Compiler/Driver/GraphChecking/Graph.fsi b/src/Compiler/Driver/GraphChecking/Graph.fsi index c6afc3ee768..8c6b9c29079 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fsi +++ b/src/Compiler/Driver/GraphChecking/Graph.fsi @@ -17,6 +17,8 @@ module internal Graph = val subGraphFor: node: 'Node -> graph: Graph<'Node> -> Graph<'Node> when 'Node: equality /// Create a reverse of the graph. val reverse<'Node when 'Node: equality> : originalGraph: Graph<'Node> -> Graph<'Node> + /// Returns layers of leaves repeatedly removed from the graph until there's nothing left + val leafSequence: graph: Graph<'Node> -> Set<'Node> seq /// Print the contents of the graph to the standard output. val print: graph: Graph<'Node> -> unit /// Create a simple Mermaid graph and save it under the path specified. diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index 66327892688..55c200bf6fc 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1467,14 +1467,14 @@ let CheckOneInputWithCallback prefixPathOpt, tcSink, tcState: TcState, - inp: ParsedInput, + input: ParsedInput, _skipImplIfSigExists: bool): (unit -> bool) * TcConfig * TcImports * TcGlobals * LongIdent option * TcResultsSink * TcState * ParsedInput * bool) : Cancellable> = cancellable { try CheckSimulateException tcConfig - let m = inp.Range + let m = input.Range let amap = tcImports.GetImportMap() let conditionalDefines = @@ -1483,7 +1483,7 @@ let CheckOneInputWithCallback else Some tcConfig.conditionalDefines - match inp with + match input with | ParsedInput.SigFile file -> let qualNameOfFile = file.QualifiedName diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 4a07c23c66e..9c3855789e0 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -192,9 +192,12 @@ type FSharpProjectSnapshot = member po.ProjectDirectory = Path.GetDirectoryName(po.ProjectFileName) + member this.UpTo fileIndex = + { this with SourceFiles = this.SourceFiles[..fileIndex] } + member this.UpTo fileName = let fileIndex = this.SourceFiles |> List.findIndex (fun x -> x.FileName = fileName) - { this with SourceFiles = this.SourceFiles[..fileIndex] } + this.UpTo fileIndex member this.Key = { ProjectFileName = this.ProjectFileName diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 3fe91074124..cf7f6f45714 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -116,6 +116,9 @@ type FSharpProjectSnapshot = member SourceFileNames: string list + /// A snapshot of the same project but only up to the given file index (including). + member UpTo: fileIndex: int -> FSharpProjectSnapshot + /// A snapshot of the same project but only up to the given file (including). member UpTo: fileName: string -> FSharpProjectSnapshot diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index 4d88e39c428..03b540bc256 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -325,7 +325,7 @@ type BoundModel private ( let getTcInfoExtras (typeCheck: GraphNode) = node { - let! _ , sink, implFile, fileName = typeCheck.GetOrComputeValue() + let! _x , sink, implFile, fileName = typeCheck.GetOrComputeValue() // Build symbol keys let itemKeyStore, semanticClassification = if enableBackgroundItemKeyStoreAndSemanticClassification then @@ -914,7 +914,7 @@ module IncrementalBuilderStateHelpers = return! prevBoundModel.Next(syntaxTree) }) - let rec createFinalizeBoundModelGraphNode (initialState: IncrementalBuilderInitialState) (boundModels: GraphNode seq) = + let createFinalizeBoundModelGraphNode (initialState: IncrementalBuilderInitialState) (boundModels: GraphNode seq) = GraphNode(node { use _ = Activity.start "GetCheckResultsAndImplementationsForProject" [|Activity.Tags.project, initialState.outfile|] let! result = @@ -928,7 +928,7 @@ module IncrementalBuilderStateHelpers = return result, DateTime.UtcNow }) - and computeStampedFileNames (initialState: IncrementalBuilderInitialState) (state: IncrementalBuilderState) (cache: TimeStampCache) = + let computeStampedFileNames (initialState: IncrementalBuilderInitialState) (state: IncrementalBuilderState) (cache: TimeStampCache) = let slots = if initialState.useChangeNotifications then state.slots @@ -967,7 +967,7 @@ module IncrementalBuilderStateHelpers = else state - and computeStampedReferencedAssemblies (initialState: IncrementalBuilderInitialState) state canTriggerInvalidation (cache: TimeStampCache) = + let computeStampedReferencedAssemblies (initialState: IncrementalBuilderInitialState) state canTriggerInvalidation (cache: TimeStampCache) = let stampedReferencedAssemblies = state.stampedReferencedAssemblies.ToBuilder() let mutable referencesUpdated = false diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 9ee1aa8b5d6..8d2825ee6c7 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -29,6 +29,8 @@ open System.Threading.Tasks open FSharp.Compiler.ParseAndCheckInputs open FSharp.Compiler.GraphChecking open FSharp.Compiler.Syntax +open FSharp.Compiler.CompilerDiagnostics +open FSharp.Compiler.NameResolution type internal FSharpFile = { @@ -48,6 +50,8 @@ type internal BootstrapInfo = { LoadClosure: LoadClosure option } +//type ParseResults + type internal TransparentCompiler ( legacyReferenceResolver, @@ -67,13 +71,14 @@ type internal TransparentCompiler ) = // Is having just one of these ok? - let _lexResourceManager = Lexhelp.LexResourceManager() + let lexResourceManager = Lexhelp.LexResourceManager() let ParseFileCache = AsyncMemoize() let ParseAndCheckFileInProjectCache = AsyncMemoize() let FrameworkImportsCache = AsyncMemoize() let BootstrapInfoCache = AsyncMemoize() let TcPriorCache = AsyncMemoize() + let TcIntermediateCache = AsyncMemoize() let DependencyGraphForLastFileCache = AsyncMemoize() // We currently share one global dependency provider for all scripts for the FSharpChecker. @@ -221,7 +226,7 @@ type internal TransparentCompiler return tcImports, tcInfo } - let ComputeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = + let computeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = node { let useSimpleResolutionSwitch = "--simpleresolution" let commandLineArgs = projectSnapshot.OtherOptions @@ -459,7 +464,7 @@ type internal TransparentCompiler let! bootstrapInfoOpt = node { try - return! ComputeBootstrapInfoInner projectSnapshot + return! computeBootstrapInfoInner projectSnapshot with exn -> errorRecoveryNoRange exn return None @@ -479,7 +484,7 @@ type internal TransparentCompiler return bootstrapInfoOpt, diagnostics } - let ComputeParseFile (file: FSharpFile) (projectSnapshot: FSharpProjectSnapshot) bootstrapInfo userOpName _key = node { + let ComputeParseFile' (file: FSharpFile) (projectSnapshot: FSharpProjectSnapshot) bootstrapInfo userOpName _key = node { let parsingOptions = FSharpParsingOptions.FromTcConfig( @@ -503,6 +508,22 @@ type internal TransparentCompiler return diagnostics, parsedInput, anyErrors, sourceText } + let ComputeParseFile (file: FSharpFile) bootstrapInfo _key = + node { + let tcConfig = bootstrapInfo.TcConfig + let diagnosticsLogger = CompilationDiagnosticLogger("Parse", tcConfig.diagnosticsOptions) + // Return the disposable object that cleans up + use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parse) + + let flags = file.IsLastCompiland, file.IsExe + let fileName = file.Source.FileName + let! sourceText = file.Source.GetSource() |> NodeCode.AwaitTask + + let input = ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) + + return input, diagnosticsLogger.GetDiagnostics() + } + let ComputeDependencyGraphForLastFile parsedInputs (tcConfig: TcConfig) _key = node { let sourceFiles: FileInProject array = @@ -525,6 +546,63 @@ type internal TransparentCompiler return graph, filePairs } + let ComputeTcIntermediate (parsedInput: ParsedInput) bootstrapInfo prevTcInfo _key = + node { + let input = parsedInput + let fileName = input.FileName + let tcConfig = bootstrapInfo.TcConfig + let tcGlobals = bootstrapInfo.TcGlobals + let tcImports = bootstrapInfo.TcImports + + let capturingDiagnosticsLogger = CapturingDiagnosticsLogger("TypeCheck") + let diagnosticsLogger = GetDiagnosticsLoggerFilteringByScopedPragmas(false, input.ScopedPragmas, tcConfig.diagnosticsOptions, capturingDiagnosticsLogger) + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.TypeCheck) + + //beforeFileChecked.Trigger fileName + + ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName fileName, tcImports.DependencyProvider) |> ignore + let sink = TcResultsSinkImpl(tcGlobals) + let hadParseErrors = not (Array.isEmpty parseErrors) + let input, moduleNamesDict = DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input + + let! (tcEnvAtEndOfFile, topAttribs, implFile, ccuSigForFile), tcState = + CheckOneInput ( + (fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), + tcConfig, tcImports, + tcGlobals, + None, + TcResultsSink.WithSink sink, + prevTcInfo.tcState, input ) + |> NodeCode.FromCancellable + + //fileChecked.Trigger fileName + + let newErrors = Array.append parseErrors (capturingDiagnosticsLogger.Diagnostics |> List.toArray) + let tcEnvAtEndOfFile = if keepAllBackgroundResolutions then tcEnvAtEndOfFile else tcState.TcEnvFromImpls + + let tcInfo = + { + tcState = tcState + tcEnvAtEndOfFile = tcEnvAtEndOfFile + moduleNamesDict = moduleNamesDict + latestCcuSigForFile = Some ccuSigForFile + tcDiagnosticsRev = newErrors :: prevTcInfo.tcDiagnosticsRev + topAttribs = Some topAttribs + tcDependencyFiles = fileName :: prevTcInfo.tcDependencyFiles + sigNameOpt = + match input with + | ParsedInput.SigFile sigFile -> + Some(sigFile.FileName, sigFile.QualifiedName) + | _ -> + None + } + return tcInfo, sink, implFile, fileName + + + } + + + // Type check everything that is needed to check given file let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) userOpName _key: NodeCode = node { @@ -546,11 +624,25 @@ type internal TransparentCompiler |> NodeCode.Parallel // compute dependency graph - let graphKey = projectSnapshot.UpTo(file.Source.FileName).SourceFileNames - let! _graph, _filePairs = DependencyGraphForLastFileCache.Get(graphKey, ComputeDependencyGraphForLastFile parsedInputs bootstrapInfo.TcConfig) - - - + let graphKey = projectSnapshot.UpTo(file.Source.FileName).SourceFiles |> List.map (fun s -> s.Key) + let! graph, _filePairs = DependencyGraphForLastFileCache.Get(graphKey, ComputeDependencyGraphForLastFile parsedInputs bootstrapInfo.TcConfig) + + // layers that can be processed in parallel + let layers = Graph.leafSequence graph + + let rec processLayer (layers: Set list) state = node { + match layers with + | [] -> return state + | layer::rest -> + let! results = + layer + |> Seq.map (fun fileIndex -> + let key = projectSnapshot.UpTo(fileIndex).Key + TcIntermediateCache.Get(key, ComputeTcIntermediate parsedInputs[fileIndex] bootstrapInfo state)) + |> NodeCode.Parallel + return! processLayer rest (combineResults state results) + } + return bootstrapInfo.InitialTcInfo } diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs index 0c18032e33b..95d966441e1 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs @@ -1,11 +1,15 @@ module FSharp.Compiler.ComponentTests.TypeChecks.Graph.GraphOperations +open System.IO open Xunit open FSharp.Compiler.GraphChecking +open FSharp.Test.ProjectGeneration +open FSharp.Compiler.Text +open FSharp.Compiler [] -let ``Transform graph to layers of leaves`` () = +let ``Transform graph to layers of leaves`` () = let g = Graph.make [ 'B', [|'A'|] @@ -14,17 +18,63 @@ let ``Transform graph to layers of leaves`` () = 'F', [|'C'; 'D'|] ] - //let layers = g |> Graph.leaves |> Seq.toList - - let _expected = [ - [|'A'; 'D'|] - [|'B'; 'C'|] - [|'E'; 'F'|] + let expected = [ + set [ 'A'; 'D' ] + set [ 'B'; 'C' ] + set [ 'E'; 'F' ] ] - let _x = Graph.reverse g + let result = Graph.leafSequence g |> Seq.toList + + Assert.Equal list>(expected, result) + + +[] +let ``See what this does`` () = + + SyntheticProject.Create( + sourceFile "A" [] |> addSignatureFile, + sourceFile "B" ["A"] |> addSignatureFile, + sourceFile "C" ["A"] |> addSignatureFile, + sourceFile "D" [] |> addSignatureFile, + sourceFile "E" ["A"; "B"; "C"] |> addSignatureFile, + sourceFile "F" ["C"; "D"] |> addSignatureFile + ).Workflow { + withProject (fun project checker -> + async { + let options = project.GetProjectOptions checker + let options, _ = checker.GetParsingOptionsFromProjectOptions options + let! inputs = + project.SourceFilePaths + |> Seq.map (fun path -> path, File.ReadAllText path |> SourceText.ofString) + |> Seq.map (fun (path, text) -> checker.ParseFile(path, text, options)) + |> Async.Parallel + + let sourceFiles: FileInProject array = + inputs + |> Seq.map (fun x -> x.ParseTree) + |> Seq.toArray + |> Array.mapi (fun idx (input: Syntax.ParsedInput) -> + { + Idx = idx + FileName = input.FileName + ParsedInput = input + }) + + let filePairs = FilePairMap(sourceFiles) + + let fullGraph = + DependencyResolution.mkGraph false filePairs sourceFiles + |> Graph.map (fun idx -> project.SourceFilePaths[idx] |> Path.GetFileName) + + let subGraph = fullGraph |> Graph.subGraphFor "FileF.fs" + + let layers = Graph.leafSequence subGraph |> Seq.toList - () + ignore layers - //Assert.Equal(expected, layers) + return () + } + ) + } diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 37173dc901d..7a3ca688eb5 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -193,6 +193,11 @@ type SyntheticProject with member this.GetFilePath fileId = this.Find fileId |> getFilePath this member this.GetSignatureFilePath fileId = this.Find fileId |> getSignatureFilePath this + member this.SourceFilePaths = + [ for f in this.SourceFiles do + if f.HasSignatureFile then this.GetSignatureFilePath f.Id + this.GetFilePath f.Id ] + let private renderNamespaceModule (project: SyntheticProject) (f: SyntheticSourceFile) = seq { @@ -617,6 +622,14 @@ type ProjectWorkflowBuilder if initialContext.IsNone then this.DeleteProjectDir() + [] + member this.WithProject(workflow: Async, f) = + workflow |> mapProjectAsync (fun project -> + async { + do! f project checker + return project + }) + /// Change contents of given file using `processFile` function. /// Does not save the file to disk. [] diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index dcbafaa0fa0..5fbb21edc2b 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -156,7 +156,8 @@ type internal FSharpWorkspaceServiceFactory [ Date: Thu, 27 Apr 2023 11:03:33 +0200 Subject: [PATCH 008/222] wip --- src/Compiler/Service/TransparentCompiler.fs | 45 ++++++++++++--------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 8d2825ee6c7..853632507a8 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -546,7 +546,7 @@ type internal TransparentCompiler return graph, filePairs } - let ComputeTcIntermediate (parsedInput: ParsedInput) bootstrapInfo prevTcInfo _key = + let ComputeTcIntermediate (parsedInput: ParsedInput, parseErrors) bootstrapInfo prevTcInfo _key = node { let input = parsedInput let fileName = input.FileName @@ -586,9 +586,9 @@ type internal TransparentCompiler tcEnvAtEndOfFile = tcEnvAtEndOfFile moduleNamesDict = moduleNamesDict latestCcuSigForFile = Some ccuSigForFile - tcDiagnosticsRev = newErrors :: prevTcInfo.tcDiagnosticsRev + tcDiagnosticsRev = [newErrors] topAttribs = Some topAttribs - tcDependencyFiles = fileName :: prevTcInfo.tcDependencyFiles + tcDependencyFiles = [fileName] sigNameOpt = match input with | ParsedInput.SigFile sigFile -> @@ -597,12 +597,8 @@ type internal TransparentCompiler None } return tcInfo, sink, implFile, fileName - - } - - // Type check everything that is needed to check given file let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) userOpName _key: NodeCode = node { @@ -616,16 +612,13 @@ type internal TransparentCompiler let! parsedInputs = files |> Seq.map (fun f -> - node { - let! _diagnostics, parsedInput, _anyErrors, _sourceText = ParseFileCache.Get((f.Source.Key, f.IsLastCompiland, f.IsExe), ComputeParseFile f projectSnapshot bootstrapInfo userOpName) - // TODO: Do we need to do something here when we get parse errors? - return parsedInput - }) + let key = f.Source.Key, f.IsLastCompiland, f.IsExe + ParseFileCache.Get(key, ComputeParseFile f bootstrapInfo)) |> NodeCode.Parallel // compute dependency graph let graphKey = projectSnapshot.UpTo(file.Source.FileName).SourceFiles |> List.map (fun s -> s.Key) - let! graph, _filePairs = DependencyGraphForLastFileCache.Get(graphKey, ComputeDependencyGraphForLastFile parsedInputs bootstrapInfo.TcConfig) + let! graph, _filePairs = DependencyGraphForLastFileCache.Get(graphKey, ComputeDependencyGraphForLastFile (parsedInputs |> Seq.map fst) bootstrapInfo.TcConfig) // layers that can be processed in parallel let layers = Graph.leafSequence graph @@ -642,7 +635,7 @@ type internal TransparentCompiler |> NodeCode.Parallel return! processLayer rest (combineResults state results) } - + return bootstrapInfo.InitialTcInfo } @@ -664,14 +657,30 @@ type internal TransparentCompiler let priorSnapshot = projectSnapshot.UpTo fileName let! tcInfo = TcPriorCache.Get(priorSnapshot.Key, ComputeTcPrior file bootstrapInfo priorSnapshot userOpName) - let! parseDiagnostics, parseTree, anyErrors, sourceText = ParseFileCache.Get((file.Source.Key, file.IsLastCompiland, file.IsExe), ComputeParseFile file projectSnapshot bootstrapInfo userOpName) + let! parseTree, parseDiagnostics = ParseFileCache.Get((file.Source.Key, file.IsLastCompiland, file.IsExe), ComputeParseFile file bootstrapInfo) - // TODO: check if we really need this in parse results - let dependencyFiles = [||] + let parseDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + bootstrapInfo.TcConfig.diagnosticsOptions, + false, + fileName, + parseDiagnostics, + suggestNamesForErrors + ) + + let diagnostics = [| yield! creationDiags; yield! parseDiagnostics |] let parseResults = - FSharpParseFileResults(parseDiagnostics, parseTree, anyErrors, dependencyFiles) + FSharpParseFileResults( + diagnostics = diagnostics, + input = parseTree, + parseHadErrors = (parseDiagnostics.Length > 0), + // TODO: check if we really need this in parse results + dependencyFiles = [||] + ) + // TODO: this might be replaced... probably should use intermediate TC result + let sourceText = Unchecked.defaultof<_> let! checkResults = FSharpCheckFileResults.CheckOneFile( parseResults, From e162b0a218f5464c997125640d2db592b8bfffa1 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 27 Apr 2023 12:52:58 +0200 Subject: [PATCH 009/222] wip --- src/Compiler/Driver/GraphChecking/Graph.fs | 48 +- src/Compiler/Service/FSharpCheckerResults.fs | 81 ++- src/Compiler/Service/FSharpCheckerResults.fsi | 36 +- src/Compiler/Service/IncrementalBuild.fs | 2 +- src/Compiler/Service/IncrementalBuild.fsi | 9 +- src/Compiler/Service/TransparentCompiler.fs | 630 +++++++++++------- src/Compiler/Service/service.fs | 8 +- 7 files changed, 502 insertions(+), 312 deletions(-) diff --git a/src/Compiler/Driver/GraphChecking/Graph.fs b/src/Compiler/Driver/GraphChecking/Graph.fs index c3e6de9731c..688fffc29bb 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fs +++ b/src/Compiler/Driver/GraphChecking/Graph.fs @@ -41,7 +41,7 @@ module internal Graph = dfs node visited |> Seq.toArray - let transitive<'Node when 'Node: equality> (graph: Graph<'Node>) : Graph<'Node> = + let transitive<'Node when 'Node: equality> (graph: Graph<'Node>) : Graph<'Node> = graph.Keys |> Seq.toArray |> Array.Parallel.map (fun node -> node, graph |> transitiveDeps node) @@ -51,12 +51,15 @@ module internal Graph = let subGraphFor node graph = let allDeps = graph |> transitiveDeps node let relevant n = n = node || allDeps |> Array.contains n + graph |> Seq.choose (fun (KeyValue (src, deps)) -> - if relevant src then Some (src, deps |> Array.filter relevant) else None) + if relevant src then + Some(src, deps |> Array.filter relevant) + else + None) |> make - /// Create a reverse of the graph let reverse (originalGraph: Graph<'Node>) : Graph<'Node> = originalGraph @@ -71,21 +74,44 @@ module internal Graph = /// Returns leaves of the graph and the remaining graph without the leaves. let cutLeaves (graph: Graph<'Node>) = - let notLeaves = set [ for (KeyValue (node, deps)) in graph do if deps.Length > 0 then node ] - let leaves = - set [ for (KeyValue (node, deps)) in graph do - if deps.Length = 0 then node - yield! deps |> Array.filter (notLeaves.Contains >> not) ] - leaves, seq { for (KeyValue (node, deps)) in graph do + let notLeaves = + set + [ + for (KeyValue (node, deps)) in graph do if deps.Length > 0 then - node, deps |> Array.filter (leaves.Contains >> not) } |> make + node + ] + + let leaves = + set + [ + for (KeyValue (node, deps)) in graph do + if deps.Length = 0 then + node + + yield! deps |> Array.filter (notLeaves.Contains >> not) + ] + + leaves, + seq { + for (KeyValue (node, deps)) in graph do + if deps.Length > 0 then + node, deps |> Array.filter (leaves.Contains >> not) + } + |> make /// Returns layers of leaves repeatedly removed from the graph until there's nothing left let leafSequence (graph: Graph<'Node>) = let rec loop (graph: Graph<'Node>) acc = match graph |> cutLeaves with | leaves, _ when leaves.IsEmpty -> acc - | leaves, graph -> seq { yield! acc; leaves } |> loop graph + | leaves, graph -> + seq { + yield! acc + leaves + } + |> loop graph + loop graph Seq.empty let printCustom (graph: Graph<'Node>) (nodePrinter: 'Node -> string) : unit = diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 9c3855789e0..bf6480fd8df 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -115,8 +115,6 @@ type internal DelayedILModuleReader = } | _ -> cancellable.Return(Some this.result) - - type FSharpFileKey = string * string type FSharpProjectSnapshotKey = @@ -132,12 +130,15 @@ type FSharpProjectSnapshotKey = } [] -type FSharpFileSnapshot = { - FileName: string - Version: string - GetSource: unit -> Task -} with +type FSharpFileSnapshot = + { + FileName: string + Version: string + GetSource: unit -> Task + } + member this.Key = this.FileName, this.Version + override this.Equals(o) = match o with | :? FSharpFileSnapshot as o -> o.FileName = this.FileName && o.Version = this.Version @@ -145,7 +146,6 @@ type FSharpFileSnapshot = { override this.GetHashCode() = this.Key.GetHashCode() - [] type FSharpProjectSnapshot = { @@ -161,6 +161,7 @@ type FSharpProjectSnapshot = OriginalLoadReferences: (range * string * string) list Stamp: int64 option } + static member UseSameProject(options1, options2) = match options1.ProjectId, options2.ProjectId with | Some (projectId1), Some (projectId2) when @@ -193,20 +194,26 @@ type FSharpProjectSnapshot = member po.ProjectDirectory = Path.GetDirectoryName(po.ProjectFileName) member this.UpTo fileIndex = - { this with SourceFiles = this.SourceFiles[..fileIndex] } + { this with + SourceFiles = this.SourceFiles[..fileIndex] + } member this.UpTo fileName = let fileIndex = this.SourceFiles |> List.findIndex (fun x -> x.FileName = fileName) this.UpTo fileIndex - member this.Key = { - ProjectFileName = this.ProjectFileName - SourceFiles = this.SourceFiles |> List.map (fun x -> x.Key) - OtherOptions = this.OtherOptions - ReferencedProjects = this.ReferencedProjects |> List.map (function FSharpReference (_, x) -> x.Key) - IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment - UseScriptResolutionRules = this.UseScriptResolutionRules - } + member this.Key = + { + ProjectFileName = this.ProjectFileName + SourceFiles = this.SourceFiles |> List.map (fun x -> x.Key) + OtherOptions = this.OtherOptions + ReferencedProjects = + this.ReferencedProjects + |> List.map (function + | FSharpReference (_, x) -> x.Key) + IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = this.UseScriptResolutionRules + } member this.SourceFileNames = this.SourceFiles |> List.map (fun x -> x.FileName) @@ -226,14 +233,17 @@ and [] public FSharpReferencedProjectSnapshot = /// The fully qualified path to the output of the referenced project. This should be the same value as the -r /// reference in the project options for this referenced project. /// - member this.OutputFile = match this with FSharpReference (projectOutputFile, _) -> projectOutputFile + member this.OutputFile = + match this with + | FSharpReference (projectOutputFile, _) -> projectOutputFile /// /// Creates a reference for an F# project. The physical data for it is stored/cached inside of the compiler service. /// /// The fully qualified path to the output of the referenced project. This should be the same value as the -r reference in the project options for this referenced project. /// The Project Options for this F# project - static member CreateFSharp(projectOutputFile, options: FSharpProjectSnapshot) = FSharpReference (projectOutputFile, options) + static member CreateFSharp(projectOutputFile, options: FSharpProjectSnapshot) = + FSharpReference(projectOutputFile, options) override this.Equals(o) = match o with @@ -246,7 +256,6 @@ and [] public FSharpReferencedProjectSnapshot = override this.GetHashCode() = this.OutputFile.GetHashCode() - [] type FSharpReferencedProject = | FSharpReference of projectOutputFile: string * options: FSharpProjectOptions @@ -336,19 +345,25 @@ and FSharpProjectOptions = "FSharpProjectOptions(" + this.ProjectFileName + ")" type FSharpProjectSnapshot with - member this.ToOptions (): FSharpProjectOptions = { - ProjectFileName = this.ProjectFileName - ProjectId = this.ProjectId - SourceFiles = this.SourceFiles |> Seq.map (fun x -> x.FileName) |> Seq.toArray - OtherOptions = this.OtherOptions |> List.toArray - ReferencedProjects = this.ReferencedProjects |> Seq.map (function FSharpReference (name, opts) -> FSharpReferencedProject.FSharpReference (name, opts.ToOptions())) |> Seq.toArray - IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment - UseScriptResolutionRules = this.UseScriptResolutionRules - LoadTime = this.LoadTime - UnresolvedReferences = this.UnresolvedReferences - OriginalLoadReferences = this.OriginalLoadReferences - Stamp = this.Stamp - } + + member this.ToOptions() : FSharpProjectOptions = + { + ProjectFileName = this.ProjectFileName + ProjectId = this.ProjectId + SourceFiles = this.SourceFiles |> Seq.map (fun x -> x.FileName) |> Seq.toArray + OtherOptions = this.OtherOptions |> List.toArray + ReferencedProjects = + this.ReferencedProjects + |> Seq.map (function + | FSharpReference (name, opts) -> FSharpReferencedProject.FSharpReference(name, opts.ToOptions())) + |> Seq.toArray + IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = this.UseScriptResolutionRules + LoadTime = this.LoadTime + UnresolvedReferences = this.UnresolvedReferences + OriginalLoadReferences = this.OriginalLoadReferences + Stamp = this.Stamp + } [] module internal FSharpCheckerResultsSettings = diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index cf7f6f45714..04e6667ca06 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -39,27 +39,25 @@ type internal DelayedILModuleReader = /// Unused in this API type public FSharpUnresolvedReferencesSet = internal FSharpUnresolvedReferencesSet of UnresolvedAssemblyReference list - type FSharpFileKey = string * string type FSharpProjectSnapshotKey = - { - ProjectFileName: string - SourceFiles: FSharpFileKey list - OtherOptions: string list - ReferencedProjects: FSharpProjectSnapshotKey list + { ProjectFileName: string + SourceFiles: FSharpFileKey list + OtherOptions: string list + ReferencedProjects: FSharpProjectSnapshotKey list - // Do we need these? - IsIncompleteTypeCheckEnvironment: bool - UseScriptResolutionRules: bool - } + // Do we need these? + IsIncompleteTypeCheckEnvironment: bool + UseScriptResolutionRules: bool } [] -type FSharpFileSnapshot = { - FileName: string - Version: string - GetSource: unit -> System.Threading.Tasks.Task -} with member Key: FSharpFileKey +type FSharpFileSnapshot = + { FileName: string + Version: string + GetSource: unit -> System.Threading.Tasks.Task } + + member Key: FSharpFileKey [] type FSharpProjectSnapshot = @@ -124,7 +122,6 @@ type FSharpProjectSnapshot = member Key: FSharpProjectSnapshotKey - and [] public FSharpReferencedProjectSnapshot = internal | FSharpReference of projectOutputFile: string * options: FSharpProjectSnapshot @@ -145,9 +142,8 @@ and [] public FSharpReferencedProjectSnapshot = /// /// The fully qualified path to the output of the referenced project. This should be the same value as the -r reference in the project options for this referenced project. /// The Project Options for this F# project - static member CreateFSharp: projectOutputFile: string * options: FSharpProjectSnapshot -> FSharpReferencedProjectSnapshot - - + static member CreateFSharp: + projectOutputFile: string * options: FSharpProjectSnapshot -> FSharpReferencedProjectSnapshot /// A set of information describing a project or script build configuration. type public FSharpProjectOptions = @@ -248,8 +244,8 @@ and [] public FSharpReferencedProject = FSharpReferencedProject type FSharpProjectSnapshot with - member ToOptions: unit -> FSharpProjectOptions + member ToOptions: unit -> FSharpProjectOptions /// Represents the use of an F# symbol from F# source code [] diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index 6d579ebcd12..0977a2fe129 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -247,7 +247,7 @@ type BoundModel private ( syntaxTreeOpt: SyntaxTree option, ?tcStateOpt: GraphNode * GraphNode ) = - + let getTypeCheck (syntaxTree: SyntaxTree) : NodeCode = node { let! input, _sourceRange, fileName, parseErrors = syntaxTree.ParseNode.GetOrComputeValue() diff --git a/src/Compiler/Service/IncrementalBuild.fsi b/src/Compiler/Service/IncrementalBuild.fsi index 1ecd34a4b85..252f2b57a83 100644 --- a/src/Compiler/Service/IncrementalBuild.fsi +++ b/src/Compiler/Service/IncrementalBuild.fsi @@ -23,8 +23,13 @@ open FSharp.Compiler.Text open FSharp.Compiler.TypedTree open FSharp.Compiler.BuildGraph - -type FrameworkImportsCacheKey = FrameworkImportsCacheKey of resolvedpath: string list * assemblyName: string * targetFrameworkDirectories: string list * fsharpBinaries: string * langVersion: decimal +type FrameworkImportsCacheKey = + | FrameworkImportsCacheKey of + resolvedpath: string list * + assemblyName: string * + targetFrameworkDirectories: string list * + fsharpBinaries: string * + langVersion: decimal /// Lookup the global static cache for building the FrameworkTcImports type internal FrameworkImportsCache = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 853632507a8..6a4d503f569 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -31,26 +31,28 @@ open FSharp.Compiler.GraphChecking open FSharp.Compiler.Syntax open FSharp.Compiler.CompilerDiagnostics open FSharp.Compiler.NameResolution - - -type internal FSharpFile = { - Range: range - Source: FSharpFileSnapshot - IsLastCompiland: bool - IsExe: bool -} +open Internal.Utilities.Library.Extras + +type internal FSharpFile = + { + Range: range + Source: FSharpFileSnapshot + IsLastCompiland: bool + IsExe: bool + } /// Things we need to start parsing and checking files for a given project snapshot -type internal BootstrapInfo = { - TcConfig: TcConfig - TcImports: TcImports - TcGlobals: TcGlobals - InitialTcInfo: TcInfo - SourceFiles: FSharpFile list - LoadClosure: LoadClosure option -} +type internal BootstrapInfo = + { + TcConfig: TcConfig + TcImports: TcImports + TcGlobals: TcGlobals + InitialTcInfo: TcInfo + SourceFiles: FSharpFile list + LoadClosure: LoadClosure option + } -//type ParseResults +//type ParseResults type internal TransparentCompiler ( @@ -131,7 +133,12 @@ type internal TransparentCompiler member x.EvaluateRawContents() = node { Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) - return! backgroundCompiler.GetAssemblyData(opts.ToOptions(), userOpName + ".CheckReferencedProject(" + nm + ")") + + return! + backgroundCompiler.GetAssemblyData( + opts.ToOptions(), + userOpName + ".CheckReferencedProject(" + nm + ")" + ) } member x.TryGetLogicalTimeStamp(cache) = @@ -142,89 +149,109 @@ type internal TransparentCompiler } ] - - let ComputeFrameworkImports (tcConfig: TcConfig) frameworkDLLs nonFrameworkResolutions _key = node { - let tcConfigP = TcConfigProvider.Constant tcConfig - return! TcImports.BuildFrameworkTcImports (tcConfigP, frameworkDLLs, nonFrameworkResolutions) - } + let ComputeFrameworkImports (tcConfig: TcConfig) frameworkDLLs nonFrameworkResolutions _key = + node { + let tcConfigP = TcConfigProvider.Constant tcConfig + return! TcImports.BuildFrameworkTcImports(tcConfigP, frameworkDLLs, nonFrameworkResolutions) + } // Link all the assemblies together and produce the input typecheck accumulator - let CombineImportedAssembliesTask ( - assemblyName, - tcConfig: TcConfig, - tcConfigP, - tcGlobals, - frameworkTcImports, - nonFrameworkResolutions, - unresolvedReferences, - dependencyProvider, - loadClosureOpt: LoadClosure option, - basicDependencies -#if !NO_TYPEPROVIDERS - ,importsInvalidatedByTypeProvider: Event -#endif + let CombineImportedAssembliesTask + ( + assemblyName, + tcConfig: TcConfig, + tcConfigP, + tcGlobals, + frameworkTcImports, + nonFrameworkResolutions, + unresolvedReferences, + dependencyProvider, + loadClosureOpt: LoadClosure option, + basicDependencies, + importsInvalidatedByTypeProvider: Event + //#endif ) = - node { - let diagnosticsLogger = CompilationDiagnosticLogger("CombineImportedAssembliesTask", tcConfig.diagnosticsOptions) - use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) - - let! tcImports = - node { - try - let! tcImports = TcImports.BuildNonFrameworkTcImports(tcConfigP, frameworkTcImports, nonFrameworkResolutions, unresolvedReferences, dependencyProvider) -#if !NO_TYPEPROVIDERS - tcImports.GetCcusExcludingBase() |> Seq.iter (fun ccu -> - // When a CCU reports an invalidation, merge them together and just report a - // general "imports invalidated". This triggers a rebuild. - // - // We are explicit about what the handler closure captures to help reason about the - // lifetime of captured objects, especially in case the type provider instance gets leaked - // or keeps itself alive mistakenly, e.g. via some global state in the type provider instance. - // - // The handler only captures - // 1. a weak reference to the importsInvalidated event. - // - // The IncrementalBuilder holds the strong reference the importsInvalidated event. - // - // In the invalidation handler we use a weak reference to allow the IncrementalBuilder to - // be collected if, for some reason, a TP instance is not disposed or not GC'd. - let capturedImportsInvalidated = WeakReference<_>(importsInvalidatedByTypeProvider) - ccu.Deref.InvalidateEvent.Add(fun _ -> - match capturedImportsInvalidated.TryGetTarget() with - | true, tg -> tg.Trigger() - | _ -> ())) -#endif - return tcImports - with exn -> - Debug.Assert(false, sprintf "Could not BuildAllReferencedDllTcImports %A" exn) - diagnosticsLogger.Warning exn - return frameworkTcImports - } - - let tcInitial, openDecls0 = GetInitialTcEnv (assemblyName, rangeStartup, tcConfig, tcImports, tcGlobals) - let tcState = GetInitialTcState (rangeStartup, assemblyName, tcConfig, tcGlobals, tcImports, tcInitial, openDecls0) - let loadClosureErrors = - [ match loadClosureOpt with - | None -> () - | Some loadClosure -> - for inp in loadClosure.Inputs do - yield! inp.MetaCommandDiagnostics ] - - let initialErrors = Array.append (Array.ofList loadClosureErrors) (diagnosticsLogger.GetDiagnostics()) - let tcInfo = - { - tcState=tcState - tcEnvAtEndOfFile=tcInitial - topAttribs=None - latestCcuSigForFile=None - tcDiagnosticsRev = [ initialErrors ] - moduleNamesDict = Map.empty - tcDependencyFiles = basicDependencies - sigNameOpt = None - } - return tcImports, tcInfo - } + node { + let diagnosticsLogger = + CompilationDiagnosticLogger("CombineImportedAssembliesTask", tcConfig.diagnosticsOptions) + + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) + + let! tcImports = + node { + try + let! tcImports = + TcImports.BuildNonFrameworkTcImports( + tcConfigP, + frameworkTcImports, + nonFrameworkResolutions, + unresolvedReferences, + dependencyProvider + ) + //#if !NO_TYPEPROVIDERS + tcImports.GetCcusExcludingBase() + |> Seq.iter (fun ccu -> + // When a CCU reports an invalidation, merge them together and just report a + // general "imports invalidated". This triggers a rebuild. + // + // We are explicit about what the handler closure captures to help reason about the + // lifetime of captured objects, especially in case the type provider instance gets leaked + // or keeps itself alive mistakenly, e.g. via some global state in the type provider instance. + // + // The handler only captures + // 1. a weak reference to the importsInvalidated event. + // + // The IncrementalBuilder holds the strong reference the importsInvalidated event. + // + // In the invalidation handler we use a weak reference to allow the IncrementalBuilder to + // be collected if, for some reason, a TP instance is not disposed or not GC'd. + let capturedImportsInvalidated = WeakReference<_>(importsInvalidatedByTypeProvider) + + ccu.Deref.InvalidateEvent.Add(fun _ -> + match capturedImportsInvalidated.TryGetTarget() with + | true, tg -> tg.Trigger() + | _ -> ())) + //#endif + return tcImports + with exn -> + Debug.Assert(false, sprintf "Could not BuildAllReferencedDllTcImports %A" exn) + diagnosticsLogger.Warning exn + return frameworkTcImports + } + + let tcInitial, openDecls0 = + GetInitialTcEnv(assemblyName, rangeStartup, tcConfig, tcImports, tcGlobals) + + let tcState = + GetInitialTcState(rangeStartup, assemblyName, tcConfig, tcGlobals, tcImports, tcInitial, openDecls0) + + let loadClosureErrors = + [ + match loadClosureOpt with + | None -> () + | Some loadClosure -> + for inp in loadClosure.Inputs do + yield! inp.MetaCommandDiagnostics + ] + + let initialErrors = + Array.append (Array.ofList loadClosureErrors) (diagnosticsLogger.GetDiagnostics()) + + let tcInfo = + { + tcState = tcState + tcEnvAtEndOfFile = tcInitial + topAttribs = None + latestCcuSigForFile = None + tcDiagnosticsRev = [ initialErrors ] + moduleNamesDict = Map.empty + tcDependencyFiles = basicDependencies + sigNameOpt = None + } + + return tcImports, tcInfo + } let computeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = node { @@ -241,8 +268,8 @@ type internal TransparentCompiler let tcConfigB, sourceFiles = let getSwitchValue (switchString: string) = - match commandLineArgs |> List.tryFindIndex(fun s -> s.StartsWithOrdinal switchString) with - | Some idx -> Some(commandLineArgs[idx].Substring(switchString.Length)) + match commandLineArgs |> List.tryFindIndex (fun s -> s.StartsWithOrdinal switchString) with + | Some idx -> Some(commandLineArgs[ idx ].Substring(switchString.Length)) | _ -> None let sdkDirOverride = @@ -252,16 +279,18 @@ type internal TransparentCompiler // see also fsc.fs: runFromCommandLineToImportingAssemblies(), as there are many similarities to where the PS creates a tcConfigB let tcConfigB = - TcConfigBuilder.CreateNew(legacyReferenceResolver, - defaultFSharpBinariesDir, - implicitIncludeDir=projectSnapshot.ProjectDirectory, - reduceMemoryUsage=ReduceMemoryFlag.Yes, - isInteractive=useScriptResolutionRules, - isInvalidationSupported=true, - defaultCopyFSharpCore=CopyFSharpCoreFlag.No, - tryGetMetadataSnapshot=tryGetMetadataSnapshot, - sdkDirOverride=sdkDirOverride, - rangeForErrors=range0) + TcConfigBuilder.CreateNew( + legacyReferenceResolver, + defaultFSharpBinariesDir, + implicitIncludeDir = projectSnapshot.ProjectDirectory, + reduceMemoryUsage = ReduceMemoryFlag.Yes, + isInteractive = useScriptResolutionRules, + isInvalidationSupported = true, + defaultCopyFSharpCore = CopyFSharpCoreFlag.No, + tryGetMetadataSnapshot = tryGetMetadataSnapshot, + sdkDirOverride = sdkDirOverride, + rangeForErrors = range0 + ) tcConfigB.primaryAssembly <- match loadClosureOpt with @@ -275,7 +304,12 @@ type internal TransparentCompiler tcConfigB.resolutionEnvironment <- (LegacyResolutionEnvironment.EditingOrCompilation true) tcConfigB.conditionalDefines <- - let define = if useScriptResolutionRules then "INTERACTIVE" else "COMPILED" + let define = + if useScriptResolutionRules then + "INTERACTIVE" + else + "COMPILED" + define :: tcConfigB.conditionalDefines tcConfigB.projectReferences <- projectReferences @@ -283,7 +317,8 @@ type internal TransparentCompiler tcConfigB.useSimpleResolution <- (getSwitchValue useSimpleResolutionSwitch) |> Option.isSome // Apply command-line arguments and collect more source files if they are in the arguments - let sourceFilesNew = ApplyCommandLineArgs(tcConfigB, projectSnapshot.SourceFileNames, commandLineArgs) + let sourceFilesNew = + ApplyCommandLineArgs(tcConfigB, projectSnapshot.SourceFileNames, commandLineArgs) // Never open PDB files for the language service, even if --standalone is specified tcConfigB.openDebugInformationForLaterStaticLinking <- false @@ -312,35 +347,48 @@ type internal TransparentCompiler match loadClosureOpt with | Some loadClosure -> let dllReferences = - [for reference in tcConfigB.referencedDLLs do - // If there's (one or more) resolutions of closure references then yield them all - match loadClosure.References |> List.tryFind (fun (resolved, _)->resolved=reference.Text) with - | Some (resolved, closureReferences) -> - for closureReference in closureReferences do - yield AssemblyReference(closureReference.originalReference.Range, resolved, None) - | None -> yield reference] + [ + for reference in tcConfigB.referencedDLLs do + // If there's (one or more) resolutions of closure references then yield them all + match + loadClosure.References + |> List.tryFind (fun (resolved, _) -> resolved = reference.Text) + with + | Some (resolved, closureReferences) -> + for closureReference in closureReferences do + yield AssemblyReference(closureReference.originalReference.Range, resolved, None) + | None -> yield reference + ] + tcConfigB.referencedDLLs <- [] - tcConfigB.primaryAssembly <- (if loadClosure.UseDesktopFramework then PrimaryAssembly.Mscorlib else PrimaryAssembly.System_Runtime) + + tcConfigB.primaryAssembly <- + (if loadClosure.UseDesktopFramework then + PrimaryAssembly.Mscorlib + else + PrimaryAssembly.System_Runtime) // Add one by one to remove duplicates - dllReferences |> List.iter (fun dllReference -> - tcConfigB.AddReferencedAssemblyByPath(dllReference.Range, dllReference.Text)) + dllReferences + |> List.iter (fun dllReference -> tcConfigB.AddReferencedAssemblyByPath(dllReference.Range, dllReference.Text)) + tcConfigB.knownUnresolvedReferences <- loadClosure.UnresolvedReferences | None -> () - setupConfigFromLoadClosure() + setupConfigFromLoadClosure () - let tcConfig = TcConfig.Create(tcConfigB, validate=true) + let tcConfig = TcConfig.Create(tcConfigB, validate = true) let _outfile, _, assemblyName = tcConfigB.DecideNames sourceFiles // Resolve assemblies and create the framework TcImports. This caches a level of "system" references. No type providers are // included in these references. - let frameworkDLLs, nonFrameworkResolutions, unresolvedReferences = TcAssemblyResolutions.SplitNonFoundationalResolutions(tcConfig) + let frameworkDLLs, nonFrameworkResolutions, unresolvedReferences = + TcAssemblyResolutions.SplitNonFoundationalResolutions(tcConfig) let frameworkDLLsKey = frameworkDLLs - |> List.map (fun ar->ar.resolvedPath) // The cache key. Just the minimal data. - |> List.sort // Sort to promote cache hits. + |> List.map (fun ar -> ar.resolvedPath) // The cache key. Just the minimal data. + |> List.sort // Sort to promote cache hits. // Prepare the frameworkTcImportsCache // @@ -348,17 +396,22 @@ type internal TransparentCompiler // the import of a set of framework DLLs into F# CCUs. That is, the F# CCUs that result from a set of DLLs (including // FSharp.Core.dll and mscorlib.dll) must be logically invariant of all the other compiler configuration parameters. let key = - FrameworkImportsCacheKey(frameworkDLLsKey, - tcConfig.primaryAssembly.Name, - tcConfig.GetTargetFrameworkDirectories(), - tcConfig.fsharpBinariesDir, - tcConfig.langVersion.SpecifiedVersion) + FrameworkImportsCacheKey( + frameworkDLLsKey, + tcConfig.primaryAssembly.Name, + tcConfig.GetTargetFrameworkDirectories(), + tcConfig.fsharpBinariesDir, + tcConfig.langVersion.SpecifiedVersion + ) - let! tcGlobals, frameworkTcImports = FrameworkImportsCache.Get(key, ComputeFrameworkImports tcConfig frameworkDLLs nonFrameworkResolutions) + let! tcGlobals, frameworkTcImports = + FrameworkImportsCache.Get(key, ComputeFrameworkImports tcConfig frameworkDLLs nonFrameworkResolutions) // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. // This is ok because not much can actually go wrong here. - let diagnosticsLogger = CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) + let diagnosticsLogger = + CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) // TODO: might need to put something like this somewhere @@ -382,12 +435,14 @@ type internal TransparentCompiler let tcConfigP = TcConfigProvider.Constant tcConfig - #if !NO_TYPEPROVIDERS + //#if !NO_TYPEPROVIDERS let importsInvalidatedByTypeProvider = Event() - #endif + //#endif // Check for the existence of loaded sources and prepend them to the sources list if present. - let sourceFiles = tcConfig.GetAvailableLoadedSources() @ (sourceFiles |> List.map (fun s -> rangeStartup, s)) + let sourceFiles = + tcConfig.GetAvailableLoadedSources() + @ (sourceFiles |> List.map (fun s -> rangeStartup, s)) // Mark up the source files with an indicator flag indicating if they are the last source file in the project let sourceFiles = @@ -395,23 +450,31 @@ type internal TransparentCompiler ((sourceFiles, flags) ||> List.map2 (fun (m, nm) flag -> (m, nm, (flag, isExe)))) let basicDependencies = - [ for UnresolvedAssemblyReference(referenceText, _) in unresolvedReferences do - // Exclude things that are definitely not a file name - if not(FileSystem.IsInvalidPathShim referenceText) then - let file = if FileSystem.IsPathRootedShim referenceText then referenceText else Path.Combine(projectSnapshot.ProjectDirectory, referenceText) - yield file - - for r in nonFrameworkResolutions do - yield r.resolvedPath ] + [ + for UnresolvedAssemblyReference (referenceText, _) in unresolvedReferences do + // Exclude things that are definitely not a file name + if not (FileSystem.IsInvalidPathShim referenceText) then + let file = + if FileSystem.IsPathRootedShim referenceText then + referenceText + else + Path.Combine(projectSnapshot.ProjectDirectory, referenceText) + + yield file + + for r in nonFrameworkResolutions do + yield r.resolvedPath + ] // For scripts, the dependency provider is already available. // For projects create a fresh one for the project. let dependencyProvider = - if projectSnapshot.UseScriptResolutionRules - then dependencyProviderForScripts - else new DependencyProvider() + if projectSnapshot.UseScriptResolutionRules then + dependencyProviderForScripts + else + new DependencyProvider() - let! tcImports, initialTcInfo = + let! tcImports, initialTcInfo = CombineImportedAssembliesTask( assemblyName, tcConfig, @@ -422,10 +485,10 @@ type internal TransparentCompiler unresolvedReferences, dependencyProvider, loadClosureOpt, - basicDependencies - #if !NO_TYPEPROVIDERS - ,importsInvalidatedByTypeProvider - #endif + basicDependencies, + //#if !NO_TYPEPROVIDERS + importsInvalidatedByTypeProvider + //#endif ) let fileSnapshots = Map [ for f in projectSnapshot.SourceFiles -> f.FileName, f ] @@ -442,16 +505,24 @@ type internal TransparentCompiler Version = (FileSystem.GetLastWriteTimeShim fileName).Ticks.ToString() GetSource = (fun () -> fileName |> File.ReadAllText |> SourceText.ofString |> Task.FromResult) }) - { Range = m; Source = source; IsLastCompiland = isLastCompiland; IsExe = isExe }) - - return Some { - TcConfig = tcConfig - TcImports = tcImports - TcGlobals = tcGlobals - InitialTcInfo = initialTcInfo - SourceFiles = sourceFiles - LoadClosure = loadClosureOpt - } + + { + Range = m + Source = source + IsLastCompiland = isLastCompiland + IsExe = isExe + }) + + return + Some + { + TcConfig = tcConfig + TcImports = tcImports + TcGlobals = tcGlobals + InitialTcInfo = initialTcInfo + SourceFiles = sourceFiles + LoadClosure = loadClosureOpt + } } let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) _key = @@ -474,44 +545,53 @@ type internal TransparentCompiler match bootstrapInfoOpt with | Some bootstrapInfo -> let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions - let diagnosticsLogger = CompilationDiagnosticLogger("IncrementalBuilderCreation", diagnosticsOptions) + + let diagnosticsLogger = + CompilationDiagnosticLogger("IncrementalBuilderCreation", diagnosticsOptions) + delayedLogger.CommitDelayedDiagnostics diagnosticsLogger diagnosticsLogger.GetDiagnostics() - | _ -> - Array.ofList delayedLogger.Diagnostics + | _ -> Array.ofList delayedLogger.Diagnostics |> Array.map (fun (diagnostic, severity) -> FSharpDiagnostic.CreateFromException(diagnostic, severity, range.Zero, suggestNamesForErrors)) + return bootstrapInfoOpt, diagnostics } - let ComputeParseFile' (file: FSharpFile) (projectSnapshot: FSharpProjectSnapshot) bootstrapInfo userOpName _key = node { + let ComputeParseFile' (file: FSharpFile) (projectSnapshot: FSharpProjectSnapshot) bootstrapInfo userOpName _key = + node { - let parsingOptions = - FSharpParsingOptions.FromTcConfig( - bootstrapInfo.TcConfig, - projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName) |> Array.ofSeq, - projectSnapshot.UseScriptResolutionRules - ) + let parsingOptions = + FSharpParsingOptions.FromTcConfig( + bootstrapInfo.TcConfig, + projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName) |> Array.ofSeq, + projectSnapshot.UseScriptResolutionRules + ) - // TODO: what is this? - // GraphNode.SetPreferredUILang tcPrior.TcConfig.preferredUiLang + // TODO: what is this? + // GraphNode.SetPreferredUILang tcPrior.TcConfig.preferredUiLang - let! sourceText = file.Source.GetSource() |> NodeCode.AwaitTask - let diagnostics, parsedInput, anyErrors = ParseAndCheckFile.parseFile( - sourceText, - file.Source.FileName, - parsingOptions, - userOpName, - suggestNamesForErrors, - captureIdentifiersWhenParsing - ) - return diagnostics, parsedInput, anyErrors, sourceText - } + let! sourceText = file.Source.GetSource() |> NodeCode.AwaitTask + + let diagnostics, parsedInput, anyErrors = + ParseAndCheckFile.parseFile ( + sourceText, + file.Source.FileName, + parsingOptions, + userOpName, + suggestNamesForErrors, + captureIdentifiersWhenParsing + ) + + return diagnostics, parsedInput, anyErrors, sourceText + } let ComputeParseFile (file: FSharpFile) bootstrapInfo _key = node { let tcConfig = bootstrapInfo.TcConfig - let diagnosticsLogger = CompilationDiagnosticLogger("Parse", tcConfig.diagnosticsOptions) + + let diagnosticsLogger = + CompilationDiagnosticLogger("Parse", tcConfig.diagnosticsOptions) // Return the disposable object that cleans up use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parse) @@ -519,7 +599,8 @@ type internal TransparentCompiler let fileName = file.Source.FileName let! sourceText = file.Source.GetSource() |> NodeCode.AwaitTask - let input = ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) + let input = + ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) return input, diagnosticsLogger.GetDiagnostics() } @@ -555,30 +636,51 @@ type internal TransparentCompiler let tcImports = bootstrapInfo.TcImports let capturingDiagnosticsLogger = CapturingDiagnosticsLogger("TypeCheck") - let diagnosticsLogger = GetDiagnosticsLoggerFilteringByScopedPragmas(false, input.ScopedPragmas, tcConfig.diagnosticsOptions, capturingDiagnosticsLogger) + + let diagnosticsLogger = + GetDiagnosticsLoggerFilteringByScopedPragmas( + false, + input.ScopedPragmas, + tcConfig.diagnosticsOptions, + capturingDiagnosticsLogger + ) + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.TypeCheck) //beforeFileChecked.Trigger fileName - ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName fileName, tcImports.DependencyProvider) |> ignore + ApplyMetaCommandsFromInputToTcConfig(tcConfig, input, Path.GetDirectoryName fileName, tcImports.DependencyProvider) + |> ignore + let sink = TcResultsSinkImpl(tcGlobals) let hadParseErrors = not (Array.isEmpty parseErrors) - let input, moduleNamesDict = DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input + + let input, moduleNamesDict = + DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input let! (tcEnvAtEndOfFile, topAttribs, implFile, ccuSigForFile), tcState = - CheckOneInput ( - (fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), - tcConfig, tcImports, - tcGlobals, - None, - TcResultsSink.WithSink sink, - prevTcInfo.tcState, input ) + CheckOneInput( + (fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), + tcConfig, + tcImports, + tcGlobals, + None, + TcResultsSink.WithSink sink, + prevTcInfo.tcState, + input + ) |> NodeCode.FromCancellable //fileChecked.Trigger fileName - let newErrors = Array.append parseErrors (capturingDiagnosticsLogger.Diagnostics |> List.toArray) - let tcEnvAtEndOfFile = if keepAllBackgroundResolutions then tcEnvAtEndOfFile else tcState.TcEnvFromImpls + let newErrors = + Array.append parseErrors (capturingDiagnosticsLogger.Diagnostics |> List.toArray) + + let tcEnvAtEndOfFile = + if keepAllBackgroundResolutions then + tcEnvAtEndOfFile + else + tcState.TcEnvFromImpls let tcInfo = { @@ -586,28 +688,47 @@ type internal TransparentCompiler tcEnvAtEndOfFile = tcEnvAtEndOfFile moduleNamesDict = moduleNamesDict latestCcuSigForFile = Some ccuSigForFile - tcDiagnosticsRev = [newErrors] + tcDiagnosticsRev = [ newErrors ] topAttribs = Some topAttribs - tcDependencyFiles = [fileName] + tcDependencyFiles = [ fileName ] sigNameOpt = match input with - | ParsedInput.SigFile sigFile -> - Some(sigFile.FileName, sigFile.QualifiedName) - | _ -> - None + | ParsedInput.SigFile sigFile -> Some(sigFile.FileName, sigFile.QualifiedName) + | _ -> None } + return tcInfo, sink, implFile, fileName } + let mergeTcInfos = + Array.fold (fun a b -> + { a with + tcState = b.tcState + tcEnvAtEndOfFile = b.tcEnvAtEndOfFile + moduleNamesDict = b.moduleNamesDict + latestCcuSigForFile = b.latestCcuSigForFile + tcDiagnosticsRev = b.tcDiagnosticsRev @ a.tcDiagnosticsRev + topAttribs = b.topAttribs + tcDependencyFiles = b.tcDependencyFiles + sigNameOpt = b.sigNameOpt + }) + // Type check everything that is needed to check given file - let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) userOpName _key: NodeCode = + let ComputeTcPrior + (file: FSharpFile) + (bootstrapInfo: BootstrapInfo) + (projectSnapshot: FSharpProjectSnapshot) + _userOpName + _key + : NodeCode = node { // parse required files - let files = seq { - yield! bootstrapInfo.SourceFiles |> Seq.takeWhile ((<>) file) - file - } + let files = + seq { + yield! bootstrapInfo.SourceFiles |> Seq.takeWhile ((<>) file) + file + } let! parsedInputs = files @@ -617,26 +738,36 @@ type internal TransparentCompiler |> NodeCode.Parallel // compute dependency graph - let graphKey = projectSnapshot.UpTo(file.Source.FileName).SourceFiles |> List.map (fun s -> s.Key) - let! graph, _filePairs = DependencyGraphForLastFileCache.Get(graphKey, ComputeDependencyGraphForLastFile (parsedInputs |> Seq.map fst) bootstrapInfo.TcConfig) + let graphKey = + projectSnapshot.UpTo(file.Source.FileName).SourceFiles + |> List.map (fun s -> s.Key) + + let! graph, _filePairs = + DependencyGraphForLastFileCache.Get( + graphKey, + ComputeDependencyGraphForLastFile (parsedInputs |> Seq.map fst) bootstrapInfo.TcConfig + ) // layers that can be processed in parallel - let layers = Graph.leafSequence graph - - let rec processLayer (layers: Set list) state = node { - match layers with - | [] -> return state - | layer::rest -> - let! results = - layer - |> Seq.map (fun fileIndex -> - let key = projectSnapshot.UpTo(fileIndex).Key - TcIntermediateCache.Get(key, ComputeTcIntermediate parsedInputs[fileIndex] bootstrapInfo state)) - |> NodeCode.Parallel - return! processLayer rest (combineResults state results) - } - - return bootstrapInfo.InitialTcInfo + let layers = Graph.leafSequence graph |> Seq.toList + + let rec processLayer (layers: Set list) state = + node { + match layers with + | [] -> return state + | layer :: rest -> + let! results = + layer + |> Seq.map (fun fileIndex -> + let key = projectSnapshot.UpTo(fileIndex).Key + TcIntermediateCache.Get(key, ComputeTcIntermediate parsedInputs[fileIndex] bootstrapInfo state)) + |> NodeCode.Parallel + + return! processLayer rest (mergeTcInfos state (results |> Array.map p14)) + } + + let! tcInfo = processLayer layers bootstrapInfo.InitialTcInfo + return tcInfo } let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) userOpName _key = @@ -652,12 +783,14 @@ type internal TransparentCompiler | Some bootstrapInfo -> - let file = bootstrapInfo.SourceFiles |> List.find (fun f -> f.Source.FileName = fileName) + let file = + bootstrapInfo.SourceFiles |> List.find (fun f -> f.Source.FileName = fileName) let priorSnapshot = projectSnapshot.UpTo fileName let! tcInfo = TcPriorCache.Get(priorSnapshot.Key, ComputeTcPrior file bootstrapInfo priorSnapshot userOpName) - let! parseTree, parseDiagnostics = ParseFileCache.Get((file.Source.Key, file.IsLastCompiland, file.IsExe), ComputeParseFile file bootstrapInfo) + let! parseTree, parseDiagnostics = + ParseFileCache.Get((file.Source.Key, file.IsLastCompiland, file.IsExe), ComputeParseFile file bootstrapInfo) let parseDiagnostics = DiagnosticHelpers.CreateDiagnostics( @@ -681,6 +814,7 @@ type internal TransparentCompiler // TODO: this might be replaced... probably should use intermediate TC result let sourceText = Unchecked.defaultof<_> + let! checkResults = FSharpCheckFileResults.CheckOneFile( parseResults, @@ -708,17 +842,12 @@ type internal TransparentCompiler return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) } - member _.ParseAndCheckFileInProject - ( - fileName: string, - projectSnapshot: FSharpProjectSnapshot, - userOpName: string - ) = node { + member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = + node { let key = fileName, projectSnapshot.Key return! ParseAndCheckFileInProjectCache.Get(key, ComputeParseAndCheckFileInProject fileName projectSnapshot userOpName) } - interface IBackgroundCompiler with member this.BeforeBackgroundFileCheck: IEvent = backgroundCompiler.BeforeBackgroundFileCheck @@ -743,13 +872,26 @@ type internal TransparentCompiler options: FSharpProjectOptions, userOpName: string ) : NodeCode = - backgroundCompiler.CheckFileInProjectAllowingStaleCachedResults(parseResults, fileName, fileVersion, sourceText, options, userOpName) + backgroundCompiler.CheckFileInProjectAllowingStaleCachedResults( + parseResults, + fileName, + fileVersion, + sourceText, + options, + userOpName + ) + + member _.ClearCache(options: seq, userOpName: string) : unit = + backgroundCompiler.ClearCache(options, userOpName) - member _.ClearCache(options: seq, userOpName: string) : unit = backgroundCompiler.ClearCache(options, userOpName) member _.ClearCaches() : unit = backgroundCompiler.ClearCaches() member _.DownsizeCaches() : unit = backgroundCompiler.DownsizeCaches() - member _.FileChecked: IEvent = backgroundCompiler.FileChecked - member _.FileParsed: IEvent = backgroundCompiler.FileParsed + + member _.FileChecked: IEvent = + backgroundCompiler.FileChecked + + member _.FileParsed: IEvent = + backgroundCompiler.FileParsed member _.FindReferencesInFile ( @@ -761,7 +903,8 @@ type internal TransparentCompiler ) : NodeCode> = backgroundCompiler.FindReferencesInFile(fileName, options, symbol, canInvalidateProject, userOpName) - member _.FrameworkImportsCache: FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache + member _.FrameworkImportsCache: FrameworkImportsCache = + backgroundCompiler.FrameworkImportsCache member _.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = backgroundCompiler.GetAssemblyData(options, userOpName) @@ -860,7 +1003,8 @@ type internal TransparentCompiler ) : Async = backgroundCompiler.ParseFile(fileName, sourceText, options, cache, userOpName) - member _.ProjectChecked: IEvent = backgroundCompiler.ProjectChecked + member _.ProjectChecked: IEvent = + backgroundCompiler.ProjectChecked member _.TryGetRecentCheckResultsForFile ( diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 22d9e69da7d..0dd0083f706 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -149,7 +149,9 @@ type FSharpChecker captureIdentifiersWhenParsing, getSource, useChangeNotifications, - useSyntaxTreeCache) :> IBackgroundCompiler + useSyntaxTreeCache + ) + :> IBackgroundCompiler else BackgroundCompiler( legacyReferenceResolver, @@ -165,7 +167,9 @@ type FSharpChecker captureIdentifiersWhenParsing, getSource, useChangeNotifications, - useSyntaxTreeCache) :> IBackgroundCompiler + useSyntaxTreeCache + ) + :> IBackgroundCompiler static let globalInstance = lazy FSharpChecker.Create() From e8ea40cd7ffcd9268e8893ff1bb12ff68baa81b1 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 28 Apr 2023 15:34:43 +0200 Subject: [PATCH 010/222] wip --- src/Compiler/Service/BackgroundCompiler.fs | 13 +++ src/Compiler/Service/FSharpCheckerResults.fs | 1 + src/Compiler/Service/FSharpCheckerResults.fsi | 3 +- src/Compiler/Service/IncrementalBuild.fs | 4 +- src/Compiler/Service/TransparentCompiler.fs | 65 +++++++------- src/Compiler/Service/service.fs | 6 ++ src/Compiler/Service/service.fsi | 4 + .../LanguageService/LanguageService.fs | 5 +- .../LanguageService/WorkspaceExtensions.fs | 85 ++++++++++++++++--- .../FSharp.Editor/Options/EditorOptions.fs | 5 ++ .../AdvancedOptionsControl.xaml | 4 + .../FSharp.UIResources/Strings.Designer.cs | 18 ++++ .../src/FSharp.UIResources/Strings.resx | 6 ++ .../src/FSharp.UIResources/xlf/Strings.cs.xlf | 10 +++ .../src/FSharp.UIResources/xlf/Strings.de.xlf | 10 +++ .../src/FSharp.UIResources/xlf/Strings.es.xlf | 10 +++ .../src/FSharp.UIResources/xlf/Strings.fr.xlf | 10 +++ .../src/FSharp.UIResources/xlf/Strings.it.xlf | 10 +++ .../src/FSharp.UIResources/xlf/Strings.ja.xlf | 10 +++ .../src/FSharp.UIResources/xlf/Strings.ko.xlf | 10 +++ .../src/FSharp.UIResources/xlf/Strings.pl.xlf | 10 +++ .../FSharp.UIResources/xlf/Strings.pt-BR.xlf | 10 +++ .../src/FSharp.UIResources/xlf/Strings.ru.xlf | 10 +++ .../src/FSharp.UIResources/xlf/Strings.tr.xlf | 10 +++ .../xlf/Strings.zh-Hans.xlf | 10 +++ .../xlf/Strings.zh-Hant.xlf | 10 +++ 26 files changed, 298 insertions(+), 51 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 36758b75afc..ddf9d3f0c04 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -124,6 +124,10 @@ type internal IBackgroundCompiler = fileName: string * fileVersion: int * sourceText: ISourceText * options: FSharpProjectOptions * userOpName: string -> NodeCode + abstract member ParseAndCheckFileInProject: + fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> + NodeCode + /// Parse and typecheck the whole project. abstract member ParseAndCheckProject: options: FSharpProjectOptions * userOpName: string -> NodeCode @@ -1373,6 +1377,7 @@ type internal BackgroundCompiler static member ActualCheckFileCount = actualCheckFileCount interface IBackgroundCompiler with + member _.BeforeBackgroundFileCheck = self.BeforeBackgroundFileCheck member _.CheckFileInProject @@ -1498,6 +1503,14 @@ type internal BackgroundCompiler ) : NodeCode = self.ParseAndCheckFileInProject(fileName, fileVersion, sourceText, options, userOpName) + member this.ParseAndCheckFileInProject + ( + _fileName: string, + _projectSnapshot: FSharpProjectSnapshot, + _userOpName: string + ) : NodeCode = + raise (System.NotImplementedException()) + member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = self.ParseAndCheckProject(options, userOpName) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index a5767cd3564..5b2dbb3cd54 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -117,6 +117,7 @@ type internal DelayedILModuleReader = type FSharpFileKey = string * string +// TODO: use stamp if we have it? type FSharpProjectSnapshotKey = { ProjectFileName: string diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 04e6667ca06..20ecde853ea 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -5,6 +5,7 @@ namespace FSharp.Compiler.CodeAnalysis open System open System.IO open System.Threading +open System.Threading.Tasks open Internal.Utilities.Library open FSharp.Compiler.AbstractIL.IL open FSharp.Compiler.AbstractIL.ILBinaryReader @@ -55,7 +56,7 @@ type FSharpProjectSnapshotKey = type FSharpFileSnapshot = { FileName: string Version: string - GetSource: unit -> System.Threading.Tasks.Task } + GetSource: unit -> Task } member Key: FSharpFileKey diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index 3178af6868b..8d48ec4503b 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -250,7 +250,7 @@ type BoundModel private ( syntaxTreeOpt: SyntaxTree option, ?tcStateOpt: GraphNode * GraphNode ) = - + let getTypeCheck (syntaxTree: SyntaxTree) : NodeCode = node { let! input, _sourceRange, fileName, parseErrors = syntaxTree.ParseNode.GetOrComputeValue() @@ -262,7 +262,7 @@ type BoundModel private ( use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.TypeCheck) beforeFileChecked.Trigger fileName - + ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName fileName, tcImports.DependencyProvider) |> ignore let sink = TcResultsSinkImpl(tcGlobals) let hadParseErrors = not (Array.isEmpty parseErrors) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index dd24c89ed3d..81a2406d811 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -52,8 +52,6 @@ type internal BootstrapInfo = LoadClosure: LoadClosure option } -//type ParseResults - type internal TransparentCompiler ( legacyReferenceResolver, @@ -168,8 +166,9 @@ type internal TransparentCompiler dependencyProvider, loadClosureOpt: LoadClosure option, basicDependencies, +#if !NO_TYPEPROVIDERS importsInvalidatedByTypeProvider: Event - //#endif +#endif ) = node { @@ -189,7 +188,7 @@ type internal TransparentCompiler unresolvedReferences, dependencyProvider ) - //#if !NO_TYPEPROVIDERS +#if !NO_TYPEPROVIDERS tcImports.GetCcusExcludingBase() |> Seq.iter (fun ccu -> // When a CCU reports an invalidation, merge them together and just report a @@ -212,7 +211,7 @@ type internal TransparentCompiler match capturedImportsInvalidated.TryGetTarget() with | true, tg -> tg.Trigger() | _ -> ())) - //#endif +#endif return tcImports with exn -> Debug.Assert(false, sprintf "Could not BuildAllReferencedDllTcImports %A" exn) @@ -435,9 +434,9 @@ type internal TransparentCompiler let tcConfigP = TcConfigProvider.Constant tcConfig - //#if !NO_TYPEPROVIDERS +#if !NO_TYPEPROVIDERS let importsInvalidatedByTypeProvider = Event() - //#endif +#endif // Check for the existence of loaded sources and prepend them to the sources list if present. let sourceFiles = @@ -486,9 +485,9 @@ type internal TransparentCompiler dependencyProvider, loadClosureOpt, basicDependencies, - //#if !NO_TYPEPROVIDERS +#if !NO_TYPEPROVIDERS importsInvalidatedByTypeProvider - //#endif +#endif ) let fileSnapshots = Map [ for f in projectSnapshot.SourceFiles -> f.FileName, f ] @@ -602,7 +601,7 @@ type internal TransparentCompiler let input = ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) - return input, diagnosticsLogger.GetDiagnostics() + return input, diagnosticsLogger.GetDiagnostics(), sourceText } let ComputeDependencyGraphForLastFile parsedInputs (tcConfig: TcConfig) _key = @@ -691,10 +690,8 @@ type internal TransparentCompiler tcDiagnosticsRev = [ newErrors ] topAttribs = Some topAttribs tcDependencyFiles = [ fileName ] - sigNameOpt = - match input with - | ParsedInput.SigFile sigFile -> Some(sigFile.FileName, sigFile.QualifiedName) - | _ -> None + // we shouldn't need this with graph checking (?) + sigNameOpt = None } return tcInfo, sink, implFile, fileName @@ -709,18 +706,13 @@ type internal TransparentCompiler latestCcuSigForFile = b.latestCcuSigForFile tcDiagnosticsRev = b.tcDiagnosticsRev @ a.tcDiagnosticsRev topAttribs = b.topAttribs - tcDependencyFiles = b.tcDependencyFiles - sigNameOpt = b.sigNameOpt + tcDependencyFiles = b.tcDependencyFiles @ a.tcDependencyFiles + // we shouldn't need this with graph checking (?) + sigNameOpt = None }) // Type check everything that is needed to check given file - let ComputeTcPrior - (file: FSharpFile) - (bootstrapInfo: BootstrapInfo) - (projectSnapshot: FSharpProjectSnapshot) - _userOpName - _key - : NodeCode = + let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) _userOpName _key = node { // parse required files @@ -745,29 +737,32 @@ type internal TransparentCompiler let! graph, _filePairs = DependencyGraphForLastFileCache.Get( graphKey, - ComputeDependencyGraphForLastFile (parsedInputs |> Seq.map fst) bootstrapInfo.TcConfig + ComputeDependencyGraphForLastFile (parsedInputs |> Seq.map p13) bootstrapInfo.TcConfig ) // layers that can be processed in parallel let layers = Graph.leafSequence graph |> Seq.toList - let rec processLayer (layers: Set list) state = + // remove the final layer, which should be the target file + let layers = layers |> List.take (layers.Length - 1) + + let rec processLayer (layers: Set list) tcInfo = node { match layers with - | [] -> return state + | [] -> return tcInfo | layer :: rest -> let! results = layer |> Seq.map (fun fileIndex -> let key = projectSnapshot.UpTo(fileIndex).Key - TcIntermediateCache.Get(key, ComputeTcIntermediate parsedInputs[fileIndex] bootstrapInfo state)) + let parsedInput, parseErrors, _ = parsedInputs[fileIndex] + TcIntermediateCache.Get(key, ComputeTcIntermediate (parsedInput, parseErrors) bootstrapInfo tcInfo)) |> NodeCode.Parallel - return! processLayer rest (mergeTcInfos state (results |> Array.map p14)) + return! processLayer rest (mergeTcInfos tcInfo (results |> Array.map p14)) } - let! tcInfo = processLayer layers bootstrapInfo.InitialTcInfo - return tcInfo + return! processLayer layers bootstrapInfo.InitialTcInfo } let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) userOpName _key = @@ -789,7 +784,8 @@ type internal TransparentCompiler let priorSnapshot = projectSnapshot.UpTo fileName let! tcInfo = TcPriorCache.Get(priorSnapshot.Key, ComputeTcPrior file bootstrapInfo priorSnapshot userOpName) - let! parseTree, parseDiagnostics = + // We could also bubble this through ComputeTcPrior + let! parseTree, parseDiagnostics, sourceText = ParseFileCache.Get((file.Source.Key, file.IsLastCompiland, file.IsExe), ComputeParseFile file bootstrapInfo) let parseDiagnostics = @@ -812,9 +808,6 @@ type internal TransparentCompiler dependencyFiles = [||] ) - // TODO: this might be replaced... probably should use intermediate TC result - let sourceText = Unchecked.defaultof<_> - let! checkResults = FSharpCheckFileResults.CheckOneFile( parseResults, @@ -849,6 +842,7 @@ type internal TransparentCompiler } interface IBackgroundCompiler with + member this.BeforeBackgroundFileCheck: IEvent = backgroundCompiler.BeforeBackgroundFileCheck @@ -990,6 +984,9 @@ type internal TransparentCompiler backgroundCompiler.ParseAndCheckFileInProject(fileName, fileVersion, sourceText, options, userOpName) + member this.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = + this.ParseAndCheckFileInProject(fileName, projectSnapshot, userOpName) + member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = backgroundCompiler.ParseAndCheckProject(options, userOpName) diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index fdbf6a1df36..69b6f7186dc 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -431,6 +431,12 @@ type FSharpChecker backgroundCompiler.ParseAndCheckFileInProject(fileName, fileVersion, sourceText, options, userOpName) |> Async.AwaitNodeCode + member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + + backgroundCompiler.ParseAndCheckFileInProject(fileName, projectSnapshot, userOpName) + |> Async.AwaitNodeCode + member _.ParseAndCheckProject(options, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index 9a7472c0841..45522fa3126 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -193,6 +193,10 @@ type public FSharpChecker = ?userOpName: string -> Async + member ParseAndCheckFileInProject: + fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> + Async + /// /// Parse and typecheck all files in a project. /// All files are read from the FileSystem API diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 6b8fd616027..a9680b20653 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -143,6 +143,8 @@ type internal FSharpWorkspaceServiceFactory [ Seq.map (fun d -> d.FilePath, d) |> Map + + let! sourceFiles = + options.SourceFiles + |> Seq.map (fun path -> + async { + let document = documents[path] + let! version = document.GetTextVersionAsync() |> Async.AwaitTask + + let getSource () = + task { + let! sourceText = document.GetTextAsync() + return sourceText.ToFSharpSourceText() + } + + return + { + FileName = path + Version = version.ToString() + GetSource = getSource + } + }) + |> Async.Parallel + + // TODO: referenced projects + let referencedProjects = [] + + let projectSnapshot: FSharpProjectSnapshot = + { + ProjectFileName = options.ProjectFileName + ProjectId = options.ProjectId + SourceFiles = sourceFiles |> List.ofArray + OtherOptions = options.OtherOptions |> List.ofArray + ReferencedProjects = referencedProjects + IsIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = options.UseScriptResolutionRules + LoadTime = options.LoadTime + UnresolvedReferences = options.UnresolvedReferences + OriginalLoadReferences = options.OriginalLoadReferences + Stamp = options.Stamp + } + + let! (parseResults, checkFileAnswer) = checker.ParseAndCheckFileInProject(fileName, projectSnapshot, userOpName) + + return + match checkFileAnswer with + | FSharpCheckFileAnswer.Aborted -> None + | FSharpCheckFileAnswer.Succeeded (checkFileResults) -> Some(parseResults, checkFileResults) + } + /// Parse and check the source text from the Roslyn document with possible stale results. member checker.ParseAndCheckDocumentWithPossibleStaleResults ( @@ -77,11 +136,6 @@ module private CheckerExtensions = return None // worker is cancelled at this point, we cannot return it and wait its completion anymore } - let bindParsedInput (results: (FSharpParseFileResults * FSharpCheckFileResults) option) = - match results with - | Some (parseResults, checkResults) -> Some(parseResults, parseResults.ParseTree, checkResults) - | None -> None - if allowStaleResults then let! freshResults = tryGetFreshResultsWithTimeout () @@ -95,10 +149,10 @@ module private CheckerExtensions = | None -> return! parseAndCheckFile } - return bindParsedInput results + return results else let! results = parseAndCheckFile - return bindParsedInput results + return results } /// Parse and check the source text from the Roslyn document. @@ -110,12 +164,17 @@ module private CheckerExtensions = ?allowStaleResults: bool ) = async { - let allowStaleResults = - match allowStaleResults with - | Some b -> b - | _ -> document.Project.IsFSharpStaleCompletionResultsEnabled - return! checker.ParseAndCheckDocumentWithPossibleStaleResults(document, options, allowStaleResults, userOpName = userOpName) + if document.Project.UseTransparentCompiiler then + return! checker.ParseAndCheckDocumentUsingTransparentCompiler(document, options, userOpName) + else + let allowStaleResults = + match allowStaleResults with + | Some b -> b + | _ -> document.Project.IsFSharpStaleCompletionResultsEnabled + + return! + checker.ParseAndCheckDocumentWithPossibleStaleResults(document, options, allowStaleResults, userOpName = userOpName) } [] @@ -215,7 +274,7 @@ type Document with let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync(userOpName) match! checker.ParseAndCheckDocument(this, projectOptions, userOpName, allowStaleResults = false) with - | Some (parseResults, _, checkResults) -> return (parseResults, checkResults) + | Some results -> return results | _ -> return raise (System.OperationCanceledException("Unable to get FSharp parse and check results.")) } diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 9ecda0ff396..cd07b46b18e 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -105,6 +105,7 @@ type AdvancedOptions = IsInlineParameterNameHintsEnabled: bool IsInlineReturnTypeHintsEnabled: bool IsLiveBuffersEnabled: bool + UseTransparentCompiler: bool } static member Default = @@ -115,6 +116,7 @@ type AdvancedOptions = IsInlineParameterNameHintsEnabled = false IsInlineReturnTypeHintsEnabled = false IsLiveBuffersEnabled = FSharpExperimentalFeaturesEnabledAutomatically + UseTransparentCompiler = false } [] @@ -238,3 +240,6 @@ module EditorOptionsExtensions = member this.IsFastFindReferencesEnabled = this.EditorOptions.LanguageServicePerformance.EnableFastFindReferencesAndRename + + member this.UseTransparentCompiiler = + this.EditorOptions.Advanced.UseTransparentCompiler diff --git a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml index 78afe373f84..7ecefa11f6f 100644 --- a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml +++ b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml @@ -38,6 +38,10 @@ + + + diff --git a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs index 05c26ae57a3..5185ce665cc 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs +++ b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs @@ -447,6 +447,15 @@ public static string Tooltip_preferred_description_width_in_characters { } } + /// + /// Looks up a localized string similar to Transparent Compiler (experimental). + /// + public static string TransparentCompiler { + get { + return ResourceManager.GetString("TransparentCompiler", resourceCulture); + } + } + /// /// Looks up a localized string similar to Analyze and suggest fixes for unused values. /// @@ -473,5 +482,14 @@ public static string Use_syntax_tree_cache { return ResourceManager.GetString("Use_syntax_tree_cache", resourceCulture); } } + + /// + /// Looks up a localized string similar to Use Transparent Compiler (restart required). + /// + public static string Use_Transparent_Compiler { + get { + return ResourceManager.GetString("Use_Transparent_Compiler", resourceCulture); + } + } } } diff --git a/vsintegration/src/FSharp.UIResources/Strings.resx b/vsintegration/src/FSharp.UIResources/Strings.resx index 98d089b053b..3db260b4c0f 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.resx +++ b/vsintegration/src/FSharp.UIResources/Strings.resx @@ -255,4 +255,10 @@ Display return type hints (preview) + + Transparent Compiler (experimental) + + + Use Transparent Compiler (restart required) + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf index 8d56d2adbdd..5853389fa82 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf @@ -147,6 +147,11 @@ Umožňuje formátovat podpis na danou šířku přidáním konců řádků odpovídajících pravidlům syntaxe F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements Odebrat nepoužívané otevřené výkazy @@ -227,6 +232,11 @@ Navrhovat názvy pro nerozpoznané identifikátory + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Výsledky analýzy mezipaměti (experimentální) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf index ea47f390c09..7d86580264b 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf @@ -147,6 +147,11 @@ Formatieren Sie die Signatur in der angegebenen Breite, indem Sie Zeilenumbrüche hinzufügen, die F#-Syntaxregeln entsprechen. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements Nicht verwendete "open"-Anweisungen entfernen @@ -227,6 +232,11 @@ Namen für nicht aufgelöste Bezeichner vorschlagen + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Cacheanalyseergebnisse (experimentell) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf index d58237d68ba..49697e5b010 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf @@ -147,6 +147,11 @@ Da formato a la firma al ancho dado agregando saltos de línea conforme a las reglas de sintaxis de F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements Quitar instrucciones open no usadas @@ -227,6 +232,11 @@ Sugerir nombres para los identificadores no resueltos + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Resultados del análisis de la caché (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf index 30ee652da68..4aa94e96d46 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf @@ -147,6 +147,11 @@ Formatez la signature à la largeur donnée en ajoutant des sauts de ligne conformes aux règles de syntaxe F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements Supprimer les instructions open inutilisées @@ -227,6 +232,11 @@ Suggérer des noms pour les identificateurs non résolus + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Résultats de l'analyse du cache (expérimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf index 6b4009249b8..c146e523ef4 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf @@ -147,6 +147,11 @@ Consente di formattare la firma in base alla larghezza specificata aggiungendo interruzioni di riga conformi alle regole di sintassi F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements Rimuovi istruzioni OPEN inutilizzate @@ -227,6 +232,11 @@ Suggerisci nomi per gli identificatori non risolti + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Risultati dell'analisi della cache (sperimentale) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf index 074852e1f72..1367bd628ff 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf @@ -147,6 +147,11 @@ F# 構文規則に準拠した改行を追加して、署名を指定された幅に書式設定します。 + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements 未使用の Open ステートメントを削除する @@ -227,6 +232,11 @@ 未解決の識別子の名前を提案します + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) キャッシュ解析の結果 (試験段階) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf index 369dc3d81cc..ce8eeb81b88 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf @@ -147,6 +147,11 @@ F# 구문 규칙에 맞는 줄 바꿈을 추가하여 지정된 너비에 시그니처의 서식을 지정합니다. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements 사용되지 않는 open 문 제거 @@ -227,6 +232,11 @@ 확인되지 않은 식별자의 이름 제안 + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) 캐시 구문 분석 결과(실험적) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf index 8f85bfb4f67..947a7bfff5f 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf @@ -147,6 +147,11 @@ Sformatuj sygnaturę na daną szerokość, dodając podziały wierszy zgodne z regułami składni języka F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements Usuń nieużywane otwarte instrukcje @@ -227,6 +232,11 @@ Sugeruj nazwy w przypadku nierozpoznanych identyfikatorów + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Wyniki analizy pamięci podręcznej (eksperymentalne) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf index 257b0f3f57a..9064b2678ff 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf @@ -147,6 +147,11 @@ Formate a assinatura para a largura fornecida adicionando quebras de linha em conformidade com as regras de sintaxe F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements Remover instruções abertas não usadas @@ -227,6 +232,11 @@ Sugerir nomes para identificadores não resolvidos + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Resultados da análise de cache (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf index ed978ec293a..0c1210c1d2b 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf @@ -147,6 +147,11 @@ Форматирование подписи до заданной ширины путем добавления разрывов строк, соответствующих правилам синтаксиса F#. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements Удалить неиспользуемые открытые операторы @@ -227,6 +232,11 @@ Предлагать имена для неразрешенных идентификаторов + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Результаты анализа кэша (экспериментальная функция) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf index 181076a632d..aea8a281677 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf @@ -147,6 +147,11 @@ F# söz dizimi kurallarına uyan satır sonları ekleyerek imzayı belirtilen genişliğe biçimlendirin. + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements Kullanılmayan açık deyimleri kaldır @@ -227,6 +232,11 @@ Çözümlenmemiş tanımlayıcılar için ad öner + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) Ayrıştırma sonuçlarını önbelleğe al (deneysel) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf index 4fc3ff63c93..6012f77e8b7 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf @@ -147,6 +147,11 @@ 通过添加符合 F# 语法规则的换行符,将签名设置为给定宽度的格式。 + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements 删除未使用的 open 语句 @@ -227,6 +232,11 @@ 为未解析标识符建议名称 + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) 缓存分析结果(实验性) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf index 1b0248ea005..a62f9671cd9 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf @@ -147,6 +147,11 @@ 透過新增符合 F# 語法規則的分行符號,將簽章格式設定為指定寬度。 + + Transparent Compiler (experimental) + Transparent Compiler (experimental) + + Remove unused open statements 移除未使用的 open 陳述式 @@ -227,6 +232,11 @@ 為未解析的識別碼建議名稱 + + Use Transparent Compiler (restart required) + Use Transparent Compiler (restart required) + + Cache parsing results (experimental) 快取剖析結果 (實驗性) From c3fd50cf674f42419c3b14de7e151e5e7c07d36a Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 2 May 2023 12:11:45 +0200 Subject: [PATCH 011/222] wip --- src/Compiler/Service/FSharpCheckerResults.fs | 35 +++++++++ src/Compiler/Service/FSharpCheckerResults.fsi | 2 + src/Compiler/Service/service.fs | 2 + src/Compiler/Service/service.fsi | 3 + .../FSharp.Compiler.ComponentTests.fsproj | 13 ++-- .../ProjectGeneration.fs | 36 +++++++++- .../LanguageService/WorkspaceExtensions.fs | 71 ++++++++----------- 7 files changed, 111 insertions(+), 51 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 5b2dbb3cd54..5e419ab9431 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -366,6 +366,41 @@ type FSharpProjectSnapshot with Stamp = this.Stamp } + static member FromOptions(options: FSharpProjectOptions, getFileSnapshot) = + async { + let! sourceFiles = + options.SourceFiles + |> Seq.map (getFileSnapshot options) + |> Async.Parallel + + let! referencedProjects = + options.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProject.FSharpReference (outputName, options) -> Some ( + async { + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + return FSharpReferencedProjectSnapshot.FSharpReference (outputName, snapshot) + }) + // TODO: other types + | _ -> None) + |> Async.Parallel + + return + { + ProjectFileName = options.ProjectFileName + ProjectId = options.ProjectId + SourceFiles = sourceFiles |> List.ofArray + OtherOptions = options.OtherOptions |> List.ofArray + ReferencedProjects = referencedProjects |> List.ofArray + IsIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = options.UseScriptResolutionRules + LoadTime = options.LoadTime + UnresolvedReferences = options.UnresolvedReferences + OriginalLoadReferences = options.OriginalLoadReferences + Stamp = options.Stamp + } + } + [] module internal FSharpCheckerResultsSettings = diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 20ecde853ea..e07f31afe90 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -248,6 +248,8 @@ type FSharpProjectSnapshot with member ToOptions: unit -> FSharpProjectOptions + static member FromOptions: options: FSharpProjectOptions * getFileSnapshot: (FSharpProjectOptions -> string -> Async) -> Async + /// Represents the use of an F# symbol from F# source code [] type public FSharpSymbolUse = diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 69b6f7186dc..83a005c258e 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -269,6 +269,8 @@ type FSharpChecker useTransparentCompiler ) + member _.UsesTransparentCompiler = useTransparentCompiler = Some true + member _.ReferenceResolver = legacyReferenceResolver member _.MatchBraces(fileName, sourceText: ISourceText, options: FSharpParsingOptions, ?userOpName: string) = diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index 45522fa3126..97bde85058f 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -60,6 +60,9 @@ type public FSharpChecker = [] ?useTransparentCompiler: bool -> FSharpChecker + [] + member UsesTransparentCompiler: bool + /// /// Parse a source code file, returning information about brace matching in the file. /// Return an enumeration of the matching parenthetical tokens in the file. diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index bc4a072aee6..b47712251d2 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -222,12 +222,9 @@ - - - - - %(RelativeDir)TestSource\%(Filename)%(Extension) - + + + @@ -249,8 +246,12 @@ + + + %(RelativeDir)TestSource\%(Filename)%(Extension) + diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index c43e0c88446..a395002d2b0 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -17,6 +17,7 @@ module FSharp.Test.ProjectGeneration open System open System.IO +open System.Threading.Tasks open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Diagnostics open FSharp.Compiler.Text @@ -328,7 +329,7 @@ module ProjectOperations = let addSignatureFile f = { f with SignatureFile = AutoGenerated } - let checkFile fileId (project: SyntheticProject) (checker: FSharpChecker) = + let checkFileWithIncrementalBuilder fileId (project: SyntheticProject) (checker: FSharpChecker) = let file = project.Find fileId let contents = renderSourceFile project file let absFileName = getFilePath project file @@ -340,6 +341,33 @@ module ProjectOperations = project.GetProjectOptions checker ) + let getFileSnapshot (project: SyntheticProject) _options path = + async { + let file = project.FindByPath path + let version = $"{file.PublicVersion}.{file.InternalVersion}.{file.Source.GetHashCode()}.{file.ExtraSource.GetHashCode()}" + + return { + FileName = file.FileName + Version = version + GetSource = fun () -> renderSourceFile project file |> SourceText.ofString |> Task.FromResult + } + } + + let checkFileWithTransparentCompiler fileId (project: SyntheticProject) (checker: FSharpChecker) = + async { + let file = project.Find fileId + let absFileName = getFilePath project file + let options = project.GetProjectOptions checker + let! projectSnapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot project) + return! checker.ParseAndCheckFileInProject(absFileName, projectSnapshot) + } + + let checkFile fileId (project: SyntheticProject) (checker: FSharpChecker) = + (if checker.UsesTransparentCompiler then + checkFileWithTransparentCompiler + else + checkFileWithIncrementalBuilder) fileId project checker + let getTypeCheckResult (parseResults: FSharpParseFileResults, checkResults: FSharpCheckFileAnswer) = Assert.True(not parseResults.ParseHadErrors) @@ -545,11 +573,13 @@ type ProjectWorkflowBuilder ?checker: FSharpChecker, ?useGetSource, ?useChangeNotifications, - ?useSyntaxTreeCache + ?useSyntaxTreeCache, + ?useTransparentCompiler ) = let useGetSource = defaultArg useGetSource false let useChangeNotifications = defaultArg useChangeNotifications false + let useTransparentCompiler = defaultArg useTransparentCompiler false let mutable latestProject = initialProject @@ -579,7 +609,7 @@ type ProjectWorkflowBuilder captureIdentifiersWhenParsing = true, documentSource = (if useGetSource then DocumentSource.Custom getSource else DocumentSource.FileSystem), useSyntaxTreeCache = defaultArg useSyntaxTreeCache false, - useTransparentCompiler = true + useTransparentCompiler = useTransparentCompiler )) let mapProjectAsync f workflow = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index d7053340933..4e9f681dc96 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -31,51 +31,38 @@ module private CheckerExtensions = userOpName: string ) = async { - let fileName = document.FilePath + let project = document.Project - let documents = project.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map - - let! sourceFiles = - options.SourceFiles - |> Seq.map (fun path -> - async { - let document = documents[path] - let! version = document.GetTextVersionAsync() |> Async.AwaitTask - - let getSource () = - task { - let! sourceText = document.GetTextAsync() - return sourceText.ToFSharpSourceText() - } - - return - { - FileName = path - Version = version.ToString() - GetSource = getSource - } - }) - |> Async.Parallel - - // TODO: referenced projects - let referencedProjects = [] - - let projectSnapshot: FSharpProjectSnapshot = - { - ProjectFileName = options.ProjectFileName - ProjectId = options.ProjectId - SourceFiles = sourceFiles |> List.ofArray - OtherOptions = options.OtherOptions |> List.ofArray - ReferencedProjects = referencedProjects - IsIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment - UseScriptResolutionRules = options.UseScriptResolutionRules - LoadTime = options.LoadTime - UnresolvedReferences = options.UnresolvedReferences - OriginalLoadReferences = options.OriginalLoadReferences - Stamp = options.Stamp + let solution = project.Solution + // TODO cache? + let projects = + solution.Projects + |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) + |> Map + + let getFileSnapshot (options: FSharpProjectOptions) path = + async { + let project = projects[options.ProjectFileName] + let document = project[path] + let! version = document.GetTextVersionAsync() |> Async.AwaitTask + + let getSource () = + task { + let! sourceText = document.GetTextAsync() + return sourceText.ToFSharpSourceText() + } + + return + { + FileName = path + Version = version.ToString() + GetSource = getSource + } } - let! (parseResults, checkFileAnswer) = checker.ParseAndCheckFileInProject(fileName, projectSnapshot, userOpName) + let! projectSnapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + + let! (parseResults, checkFileAnswer) = checker.ParseAndCheckFileInProject(document.FilePath, projectSnapshot, userOpName) return match checkFileAnswer with From 831ed47840851823248ac49244fbe5338fb17a4c Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 2 May 2023 18:15:15 +0200 Subject: [PATCH 012/222] wip --- src/Compiler/Facilities/AsyncMemoize.fs | 9 +- src/Compiler/Service/TransparentCompiler.fs | 19 ++-- .../CompilerService/AsyncMemoize.fs | 7 +- .../FSharpChecker/CommonWorkflows.fs | 1 - .../FSharpChecker/TransparentCompiler.fs | 91 +++++++++++++++++++ .../ProjectGeneration.fs | 24 +++-- 6 files changed, 126 insertions(+), 25 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 960d2d0eeda..220c27c8e96 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -3,6 +3,7 @@ namespace Internal.Utilities.Collections open FSharp.Compiler.BuildGraph open System.Threading open System.Collections.Generic +open FSharp.Compiler.Diagnostics type internal Action<'TKey, 'TValue> = | GetOrCompute of ('TKey -> NodeCode<'TValue>) * CancellationToken @@ -26,10 +27,12 @@ type internal JobEvent<'TKey> = | Finished key -> key | Canceled key -> key -type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?eventLog: ResizeArray>) = +type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobEvent<'TKey> -> unit), ?name: string) = let tok = obj () + let name = name |> Option.defaultValue "Unnamed" + let cache = MruCache<_, 'TKey, Job<'TValue>>(keepStrongly = 10, areSame = (fun (x, y) -> x = y)) @@ -46,7 +49,7 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?eventLog: Resiz inbox.PostAndAsyncReply(fun rc -> key, msg, rc) |> Async.Ignore |> Async.Start let log event = - eventLog |> Option.iter (fun log -> log.Add event) + logEvent |> Option.iter ((|>) event) let agent = MailboxProcessor.Start(fun (inbox: MailboxProcessor>) -> @@ -74,6 +77,8 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?eventLog: Resiz Async.StartAsTask( Async.AwaitNodeCode( node { + use _ = Activity.start $"AsyncMemoize.{name}" [| |] + let! result = computation key post key JobCompleted return result diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 81a2406d811..2ee026bc7aa 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -73,13 +73,13 @@ type internal TransparentCompiler // Is having just one of these ok? let lexResourceManager = Lexhelp.LexResourceManager() - let ParseFileCache = AsyncMemoize() - let ParseAndCheckFileInProjectCache = AsyncMemoize() - let FrameworkImportsCache = AsyncMemoize() - let BootstrapInfoCache = AsyncMemoize() - let TcPriorCache = AsyncMemoize() - let TcIntermediateCache = AsyncMemoize() - let DependencyGraphForLastFileCache = AsyncMemoize() + let ParseFileCache = AsyncMemoize(name = "ParseFile") + let ParseAndCheckFileInProjectCache = AsyncMemoize(name = "ParseAndCheckFileInProject") + let FrameworkImportsCache = AsyncMemoize(name = "FrameworkImports") + let BootstrapInfoCache = AsyncMemoize(name = "BootstrapInfo") + let TcPriorCache = AsyncMemoize(name = "TcPrior") + let TcIntermediateCache = AsyncMemoize(name = "TcIntermediate") + let DependencyGraphForLastFileCache = AsyncMemoize(name = "DependencyGraphForLastFile") // We currently share one global dependency provider for all scripts for the FSharpChecker. // For projects, one is used per project. @@ -699,6 +699,7 @@ type internal TransparentCompiler let mergeTcInfos = Array.fold (fun a b -> + // TODO: proper merge { a with tcState = b.tcState tcEnvAtEndOfFile = b.tcEnvAtEndOfFile @@ -768,7 +769,7 @@ type internal TransparentCompiler let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) userOpName _key = node { - let! bootstrapInfoOpt, creationDiags = BootstrapInfoCache.Get(projectSnapshot.Key, ComputeBootstrapInfo projectSnapshot) // probably cache + let! bootstrapInfoOpt, creationDiags = BootstrapInfoCache.Get(projectSnapshot.Key, ComputeBootstrapInfo projectSnapshot) match bootstrapInfoOpt with | None -> @@ -779,7 +780,7 @@ type internal TransparentCompiler | Some bootstrapInfo -> let file = - bootstrapInfo.SourceFiles |> List.find (fun f -> f.Source.FileName = fileName) + bootstrapInfo.SourceFiles |> List.tryFind (fun f -> f.Source.FileName = fileName) |> Option.defaultWith (fun _ -> failwith ($"File {fileName} not found in project snapshot. Files in project: \n\n" + (bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n"))) let priorSnapshot = projectSnapshot.UpTo fileName let! tcInfo = TcPriorCache.Get(priorSnapshot.Key, ComputeTcPrior file bootstrapInfo priorSnapshot userOpName) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 78123ef4efd..4d18dbbbbac 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -18,7 +18,7 @@ let ``Basics``() = let eventLog = ResizeArray() - let memoize = AsyncMemoize(eventLog) + let memoize = AsyncMemoize(eventLog.Add) let task = NodeCode.Parallel(seq { @@ -50,7 +50,7 @@ let ``We can cancel a job`` () = } let eventLog = ResizeArray() - let memoize = AsyncMemoize(eventLog) + let memoize = AsyncMemoize(eventLog.Add) use cts1 = new CancellationTokenSource() use cts2 = new CancellationTokenSource() @@ -91,7 +91,7 @@ let ``Job keeps running even if first requestor cancels`` () = } let eventLog = ResizeArray() - let memoize = AsyncMemoize(eventLog) + let memoize = AsyncMemoize(eventLog.Add) use cts1 = new CancellationTokenSource() use cts2 = new CancellationTokenSource() @@ -111,5 +111,6 @@ let ``Job keeps running even if first requestor cancels`` () = let result = _task2.Result Assert.Equal(2, result) + Thread.Sleep 1 // Wait for event log to be updated Assert.Equal array>([| Started key; Finished key |], eventLog |> Seq.toArray ) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs index 7aa8dfc25a3..21864e64ed4 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs @@ -132,4 +132,3 @@ let ``Using getSource and notifications instead of filesystem`` () = checkFile middle expectSignatureChanged checkFile last expectSignatureChanged } - diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs new file mode 100644 index 00000000000..4c93a1b4d0c --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -0,0 +1,91 @@ +module FSharp.Compiler.ComponentTests.FSharpChecker.TransparentCompiler + +open System +open System.IO +open System.Diagnostics + +open Xunit + +open FSharp.Test.ProjectGeneration +open FSharp.Compiler.Text +open FSharp.Compiler.CodeAnalysis + +module Activity = + let listen (filter: string) = + let indent (activity: Activity) = + let rec loop (activity: Activity) n = + if activity.Parent <> null then + loop (activity.Parent) (n + 1) + else + n + + String.replicate (loop activity 0) " " + + let collectTags (activity: Activity) = + [ for tag in activity.Tags -> $"{tag.Key}: %A{tag.Value}" ] + |> String.concat ", " + + let listener = + new ActivityListener( + ShouldListenTo = (fun source -> source.Name = FSharp.Compiler.Diagnostics.ActivityNames.FscSourceName), + Sample = + (fun context -> + if context.Name.Contains(filter) then + ActivitySamplingResult.AllDataAndRecorded + else + ActivitySamplingResult.None), + ActivityStarted = (fun a -> Trace.TraceInformation $"{indent a}{a.OperationName} {collectTags a}") + ) + + ActivitySource.AddActivityListener(listener) + + let listenToAll () = listen "" + + +[] +let ``Use Transparent Compiler`` () = + + let _logger = Activity.listenToAll () + + let size = 20 + + let project = + { SyntheticProject.Create() with + SourceFiles = [ + sourceFile $"File%03d{0}" [] + for i in 1..size do + sourceFile $"File%03d{i}" [$"File%03d{i-1}"] + ] + } + + let first = "File001" + let middle = $"File%03d{size / 2}" + let last = $"File%03d{size}" + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + updateFile first updatePublicSurface + checkFile first expectSignatureChanged + checkFile last expectSignatureChanged + updateFile middle updatePublicSurface + checkFile last expectSignatureChanged + addFileAbove middle (sourceFile "addedFile" [first]) + updateFile middle (addDependency "addedFile") + checkFile middle expectSignatureChanged + checkFile last expectSignatureChanged + } + +[] +let ``Parallel processing`` () = + + let _logger = Activity.listenToAll () + + let project = SyntheticProject.Create( + sourceFile "A" [], + sourceFile "B" ["A"], + sourceFile "C" ["A"], + sourceFile "D" ["A"], + sourceFile "E" ["B"; "C"; "D"]) + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + checkFile "E" expectOk + } diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index a395002d2b0..73082a5df5f 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -344,10 +344,11 @@ module ProjectOperations = let getFileSnapshot (project: SyntheticProject) _options path = async { let file = project.FindByPath path - let version = $"{file.PublicVersion}.{file.InternalVersion}.{file.Source.GetHashCode()}.{file.ExtraSource.GetHashCode()}" + let dependencies = file.DependsOn |> String.concat "|" + let version = $"{file.PublicVersion}.{file.InternalVersion}.{file.Source.GetHashCode()}.{file.ExtraSource.GetHashCode()}.{dependencies}" return { - FileName = file.FileName + FileName = getFilePath project file Version = version GetSource = fun () -> renderSourceFile project file |> SourceText.ofString |> Task.FromResult } @@ -551,14 +552,17 @@ let SaveAndCheckProject project checker = if not (Array.isEmpty results.Diagnostics) then failwith $"Project {project.Name} failed initial check: \n%A{results.Diagnostics}" - let! signatures = - Async.Sequential - [ for file in project.SourceFiles do - async { - let! result = checkFile file.Id project checker - let signature = getSignature result - return file.Id, signature - } ] + // TODO: re-enable + //let! signatures = + // Async.Sequential + // [ for file in project.SourceFiles do + // async { + // let! result = checkFile file.Id project checker + // let signature = getSignature result + // return file.Id, signature + // } ] + + let signatures = [ for file in project.SourceFiles -> file.Id, "" ] return { Project = project From 88593d0949316875708997b4ebe8c98a17f74acc Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 3 May 2023 13:23:42 +0200 Subject: [PATCH 013/222] wip --- src/Compiler/Service/IncrementalBuild.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index 8d48ec4503b..dc27983338e 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -262,7 +262,7 @@ type BoundModel private ( use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.TypeCheck) beforeFileChecked.Trigger fileName - + ApplyMetaCommandsFromInputToTcConfig (tcConfig, input, Path.GetDirectoryName fileName, tcImports.DependencyProvider) |> ignore let sink = TcResultsSinkImpl(tcGlobals) let hadParseErrors = not (Array.isEmpty parseErrors) @@ -328,7 +328,7 @@ type BoundModel private ( let getTcInfoExtras (typeCheck: GraphNode) = node { - let! _x , sink, implFile, fileName = typeCheck.GetOrComputeValue() + let! _ , sink, implFile, fileName = typeCheck.GetOrComputeValue() // Build symbol keys let itemKeyStore, semanticClassification = if enableBackgroundItemKeyStoreAndSemanticClassification then From 0dcc16393f0056ea37679fd68c34447019670640 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 3 May 2023 17:56:35 +0200 Subject: [PATCH 014/222] wip --- src/Compiler/Service/TransparentCompiler.fs | 29 +-------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 2ee026bc7aa..de41d47b82b 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -557,34 +557,6 @@ type internal TransparentCompiler return bootstrapInfoOpt, diagnostics } - let ComputeParseFile' (file: FSharpFile) (projectSnapshot: FSharpProjectSnapshot) bootstrapInfo userOpName _key = - node { - - let parsingOptions = - FSharpParsingOptions.FromTcConfig( - bootstrapInfo.TcConfig, - projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName) |> Array.ofSeq, - projectSnapshot.UseScriptResolutionRules - ) - - // TODO: what is this? - // GraphNode.SetPreferredUILang tcPrior.TcConfig.preferredUiLang - - let! sourceText = file.Source.GetSource() |> NodeCode.AwaitTask - - let diagnostics, parsedInput, anyErrors = - ParseAndCheckFile.parseFile ( - sourceText, - file.Source.FileName, - parsingOptions, - userOpName, - suggestNamesForErrors, - captureIdentifiersWhenParsing - ) - - return diagnostics, parsedInput, anyErrors, sourceText - } - let ComputeParseFile (file: FSharpFile) bootstrapInfo _key = node { let tcConfig = bootstrapInfo.TcConfig @@ -621,6 +593,7 @@ type internal TransparentCompiler // TODO: we will probably want to cache and re-use larger graphs if available let graph = DependencyResolution.mkGraph tcConfig.compilingFSharpCore filePairs sourceFiles + |> fst |> Graph.subGraphFor (sourceFiles |> Array.last).Idx return graph, filePairs From 68c1c03b4e988bff9030097ff3c13f541246230e Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 4 May 2023 16:22:43 +0200 Subject: [PATCH 015/222] wip --- src/Compiler/Driver/ParseAndCheckInputs.fsi | 13 +++++++ src/Compiler/Service/TransparentCompiler.fs | 37 ++++++++++++++----- .../TypeChecks/Graph/GraphOperations.fs | 1 + 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fsi b/src/Compiler/Driver/ParseAndCheckInputs.fsi index 745afa51be4..307236f8014 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fsi +++ b/src/Compiler/Driver/ParseAndCheckInputs.fsi @@ -13,6 +13,7 @@ open FSharp.Compiler.CompilerImports open FSharp.Compiler.Diagnostics open FSharp.Compiler.DependencyManager open FSharp.Compiler.DiagnosticsLogger +open FSharp.Compiler.NameResolution open FSharp.Compiler.Syntax open FSharp.Compiler.TcGlobals open FSharp.Compiler.Text @@ -151,6 +152,18 @@ val CheckOneInput: input: ParsedInput -> Cancellable<(TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType) * TcState> +val AddCheckResultsToTcState: + tcGlobals: TcGlobals * + amap: Import.ImportMap * + hadSig: bool * + prefixPathOpt: LongIdent option * + tcSink: TcResultsSink * + tcImplEnv: TcEnv * + qualNameOfFile: QualifiedNameOfFile * + implFileSigType: ModuleOrNamespaceType -> + tcState: TcState -> + ModuleOrNamespaceType * TcState + /// Finish the checking of multiple inputs val CheckMultipleInputsFinish: (TcEnv * TopAttribs * 'T option * 'U) list * TcState -> (TcEnv * TopAttribs * 'T list * 'U list) * TcState diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index de41d47b82b..ccd2b065f7e 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -32,6 +32,7 @@ open FSharp.Compiler.Syntax open FSharp.Compiler.CompilerDiagnostics open FSharp.Compiler.NameResolution open Internal.Utilities.Library.Extras +open FSharp.Compiler.TypedTree type internal FSharpFile = { @@ -52,6 +53,8 @@ type internal BootstrapInfo = LoadClosure: LoadClosure option } +type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string + type internal TransparentCompiler ( legacyReferenceResolver, @@ -165,9 +168,9 @@ type internal TransparentCompiler unresolvedReferences, dependencyProvider, loadClosureOpt: LoadClosure option, - basicDependencies, + basicDependencies #if !NO_TYPEPROVIDERS - importsInvalidatedByTypeProvider: Event + ,importsInvalidatedByTypeProvider: Event #endif ) = @@ -484,9 +487,9 @@ type internal TransparentCompiler unresolvedReferences, dependencyProvider, loadClosureOpt, - basicDependencies, + basicDependencies #if !NO_TYPEPROVIDERS - importsInvalidatedByTypeProvider + ,importsInvalidatedByTypeProvider #endif ) @@ -670,11 +673,27 @@ type internal TransparentCompiler return tcInfo, sink, implFile, fileName } - let mergeTcInfos = - Array.fold (fun a b -> + let mergeIntermediateResults bootstrapInfo = + Array.reduce (fun (a: TcInfo, _: TcResultsSinkImpl, _, _) (b, bSink, implFileOpt: CheckedImplFile option, name) -> // TODO: proper merge + + let amap = bootstrapInfo.TcImports.GetImportMap() + + // TODO: figure out + let hadSig = false + + let prefixPathOpt = None + + // TODO: + let implFile = implFileOpt.Value + + let _ccuSigForFile, tcState = + AddCheckResultsToTcState + (bootstrapInfo.TcGlobals, amap, hadSig, prefixPathOpt, TcResultsSink.NoSink, b.tcState.TcEnvFromImpls, implFile.QualifiedNameOfFile, implFile.Signature) + a.tcState + { a with - tcState = b.tcState + tcState = tcState tcEnvAtEndOfFile = b.tcEnvAtEndOfFile moduleNamesDict = b.moduleNamesDict latestCcuSigForFile = b.latestCcuSigForFile @@ -683,7 +702,7 @@ type internal TransparentCompiler tcDependencyFiles = b.tcDependencyFiles @ a.tcDependencyFiles // we shouldn't need this with graph checking (?) sigNameOpt = None - }) + }, bSink, Some implFile, name) // Type check everything that is needed to check given file let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) _userOpName _key = @@ -733,7 +752,7 @@ type internal TransparentCompiler TcIntermediateCache.Get(key, ComputeTcIntermediate (parsedInput, parseErrors) bootstrapInfo tcInfo)) |> NodeCode.Parallel - return! processLayer rest (mergeTcInfos tcInfo (results |> Array.map p14)) + return! processLayer rest (mergeIntermediateResults bootstrapInfo results |> p14) } return! processLayer layers bootstrapInfo.InitialTcInfo diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs index 95d966441e1..f8e58f352a9 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs @@ -65,6 +65,7 @@ let ``See what this does`` () = let fullGraph = DependencyResolution.mkGraph false filePairs sourceFiles + |> fst |> Graph.map (fun idx -> project.SourceFilePaths[idx] |> Path.GetFileName) let subGraph = fullGraph |> Graph.subGraphFor "FileF.fs" From 09a522fc7482b92b826dddbfe028864911699ae6 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 4 May 2023 17:49:25 +0200 Subject: [PATCH 016/222] wip --- src/Compiler/Service/TransparentCompiler.fs | 14 +++++++------- .../FSharpChecker/TransparentCompiler.fs | 4 ++-- .../ProjectGeneration.fs | 19 ++++++++----------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index ccd2b065f7e..9775fc871e4 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -674,7 +674,7 @@ type internal TransparentCompiler } let mergeIntermediateResults bootstrapInfo = - Array.reduce (fun (a: TcInfo, _: TcResultsSinkImpl, _, _) (b, bSink, implFileOpt: CheckedImplFile option, name) -> + Array.fold (fun (a: TcInfo, _, _, _) (b, sink, implFileOpt: CheckedImplFile option, name) -> // TODO: proper merge let amap = bootstrapInfo.TcImports.GetImportMap() @@ -687,22 +687,22 @@ type internal TransparentCompiler // TODO: let implFile = implFileOpt.Value - let _ccuSigForFile, tcState = + let ccuSigForFile, tcState = AddCheckResultsToTcState - (bootstrapInfo.TcGlobals, amap, hadSig, prefixPathOpt, TcResultsSink.NoSink, b.tcState.TcEnvFromImpls, implFile.QualifiedNameOfFile, implFile.Signature) - a.tcState + (bootstrapInfo.TcGlobals, amap, hadSig, prefixPathOpt, TcResultsSink.NoSink, a.tcState.TcEnvFromImpls, implFile.QualifiedNameOfFile, implFile.Signature) + b.tcState { a with tcState = tcState tcEnvAtEndOfFile = b.tcEnvAtEndOfFile moduleNamesDict = b.moduleNamesDict - latestCcuSigForFile = b.latestCcuSigForFile + latestCcuSigForFile = Some ccuSigForFile tcDiagnosticsRev = b.tcDiagnosticsRev @ a.tcDiagnosticsRev topAttribs = b.topAttribs tcDependencyFiles = b.tcDependencyFiles @ a.tcDependencyFiles // we shouldn't need this with graph checking (?) sigNameOpt = None - }, bSink, Some implFile, name) + }, sink, Some implFile, name) // Type check everything that is needed to check given file let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) _userOpName _key = @@ -752,7 +752,7 @@ type internal TransparentCompiler TcIntermediateCache.Get(key, ComputeTcIntermediate (parsedInput, parseErrors) bootstrapInfo tcInfo)) |> NodeCode.Parallel - return! processLayer rest (mergeIntermediateResults bootstrapInfo results |> p14) + return! processLayer rest (mergeIntermediateResults bootstrapInfo (tcInfo, Unchecked.defaultof<_>, None, "") results |> p14) } return! processLayer layers bootstrapInfo.InitialTcInfo diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 4c93a1b4d0c..d619d9e45c1 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -45,7 +45,7 @@ module Activity = [] let ``Use Transparent Compiler`` () = - let _logger = Activity.listenToAll () + Activity.listenToAll () let size = 20 @@ -77,7 +77,7 @@ let ``Use Transparent Compiler`` () = [] let ``Parallel processing`` () = - let _logger = Activity.listenToAll () + Activity.listenToAll () let project = SyntheticProject.Create( sourceFile "A" [], diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 73082a5df5f..16d42718f1e 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -552,17 +552,14 @@ let SaveAndCheckProject project checker = if not (Array.isEmpty results.Diagnostics) then failwith $"Project {project.Name} failed initial check: \n%A{results.Diagnostics}" - // TODO: re-enable - //let! signatures = - // Async.Sequential - // [ for file in project.SourceFiles do - // async { - // let! result = checkFile file.Id project checker - // let signature = getSignature result - // return file.Id, signature - // } ] - - let signatures = [ for file in project.SourceFiles -> file.Id, "" ] + let! signatures = + Async.Sequential + [ for file in project.SourceFiles do + async { + let! result = checkFile file.Id project checker + let signature = getSignature result + return file.Id, signature + } ] return { Project = project From dd518d825e12006fd01856528940646073a29e7f Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 4 May 2023 17:53:14 +0200 Subject: [PATCH 017/222] fantomas --- .fantomasignore | 1 + src/Compiler/Driver/ParseAndCheckInputs.fsi | 2 +- src/Compiler/Facilities/AsyncMemoize.fs | 5 ++--- src/Compiler/Service/FSharpCheckerResults.fs | 17 ++++++++--------- src/Compiler/Service/FSharpCheckerResults.fsi | 4 +++- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.fantomasignore b/.fantomasignore index d2f57d7fc7f..f9924a137aa 100644 --- a/.fantomasignore +++ b/.fantomasignore @@ -91,6 +91,7 @@ src/FSharp.Core/seqcore.fs src/Compiler/AbstractIL/ilwrite.fs src/Compiler/Utilities/lib.fs src/Compiler/Service/IncrementalBuild.fs +src/Compiler/Service/TransparentCompiler.fs src/Compiler/Service/ServiceAssemblyContent.fs src/Compiler/Service/ServiceDeclarationLists.fs src/Compiler/Service/ServiceErrorResolutionHints.fs diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fsi b/src/Compiler/Driver/ParseAndCheckInputs.fsi index 307236f8014..9d2bb5b9e41 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fsi +++ b/src/Compiler/Driver/ParseAndCheckInputs.fsi @@ -162,7 +162,7 @@ val AddCheckResultsToTcState: qualNameOfFile: QualifiedNameOfFile * implFileSigType: ModuleOrNamespaceType -> tcState: TcState -> - ModuleOrNamespaceType * TcState + ModuleOrNamespaceType * TcState /// Finish the checking of multiple inputs val CheckMultipleInputsFinish: diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 220c27c8e96..8cb38c9540d 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -48,8 +48,7 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobE let sendAsync (inbox: MailboxProcessor<_>) key msg = inbox.PostAndAsyncReply(fun rc -> key, msg, rc) |> Async.Ignore |> Async.Start - let log event = - logEvent |> Option.iter ((|>) event) + let log event = logEvent |> Option.iter ((|>) event) let agent = MailboxProcessor.Start(fun (inbox: MailboxProcessor>) -> @@ -77,7 +76,7 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobE Async.StartAsTask( Async.AwaitNodeCode( node { - use _ = Activity.start $"AsyncMemoize.{name}" [| |] + use _ = Activity.start $"AsyncMemoize.{name}" [||] let! result = computation key post key JobCompleted diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 5e419ab9431..9f7973d90c7 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -368,19 +368,18 @@ type FSharpProjectSnapshot with static member FromOptions(options: FSharpProjectOptions, getFileSnapshot) = async { - let! sourceFiles = - options.SourceFiles - |> Seq.map (getFileSnapshot options) - |> Async.Parallel + let! sourceFiles = options.SourceFiles |> Seq.map (getFileSnapshot options) |> Async.Parallel let! referencedProjects = options.ReferencedProjects |> Seq.choose (function - | FSharpReferencedProject.FSharpReference (outputName, options) -> Some ( - async { - let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) - return FSharpReferencedProjectSnapshot.FSharpReference (outputName, snapshot) - }) + | FSharpReferencedProject.FSharpReference (outputName, options) -> + Some( + async { + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + return FSharpReferencedProjectSnapshot.FSharpReference(outputName, snapshot) + } + ) // TODO: other types | _ -> None) |> Async.Parallel diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index e07f31afe90..43052e69f9c 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -248,7 +248,9 @@ type FSharpProjectSnapshot with member ToOptions: unit -> FSharpProjectOptions - static member FromOptions: options: FSharpProjectOptions * getFileSnapshot: (FSharpProjectOptions -> string -> Async) -> Async + static member FromOptions: + options: FSharpProjectOptions * getFileSnapshot: (FSharpProjectOptions -> string -> Async) -> + Async /// Represents the use of an F# symbol from F# source code [] From f2bd3494e017f994aeb9cf72256c9990182d2a35 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 4 May 2023 19:34:39 +0200 Subject: [PATCH 018/222] tests --- .../CompilerService/AsyncMemoize.fs | 2 + ...ervice.SurfaceArea.netstandard20.debug.bsl | 126 +++++++++++++++++- ...vice.SurfaceArea.netstandard20.release.bsl | 123 ++++++++++++++++- 3 files changed, 248 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 4d18dbbbbac..2dae1fab2e4 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -69,6 +69,8 @@ let ``We can cancel a job`` () = cts1.Cancel() cts2.Cancel() + Thread.Sleep 10 + Assert.Equal array>([| Started key |], eventLog |> Seq.toArray ) cts3.Cancel() diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 7b5b7a8f4dd..32d9d761d36 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -1961,6 +1961,16 @@ FSharp.Compiler.CodeAnalysis.DocumentSource: FSharp.Compiler.CodeAnalysis.Docume FSharp.Compiler.CodeAnalysis.DocumentSource: Int32 Tag FSharp.Compiler.CodeAnalysis.DocumentSource: Int32 get_Tag() FSharp.Compiler.CodeAnalysis.DocumentSource: System.String ToString() +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 braceMatchCacheSize +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 checkFileInProjectCacheSize +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 frameworkTcImportsCacheStrongSize +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_braceMatchCacheSize() +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_checkFileInProjectCacheSize() +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_frameworkTcImportsCacheStrongSize() +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_parseFileCacheSize() +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_projectCacheSizeDefault() +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 parseFileCacheSize +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 projectCacheSizeDefault FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer+Succeeded: FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults Item FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer+Succeeded: FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults get_Item() FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer+Tags: Int32 Aborted @@ -2031,7 +2041,9 @@ FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: FSharp.Compiler.Symbols. FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] DependencyFiles FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] get_DependencyFiles() -FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Boolean UsesTransparentCompiler +FSharp.Compiler.CodeAnalysis.FSharpChecker: Boolean get_UsesTransparentCompiler() +FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Instance FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker get_Instance() FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions GetProjectOptionsFromCommandLineArgs(System.String, System.String[], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) @@ -2050,6 +2062,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyFileChanged(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyProjectCleaned(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]]] GetProjectOptionsFromScript(System.String, FSharp.Compiler.Text.ISourceText, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.String[]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Int64], Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2073,6 +2086,18 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearCache(System.Collections.G FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateAll() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateConfiguration(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]] GetSource +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]] get_GetSource() +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String FileName +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String ToString() +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String Version +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String get_FileName() +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String get_Version() +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.Tuple`2[System.String,System.String] Key +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.Tuple`2[System.String,System.String] get_Key() +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]]) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsBindingALambdaAtPosition(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPosContainedInApplication(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPositionContainedInACurriedParameter(FSharp.Compiler.Text.Position) @@ -2165,6 +2190,65 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: System.String[] SourceFiles FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: System.String[] get_OtherOptions() FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: System.String[] get_SourceFiles() FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], System.String[], System.String[], FSharp.Compiler.CodeAnalysis.FSharpReferencedProject[], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean Equals(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean IsIncompleteTypeCheckEnvironment +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean UseScriptResolutionRules +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_IsIncompleteTypeCheckEnvironment() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_UseScriptResolutionRules() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions ToOptions() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(Int32) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(System.String) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey Key +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey get_Key() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot] SourceFiles +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot] get_SourceFiles() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot] ReferencedProjects +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot] get_ReferencedProjects() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] OtherOptions +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] SourceFileNames +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_OtherOptions() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_SourceFileNames() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] OriginalLoadReferences +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] get_OriginalLoadReferences() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot]]]) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] UnresolvedReferences +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] get_UnresolvedReferences() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] Stamp +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] get_Stamp() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] ProjectId +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_ProjectId() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.DateTime LoadTime +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.DateTime get_LoadTime() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String ProjectFileName +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String ToString() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String get_ProjectFileName() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean Equals(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean IsIncompleteTypeCheckEnvironment +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean UseScriptResolutionRules +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean get_IsIncompleteTypeCheckEnvironment() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean get_UseScriptResolutionRules() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 CompareTo(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 CompareTo(System.Object) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey] ReferencedProjects +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey] get_ReferencedProjects() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] OtherOptions +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_OtherOptions() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]] SourceFiles +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]] get_SourceFiles() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String ProjectFileName +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String ToString() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String get_ProjectFileName() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Void .ctor(System.String, Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey], Boolean, Boolean) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: Boolean Equals(System.Object) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: FSharp.Compiler.CodeAnalysis.FSharpReferencedProject CreateFSharp(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: FSharp.Compiler.CodeAnalysis.FSharpReferencedProject CreateFromILModuleReader(System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.AbstractIL.ILBinaryReader+ILModuleReader]) @@ -2173,6 +2257,12 @@ FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: Int32 GetHashCode() FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: System.String OutputFile FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: System.String get_OutputFile() +FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot CreateFSharp(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot) +FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: System.String OutputFile +FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: System.String ToString() +FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: System.String get_OutputFile() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromAttribute FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromComputationExpression FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromDefinition @@ -2210,6 +2300,35 @@ FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: Boolean Equals(Syste FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: Int32 GetHashCode() FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: Int32 GetHashCode(System.Collections.IEqualityComparer) FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: System.String ToString() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Boolean Equals(FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey NewFrameworkImportsCacheKey(Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, System.Decimal) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 CompareTo(FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 CompareTo(System.Object) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 Tag +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 get_Tag() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_resolvedpath() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_targetFrameworkDirectories() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] resolvedpath +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] targetFrameworkDirectories +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.Decimal get_langVersion() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.Decimal langVersion +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String ToString() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String assemblyName +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String fsharpBinaries +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String get_assemblyName() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String get_fsharpBinaries() +FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSameForChecking2(System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) +FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSameForChecking3(System.Tuple`3[System.String,System.Int64,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`3[System.String,System.Int64,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) +FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSameForParsing[a](System.Tuple`3[System.String,System.Int64,a], System.Tuple`3[System.String,System.Int64,a]) +FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSimilarForParsing[a,b,c,d,e](System.Tuple`3[a,b,c], System.Tuple`3[a,d,e]) +FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSubsumable2(System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) +FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSubsumable3[a,b](System.Tuple`3[System.String,a,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`3[System.String,b,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) +FSharp.Compiler.CodeAnalysis.Helpers: Boolean NamesContainAttribute(FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Collections.FSharpSet`1[System.String]) FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver: FSharp.Compiler.CodeAnalysis.LegacyResolvedFile[] Resolve(FSharp.Compiler.CodeAnalysis.LegacyResolutionEnvironment, System.Tuple`2[System.String,System.String][], System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpFunc`2[System.Boolean,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.Unit]]]) FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver: System.String DotNetFrameworkReferenceAssembliesRootDirectory FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver: System.String HighestInstalledNetFrameworkVersion() @@ -2253,6 +2372,8 @@ FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_baggage() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_itemSpec() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String itemSpec FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.String],System.String], System.String) +FSharp.Compiler.CodeAnalysis.ParseCacheLockToken: Void .ctor() +FSharp.Compiler.CodeAnalysis.ScriptClosureCacheToken: Void .ctor() FSharp.Compiler.CompilerEnvironment: Boolean IsCheckerSupportedSubcategory(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsCompilable(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsScriptFile(System.String) @@ -10133,7 +10254,7 @@ FSharp.Compiler.Tokenization.FSharpLineTokenizer: FSharp.Compiler.Tokenization.F FSharp.Compiler.Tokenization.FSharpLineTokenizer: System.Tuple`2[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Tokenization.FSharpTokenInfo],FSharp.Compiler.Tokenization.FSharpTokenizerLexState] ScanToken(FSharp.Compiler.Tokenization.FSharpTokenizerLexState) FSharp.Compiler.Tokenization.FSharpSourceTokenizer: FSharp.Compiler.Tokenization.FSharpLineTokenizer CreateBufferTokenizer(Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`3[System.Char[],System.Int32,System.Int32],System.Int32]) FSharp.Compiler.Tokenization.FSharpSourceTokenizer: FSharp.Compiler.Tokenization.FSharpLineTokenizer CreateLineTokenizer(System.String) -FSharp.Compiler.Tokenization.FSharpSourceTokenizer: Void .ctor(Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.Tokenization.FSharpSourceTokenizer: Void .ctor(Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.Tokenization.FSharpToken: Boolean IsCommentTrivia FSharp.Compiler.Tokenization.FSharpToken: Boolean IsIdentifier FSharp.Compiler.Tokenization.FSharpToken: Boolean IsKeyword @@ -11302,6 +11423,7 @@ FSharp.Compiler.Tokenization.FSharpTokenizerColorState: FSharp.Compiler.Tokeniza FSharp.Compiler.Tokenization.FSharpTokenizerColorState: FSharp.Compiler.Tokenization.FSharpTokenizerColorState Comment FSharp.Compiler.Tokenization.FSharpTokenizerColorState: FSharp.Compiler.Tokenization.FSharpTokenizerColorState EndLineThenSkip FSharp.Compiler.Tokenization.FSharpTokenizerColorState: FSharp.Compiler.Tokenization.FSharpTokenizerColorState EndLineThenToken +FSharp.Compiler.Tokenization.FSharpTokenizerColorState: FSharp.Compiler.Tokenization.FSharpTokenizerColorState ExtendedInterpolatedString FSharp.Compiler.Tokenization.FSharpTokenizerColorState: FSharp.Compiler.Tokenization.FSharpTokenizerColorState IfDefSkip FSharp.Compiler.Tokenization.FSharpTokenizerColorState: FSharp.Compiler.Tokenization.FSharpTokenizerColorState InitialState FSharp.Compiler.Tokenization.FSharpTokenizerColorState: FSharp.Compiler.Tokenization.FSharpTokenizerColorState SingleLineComment diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index f8b2c9dbd40..32d9d761d36 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -1961,6 +1961,16 @@ FSharp.Compiler.CodeAnalysis.DocumentSource: FSharp.Compiler.CodeAnalysis.Docume FSharp.Compiler.CodeAnalysis.DocumentSource: Int32 Tag FSharp.Compiler.CodeAnalysis.DocumentSource: Int32 get_Tag() FSharp.Compiler.CodeAnalysis.DocumentSource: System.String ToString() +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 braceMatchCacheSize +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 checkFileInProjectCacheSize +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 frameworkTcImportsCacheStrongSize +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_braceMatchCacheSize() +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_checkFileInProjectCacheSize() +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_frameworkTcImportsCacheStrongSize() +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_parseFileCacheSize() +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_projectCacheSizeDefault() +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 parseFileCacheSize +FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 projectCacheSizeDefault FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer+Succeeded: FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults Item FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer+Succeeded: FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults get_Item() FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer+Tags: Int32 Aborted @@ -2031,7 +2041,9 @@ FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: FSharp.Compiler.Symbols. FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] DependencyFiles FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] get_DependencyFiles() -FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Boolean UsesTransparentCompiler +FSharp.Compiler.CodeAnalysis.FSharpChecker: Boolean get_UsesTransparentCompiler() +FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Instance FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker get_Instance() FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions GetProjectOptionsFromCommandLineArgs(System.String, System.String[], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) @@ -2050,6 +2062,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyFileChanged(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyProjectCleaned(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]]] GetProjectOptionsFromScript(System.String, FSharp.Compiler.Text.ISourceText, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.String[]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Int64], Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2073,6 +2086,18 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearCache(System.Collections.G FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateAll() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateConfiguration(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]] GetSource +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]] get_GetSource() +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String FileName +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String ToString() +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String Version +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String get_FileName() +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String get_Version() +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.Tuple`2[System.String,System.String] Key +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.Tuple`2[System.String,System.String] get_Key() +FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]]) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsBindingALambdaAtPosition(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPosContainedInApplication(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPositionContainedInACurriedParameter(FSharp.Compiler.Text.Position) @@ -2165,6 +2190,65 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: System.String[] SourceFiles FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: System.String[] get_OtherOptions() FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: System.String[] get_SourceFiles() FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], System.String[], System.String[], FSharp.Compiler.CodeAnalysis.FSharpReferencedProject[], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean Equals(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean IsIncompleteTypeCheckEnvironment +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean UseScriptResolutionRules +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_IsIncompleteTypeCheckEnvironment() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_UseScriptResolutionRules() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions ToOptions() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(Int32) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(System.String) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey Key +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey get_Key() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot] SourceFiles +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot] get_SourceFiles() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot] ReferencedProjects +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot] get_ReferencedProjects() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] OtherOptions +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] SourceFileNames +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_OtherOptions() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_SourceFileNames() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] OriginalLoadReferences +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] get_OriginalLoadReferences() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot]]]) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] UnresolvedReferences +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] get_UnresolvedReferences() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] Stamp +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] get_Stamp() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] ProjectId +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_ProjectId() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.DateTime LoadTime +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.DateTime get_LoadTime() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String ProjectFileName +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String ToString() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String get_ProjectFileName() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean Equals(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean IsIncompleteTypeCheckEnvironment +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean UseScriptResolutionRules +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean get_IsIncompleteTypeCheckEnvironment() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean get_UseScriptResolutionRules() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 CompareTo(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 CompareTo(System.Object) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey] ReferencedProjects +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey] get_ReferencedProjects() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] OtherOptions +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_OtherOptions() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]] SourceFiles +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]] get_SourceFiles() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String ProjectFileName +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String ToString() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String get_ProjectFileName() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Void .ctor(System.String, Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey], Boolean, Boolean) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: Boolean Equals(System.Object) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: FSharp.Compiler.CodeAnalysis.FSharpReferencedProject CreateFSharp(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: FSharp.Compiler.CodeAnalysis.FSharpReferencedProject CreateFromILModuleReader(System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.AbstractIL.ILBinaryReader+ILModuleReader]) @@ -2173,6 +2257,12 @@ FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: Int32 GetHashCode() FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: System.String OutputFile FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: System.String get_OutputFile() +FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot CreateFSharp(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot) +FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: System.String OutputFile +FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: System.String ToString() +FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: System.String get_OutputFile() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromAttribute FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromComputationExpression FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromDefinition @@ -2210,6 +2300,35 @@ FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: Boolean Equals(Syste FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: Int32 GetHashCode() FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: Int32 GetHashCode(System.Collections.IEqualityComparer) FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: System.String ToString() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Boolean Equals(FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey NewFrameworkImportsCacheKey(Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, System.Decimal) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 CompareTo(FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 CompareTo(System.Object) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 Tag +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 get_Tag() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_resolvedpath() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_targetFrameworkDirectories() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] resolvedpath +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] targetFrameworkDirectories +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.Decimal get_langVersion() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.Decimal langVersion +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String ToString() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String assemblyName +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String fsharpBinaries +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String get_assemblyName() +FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String get_fsharpBinaries() +FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSameForChecking2(System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) +FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSameForChecking3(System.Tuple`3[System.String,System.Int64,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`3[System.String,System.Int64,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) +FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSameForParsing[a](System.Tuple`3[System.String,System.Int64,a], System.Tuple`3[System.String,System.Int64,a]) +FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSimilarForParsing[a,b,c,d,e](System.Tuple`3[a,b,c], System.Tuple`3[a,d,e]) +FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSubsumable2(System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) +FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSubsumable3[a,b](System.Tuple`3[System.String,a,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`3[System.String,b,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) +FSharp.Compiler.CodeAnalysis.Helpers: Boolean NamesContainAttribute(FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Collections.FSharpSet`1[System.String]) FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver: FSharp.Compiler.CodeAnalysis.LegacyResolvedFile[] Resolve(FSharp.Compiler.CodeAnalysis.LegacyResolutionEnvironment, System.Tuple`2[System.String,System.String][], System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpFunc`2[System.Boolean,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.Unit]]]) FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver: System.String DotNetFrameworkReferenceAssembliesRootDirectory FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver: System.String HighestInstalledNetFrameworkVersion() @@ -2253,6 +2372,8 @@ FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_baggage() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_itemSpec() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String itemSpec FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.String],System.String], System.String) +FSharp.Compiler.CodeAnalysis.ParseCacheLockToken: Void .ctor() +FSharp.Compiler.CodeAnalysis.ScriptClosureCacheToken: Void .ctor() FSharp.Compiler.CompilerEnvironment: Boolean IsCheckerSupportedSubcategory(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsCompilable(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsScriptFile(System.String) From b3e8b236ea8f067a4faf6656491311d6f0a2bbc8 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 5 May 2023 13:25:06 +0200 Subject: [PATCH 019/222] try to deflake test --- .../CompilerService/AsyncMemoize.fs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 2dae1fab2e4..a2e6c84bdc7 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -43,7 +43,10 @@ let ``Basics``() = [] let ``We can cancel a job`` () = + let resetEvent = new ManualResetEvent(false) + let computation key = node { + resetEvent.Set() |> ignore do! Async.Sleep 1000 |> NodeCode.AwaitAsync failwith "Should be canceled before it gets here" return key * 2 @@ -62,7 +65,7 @@ let ``We can cancel a job`` () = let _task2 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts2.Token) let _task3 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts3.Token) - Thread.Sleep 10 + resetEvent.WaitOne() |> ignore Assert.Equal array>([| Started key |], eventLog |> Seq.toArray ) From a3739866bafc94c0322bde7274354576ad338628 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 5 May 2023 14:23:03 +0200 Subject: [PATCH 020/222] fix fsproj --- src/Compiler/FSharp.Compiler.Service.fsproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 1cd12ec49ae..5eaf378da62 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -470,7 +470,7 @@ - + From 9902755342a6c1966bfcc51cf94ab81f77427803 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 5 May 2023 15:08:05 +0200 Subject: [PATCH 021/222] tests --- src/Compiler/Facilities/AsyncMemoize.fs | 4 +- .../FSharpChecker/TransparentCompiler.fs | 81 +++++++++++++++++++ .../ProjectGeneration.fs | 55 ++++++++----- 3 files changed, 116 insertions(+), 24 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 8cb38c9540d..941cacb8013 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -77,7 +77,7 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobE Async.AwaitNodeCode( node { use _ = Activity.start $"AsyncMemoize.{name}" [||] - + log (Started key) let! result = computation key post key JobCompleted return result @@ -86,8 +86,6 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobE cancellationToken = cts.Token ) - log (Started key) - let job = NodeCode.AwaitTask startedComputation cache.Set(tok, key, (Running(job, cts))) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index d619d9e45c1..4fa464d9bac 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -89,3 +89,84 @@ let ``Parallel processing`` () = ProjectWorkflowBuilder(project, useTransparentCompiler = true) { checkFile "E" expectOk } + + +let makeTestProject () = + SyntheticProject.Create( + sourceFile "First" [], + sourceFile "Second" ["First"], + sourceFile "Third" ["First"], + { sourceFile "Last" ["Second"; "Third"] with EntryPoint = true }) + +let testWorkflow () = + ProjectWorkflowBuilder(makeTestProject(), useTransparentCompiler = true) + +[] +let ``Edit file, check it, then check dependent file`` () = + testWorkflow() { + updateFile "First" breakDependentFiles + checkFile "First" expectSignatureChanged + checkFile "Second" expectErrors + } + +[] +let ``Edit file, don't check it, check dependent file`` () = + testWorkflow() { + updateFile "First" breakDependentFiles + checkFile "Second" expectErrors + } + +[] +let ``Check transitive dependency`` () = + testWorkflow() { + updateFile "First" breakDependentFiles + checkFile "Last" expectSignatureChanged + } + +[] +let ``Change multiple files at once`` () = + testWorkflow() { + updateFile "First" (setPublicVersion 2) + updateFile "Second" (setPublicVersion 2) + updateFile "Third" (setPublicVersion 2) + checkFile "Last" (expectSignatureContains "val f: x: 'a -> (ModuleFirst.TFirstV_2<'a> * ModuleSecond.TSecondV_2<'a>) * (ModuleFirst.TFirstV_2<'a> * ModuleThird.TThirdV_2<'a>) * TLastV_1<'a>") + } + +// TODO: [] +let ``Files depend on signature file if present`` () = + let project = makeTestProject() |> updateFile "First" addSignatureFile + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + updateFile "First" breakDependentFiles + saveFile "First" + checkFile "Second" expectNoChanges + } + +[] +let ``Adding a file`` () = + testWorkflow() { + addFileAbove "Second" (sourceFile "New" []) + updateFile "Second" (addDependency "New") + checkFile "Last" (expectSignatureContains "val f: x: 'a -> (ModuleNew.TNewV_1<'a> * ModuleFirst.TFirstV_1<'a> * ModuleSecond.TSecondV_1<'a>) * (ModuleFirst.TFirstV_1<'a> * ModuleThird.TThirdV_1<'a>) * TLastV_1<'a>") + } + +[] +let ``Removing a file`` () = + testWorkflow() { + removeFile "Second" + checkFile "Last" expectErrors + } + +[] +let ``Changes in a referenced project`` () = + let library = SyntheticProject.Create("library", sourceFile "Library" []) + + let project = + { makeTestProject() with DependsOn = [library] } + |> updateFile "First" (addDependency "Library") + + project.Workflow { + updateFile "Library" updatePublicSurface + saveFile "Library" + checkFile "Last" expectSignatureChanged + } \ No newline at end of file diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 16d42718f1e..388e16185d4 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -341,16 +341,43 @@ module ProjectOperations = project.GetProjectOptions checker ) - let getFileSnapshot (project: SyntheticProject) _options path = + let getSourceText (project: SyntheticProject) (filePath: string) = + if filePath.EndsWith(".fsi") then + let implFilePath = filePath[..filePath.Length - 2] + let source = project.FindByPath implFilePath + match source.SignatureFile with + | No -> failwith $"{implFilePath} does not have a signature file" + | Custom _ -> renderCustomSignatureFile project source + | AutoGenerated -> + if File.Exists filePath then + // TODO: could be outdated + File.ReadAllText filePath + else + failwith "AutoGenerated signatures not yet supported for getSource workflow" + else + filePath + |> project.FindByPath + |> renderSourceFile project + |> SourceText.ofString + + let getFileSnapshot (project: SyntheticProject) _options (path: string) = async { - let file = project.FindByPath path + let file, filePath = + if path.EndsWith(".fsi") then + let implFilePath = path[..path.Length - 2] + let f = project.FindByPath implFilePath + f, getSignatureFilePath project f + else + let f = project.FindByPath path + f, getFilePath project f + let dependencies = file.DependsOn |> String.concat "|" - let version = $"{file.PublicVersion}.{file.InternalVersion}.{file.Source.GetHashCode()}.{file.ExtraSource.GetHashCode()}.{dependencies}" + let version = $"{file.PublicVersion}|{file.InternalVersion}|{file.FunctionName}|{file.Source.GetHashCode()}|{file.ExtraSource.GetHashCode()}|{dependencies}" return { - FileName = getFilePath project file + FileName = filePath Version = version - GetSource = fun () -> renderSourceFile project file |> SourceText.ofString |> Task.FromResult + GetSource = fun () -> getSourceText project path |> Task.FromResult } } @@ -457,7 +484,7 @@ module ProjectOperations = match file.SignatureFile with | AutoGenerated when generateSignatureFiles -> - let project = { p with SourceFiles = p.SourceFiles[0..i] } + let project = { p with SourceFiles = p.SourceFiles[0..i - 1] @ [ { file with SignatureFile = No }] } let! results = checkFile file.Id project checker let signature = getSignature results writeFileIfChanged signatureFileName signature @@ -584,21 +611,7 @@ type ProjectWorkflowBuilder let mutable latestProject = initialProject - let getSource (filePath: string) = - if filePath.EndsWith(".fsi") then - let implFilePath = filePath[..filePath.Length - 2] - let source = latestProject.FindByPath implFilePath - match source.SignatureFile with - | No -> failwith $"{implFilePath} does not have a signature file" - | Custom _ -> renderCustomSignatureFile latestProject source - | AutoGenerated -> failwith "AutoGenerated signatures not yet supported for getSource workflow" - else - filePath - |> latestProject.FindByPath - |> renderSourceFile latestProject - |> SourceText.ofString - |> Some - |> async.Return + let getSource f = f |> getSourceText latestProject |> Some |> async.Return let checker = defaultArg From a2d7e567a5f77ce8ce99bb3bab6f3a859cd32210 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 5 May 2023 17:09:08 +0200 Subject: [PATCH 022/222] wip --- src/Compiler/Service/TransparentCompiler.fs | 20 +++++++++++-------- .../CompilerService/AsyncMemoize.fs | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 9775fc871e4..38a5a56fdbc 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -684,25 +684,29 @@ type internal TransparentCompiler let prefixPathOpt = None - // TODO: - let implFile = implFileOpt.Value - let ccuSigForFile, tcState = - AddCheckResultsToTcState - (bootstrapInfo.TcGlobals, amap, hadSig, prefixPathOpt, TcResultsSink.NoSink, a.tcState.TcEnvFromImpls, implFile.QualifiedNameOfFile, implFile.Signature) - b.tcState + match implFileOpt with + | Some implFile -> + + let ccuSigForFile, tcState = + AddCheckResultsToTcState + (bootstrapInfo.TcGlobals, amap, hadSig, prefixPathOpt, TcResultsSink.NoSink, a.tcState.TcEnvFromImpls, implFile.QualifiedNameOfFile, implFile.Signature) + b.tcState + Some ccuSigForFile, tcState + | None -> + b.latestCcuSigForFile, b.tcState { a with tcState = tcState tcEnvAtEndOfFile = b.tcEnvAtEndOfFile moduleNamesDict = b.moduleNamesDict - latestCcuSigForFile = Some ccuSigForFile + latestCcuSigForFile = ccuSigForFile tcDiagnosticsRev = b.tcDiagnosticsRev @ a.tcDiagnosticsRev topAttribs = b.topAttribs tcDependencyFiles = b.tcDependencyFiles @ a.tcDependencyFiles // we shouldn't need this with graph checking (?) sigNameOpt = None - }, sink, Some implFile, name) + }, sink, implFileOpt, name) // Type check everything that is needed to check given file let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) _userOpName _key = diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index a2e6c84bdc7..41880ac0d96 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -78,7 +78,7 @@ let ``We can cancel a job`` () = cts3.Cancel() - Thread.Sleep 10 + Thread.Sleep 100 Assert.Equal array>([| Started key; Canceled key |], eventLog |> Seq.toArray ) From f1eb8080eddd5e1949b6268798cc99b281e9feb3 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 9 May 2023 12:05:24 +0200 Subject: [PATCH 023/222] wip --- src/Compiler/Driver/ParseAndCheckInputs.fs | 73 ++++++++++--------- src/Compiler/Driver/ParseAndCheckInputs.fsi | 33 +++++++++ .../FSharpChecker/TransparentCompiler.fs | 2 +- .../TypeChecks/Graph/GraphOperations.fs | 9 ++- 4 files changed, 77 insertions(+), 40 deletions(-) diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index 55b9736ad56..171cc639580 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1608,6 +1608,42 @@ let AddSignatureResultToTcImplEnv (tcImports: TcImports, tcGlobals, prefixPathOp tcState.tcsTcSigEnv, EmptyTopAttrs, None, ccuSigForFile partialResult, tcState +let TransformDependencyGraph (graph: Graph, filePairs: FilePairMap) = + let mkArtificialImplFile n = NodeToTypeCheck.ArtificialImplFile n + let mkPhysicalFile n = NodeToTypeCheck.PhysicalFile n + + /// Map any signature dependencies to the ArtificialImplFile counterparts, + /// unless the signature dependency is the backing file of the current (implementation) file. + let mapDependencies idx deps = + Array.map + (fun dep -> + if filePairs.IsSignature dep then + let implIdx = filePairs.GetImplementationIndex dep + + if implIdx = idx then + // This is the matching signature for the implementation. + // Retain the direct dependency onto the signature file. + mkPhysicalFile dep + else + mkArtificialImplFile dep + else + mkPhysicalFile dep) + deps + + // Transform the graph to include ArtificialImplFile nodes when necessary. + graph + |> Seq.collect (fun (KeyValue (fileIdx, deps)) -> + if filePairs.IsSignature fileIdx then + // Add an additional ArtificialImplFile node for the signature file. + [| + // Mark the current file as physical and map the dependencies. + mkPhysicalFile fileIdx, mapDependencies fileIdx deps + // Introduce a new node that depends on the signature. + mkArtificialImplFile fileIdx, [| mkPhysicalFile fileIdx |] + |] + else + [| mkPhysicalFile fileIdx, mapDependencies fileIdx deps |]) + |> Graph.make /// Constructs a file dependency graph and type-checks the files in parallel where possible. let CheckMultipleInputsUsingGraphMode @@ -1630,42 +1666,7 @@ let CheckMultipleInputsUsingGraphMode let graph, trie = DependencyResolution.mkGraph tcConfig.compilingFSharpCore filePairs sourceFiles - let nodeGraph = - let mkArtificialImplFile n = NodeToTypeCheck.ArtificialImplFile n - let mkPhysicalFile n = NodeToTypeCheck.PhysicalFile n - - /// Map any signature dependencies to the ArtificialImplFile counterparts, - /// unless the signature dependency is the backing file of the current (implementation) file. - let mapDependencies idx deps = - Array.map - (fun dep -> - if filePairs.IsSignature dep then - let implIdx = filePairs.GetImplementationIndex dep - - if implIdx = idx then - // This is the matching signature for the implementation. - // Retain the direct dependency onto the signature file. - mkPhysicalFile dep - else - mkArtificialImplFile dep - else - mkPhysicalFile dep) - deps - - // Transform the graph to include ArtificialImplFile nodes when necessary. - graph - |> Seq.collect (fun (KeyValue (fileIdx, deps)) -> - if filePairs.IsSignature fileIdx then - // Add an additional ArtificialImplFile node for the signature file. - [| - // Mark the current file as physical and map the dependencies. - mkPhysicalFile fileIdx, mapDependencies fileIdx deps - // Introduce a new node that depends on the signature. - mkArtificialImplFile fileIdx, [| mkPhysicalFile fileIdx |] - |] - else - [| mkPhysicalFile fileIdx, mapDependencies fileIdx deps |]) - |> Graph.make + let nodeGraph = TransformDependencyGraph (graph, filePairs) // Persist the graph to a Mermaid diagram if specified. if tcConfig.typeCheckingConfig.DumpGraph then diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fsi b/src/Compiler/Driver/ParseAndCheckInputs.fsi index 9d2bb5b9e41..813f99e8c2d 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fsi +++ b/src/Compiler/Driver/ParseAndCheckInputs.fsi @@ -13,6 +13,7 @@ open FSharp.Compiler.CompilerImports open FSharp.Compiler.Diagnostics open FSharp.Compiler.DependencyManager open FSharp.Compiler.DiagnosticsLogger +open FSharp.Compiler.GraphChecking open FSharp.Compiler.NameResolution open FSharp.Compiler.Syntax open FSharp.Compiler.TcGlobals @@ -20,6 +21,36 @@ open FSharp.Compiler.Text open FSharp.Compiler.TypedTree open FSharp.Compiler.UnicodeLexing +/// Auxiliary type for re-using signature information in TcEnvFromImpls. +/// +/// TcState has two typing environments: TcEnvFromSignatures && TcEnvFromImpls +/// When type checking a file, depending on the type (implementation or signature), it will use one of these typing environments (TcEnv). +/// Checking a file will populate the respective TcEnv. +/// +/// When a file has a dependencies, the information of the signature file in case a pair (implementation file backed by a signature) will suffice to type-check that file. +/// Example: if `B.fs` has a dependency on `A`, the information of `A.fsi` is enough for `B.fs` to type-check, on condition that information is available in the TcEnvFromImpls. +/// We introduce a special ArtificialImplFile node in the graph to satisfy this. `B.fs -> [ A.fsi ]` becomes `B.fs -> [ ArtificialImplFile A ]. +/// The `ArtificialImplFile A` node will duplicate the signature information which A.fsi provided earlier. +/// Processing a `ArtificialImplFile` node will add the information from the TcEnvFromSignatures to the TcEnvFromImpls. +/// This means `A` will be known in both TcEnvs and therefor `B.fs` can be type-checked. +/// By doing this, we can speed up the graph processing as type checking a signature file is less expensive than its implementation counterpart. +/// +/// When we need to actually type-check an implementation file backed by a signature, we cannot have the duplicate information of the signature file present in TcEnvFromImpls. +/// Example `A.fs -> [ A.fsi ]`. An implementation file always depends on its signature. +/// Type-checking `A.fs` will add the actual information to TcEnvFromImpls and we do not depend on the `ArtificialImplFile A` for `A.fs`. +/// +/// In order to deal correctly with the `ArtificialImplFile` logic, we need to transform the resolved graph to contain the additional pair nodes. +/// After we have type-checked the graph, we exclude the ArtificialImplFile nodes as they are not actual physical files in our project. +[] +type NodeToTypeCheck = + /// A real physical file in the current project. + /// This can be either an implementation or a signature file. + | PhysicalFile of fileIndex: FileIndex + /// An artificial node that will add the earlier processed signature information to the TcEnvFromImpls. + /// Dependants on this type of node will perceive that a file is known in both TcEnvFromSignatures and TcEnvFromImpls. + /// Even though the actual implementation file was not type-checked. + | ArtificialImplFile of signatureFileIndex: FileIndex + val IsScript: string -> bool val ComputeQualifiedNameOfFileFromUniquePath: range * string list -> QualifiedNameOfFile @@ -163,6 +194,8 @@ val AddCheckResultsToTcState: implFileSigType: ModuleOrNamespaceType -> tcState: TcState -> ModuleOrNamespaceType * TcState + +val TransformDependencyGraph: graph: Graph * filePairs: FilePairMap -> Graph /// Finish the checking of multiple inputs val CheckMultipleInputsFinish: diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 4fa464d9bac..e4ee36efc92 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -132,7 +132,7 @@ let ``Change multiple files at once`` () = checkFile "Last" (expectSignatureContains "val f: x: 'a -> (ModuleFirst.TFirstV_2<'a> * ModuleSecond.TSecondV_2<'a>) * (ModuleFirst.TFirstV_2<'a> * ModuleThird.TThirdV_2<'a>) * TLastV_1<'a>") } -// TODO: [] +[] let ``Files depend on signature file if present`` () = let project = makeTestProject() |> updateFile "First" addSignatureFile diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs index f8e58f352a9..757248d8add 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs @@ -6,6 +6,7 @@ open FSharp.Compiler.GraphChecking open FSharp.Test.ProjectGeneration open FSharp.Compiler.Text open FSharp.Compiler +open FSharp.Compiler.ParseAndCheckInputs [] @@ -66,13 +67,15 @@ let ``See what this does`` () = let fullGraph = DependencyResolution.mkGraph false filePairs sourceFiles |> fst - |> Graph.map (fun idx -> project.SourceFilePaths[idx] |> Path.GetFileName) + //|> Graph.map (fun idx -> project.SourceFilePaths[idx] |> Path.GetFileName) - let subGraph = fullGraph |> Graph.subGraphFor "FileF.fs" + let subGraph = fullGraph |> Graph.subGraphFor 9 //"FileF.fs" let layers = Graph.leafSequence subGraph |> Seq.toList - ignore layers + let transformed = TransformDependencyGraph (subGraph, filePairs) + + ignore (layers, transformed) return () } From 20ea96d085bdb36bc6196c9fffd0914e3d250e75 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 9 May 2023 18:11:36 +0200 Subject: [PATCH 024/222] wip --- src/Compiler/Driver/GraphChecking/Graph.fs | 6 ++- src/Compiler/Driver/GraphChecking/Graph.fsi | 4 +- src/Compiler/Driver/ParseAndCheckInputs.fs | 5 +- src/Compiler/Driver/ParseAndCheckInputs.fsi | 14 +++++- src/Compiler/Service/TransparentCompiler.fs | 48 ++++++++++++++++--- .../FSharpChecker/TransparentCompiler.fs | 19 ++++++++ .../TypeChecks/Graph/GraphOperations.fs | 2 +- .../TypeChecks/Graph/TypedTreeGraph.fs | 4 +- .../ProjectGeneration.fs | 2 + 9 files changed, 88 insertions(+), 16 deletions(-) diff --git a/src/Compiler/Driver/GraphChecking/Graph.fs b/src/Compiler/Driver/GraphChecking/Graph.fs index 688fffc29bb..9cbce0b4b9b 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fs +++ b/src/Compiler/Driver/GraphChecking/Graph.fs @@ -124,7 +124,7 @@ module internal Graph = let print (graph: Graph<'Node>) : unit = printCustom graph (fun node -> node.ToString()) - let serialiseToMermaid path (graph: Graph) = + let serialiseToMermaid (graph: Graph) = let sb = StringBuilder() let appendLine (line: string) = sb.AppendLine(line) |> ignore @@ -139,8 +139,10 @@ module internal Graph = appendLine $" %i{idx} --> %i{depIdx}" appendLine "```" + sb.ToString() + let writeMermaidToFile path (graph: Graph) = use out = FileSystem.OpenFileForWriteShim(path, fileMode = System.IO.FileMode.Create) - out.WriteAllText(sb.ToString()) + graph |> serialiseToMermaid |> out.WriteAllText diff --git a/src/Compiler/Driver/GraphChecking/Graph.fsi b/src/Compiler/Driver/GraphChecking/Graph.fsi index 8c6b9c29079..7d885f5c96a 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fsi +++ b/src/Compiler/Driver/GraphChecking/Graph.fsi @@ -21,5 +21,7 @@ module internal Graph = val leafSequence: graph: Graph<'Node> -> Set<'Node> seq /// Print the contents of the graph to the standard output. val print: graph: Graph<'Node> -> unit + /// Create a simple Mermaid graph + val serialiseToMermaid: graph: Graph -> string /// Create a simple Mermaid graph and save it under the path specified. - val serialiseToMermaid: path: string -> graph: Graph -> unit + val writeMermaidToFile: path: string -> graph: Graph -> unit diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index 171cc639580..7a70b6f5c51 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1608,6 +1608,7 @@ let AddSignatureResultToTcImplEnv (tcImports: TcImports, tcGlobals, prefixPathOp tcState.tcsTcSigEnv, EmptyTopAttrs, None, ccuSigForFile partialResult, tcState + let TransformDependencyGraph (graph: Graph, filePairs: FilePairMap) = let mkArtificialImplFile n = NodeToTypeCheck.ArtificialImplFile n let mkPhysicalFile n = NodeToTypeCheck.PhysicalFile n @@ -1666,7 +1667,7 @@ let CheckMultipleInputsUsingGraphMode let graph, trie = DependencyResolution.mkGraph tcConfig.compilingFSharpCore filePairs sourceFiles - let nodeGraph = TransformDependencyGraph (graph, filePairs) + let nodeGraph = TransformDependencyGraph(graph, filePairs) // Persist the graph to a Mermaid diagram if specified. if tcConfig.typeCheckingConfig.DumpGraph then @@ -1686,7 +1687,7 @@ let CheckMultipleInputsUsingGraphMode .TrimStart([| '\\'; '/' |]) (idx, friendlyFileName)) - |> Graph.serialiseToMermaid graphFile) + |> Graph.writeMermaidToFile graphFile) let _ = ctok // TODO Use it let diagnosticsLogger = DiagnosticsThreadStatics.DiagnosticsLogger diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fsi b/src/Compiler/Driver/ParseAndCheckInputs.fsi index 813f99e8c2d..718a9e3d746 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fsi +++ b/src/Compiler/Driver/ParseAndCheckInputs.fsi @@ -194,7 +194,19 @@ val AddCheckResultsToTcState: implFileSigType: ModuleOrNamespaceType -> tcState: TcState -> ModuleOrNamespaceType * TcState - + +type PartialResult = TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType + +val AddSignatureResultToTcImplEnv: + tcImports: TcImports * + tcGlobals: TcGlobals * + prefixPathOpt: LongIdent option * + tcSink: TcResultsSink * + tcState: TcState * + input: ParsedInput -> + (TcState -> + PartialResult * TcState) + val TransformDependencyGraph: graph: Graph * filePairs: FilePairMap -> Graph /// Finish the checking of multiple inputs diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 38a5a56fdbc..bf11cc15829 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -599,7 +599,7 @@ type internal TransparentCompiler |> fst |> Graph.subGraphFor (sourceFiles |> Array.last).Idx - return graph, filePairs + return TransformDependencyGraph(graph, filePairs), filePairs } let ComputeTcIntermediate (parsedInput: ParsedInput, parseErrors) bootstrapInfo prevTcInfo _key = @@ -737,25 +737,59 @@ type internal TransparentCompiler ComputeDependencyGraphForLastFile (parsedInputs |> Seq.map p13) bootstrapInfo.TcConfig ) + let fileNames = + parsedInputs + |> Seq.mapi (fun idx (input, _, _) -> idx, Path.GetFileName input.FileName) + |> Map.ofSeq + + let debugGraph = + graph + |> Graph.map (function NodeToTypeCheck.PhysicalFile i -> i, fileNames[i] | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF : {fileNames[i]}") + + Trace.TraceInformation("\n" + (debugGraph |> Graph.serialiseToMermaid)) + // layers that can be processed in parallel let layers = Graph.leafSequence graph |> Seq.toList // remove the final layer, which should be the target file let layers = layers |> List.take (layers.Length - 1) - let rec processLayer (layers: Set list) tcInfo = + let rec processLayer (layers: Set list) tcInfo = node { match layers with | [] -> return tcInfo | layer :: rest -> let! results = layer - |> Seq.map (fun fileIndex -> - let key = projectSnapshot.UpTo(fileIndex).Key - let parsedInput, parseErrors, _ = parsedInputs[fileIndex] - TcIntermediateCache.Get(key, ComputeTcIntermediate (parsedInput, parseErrors) bootstrapInfo tcInfo)) + |> Seq.map (fun fileNode -> + + match fileNode with + | NodeToTypeCheck.PhysicalFile fileIndex -> + let parsedInput, parseErrors, _ = parsedInputs[fileIndex] + let key = projectSnapshot.UpTo(fileIndex).Key + TcIntermediateCache.Get(key, ComputeTcIntermediate (parsedInput, parseErrors) bootstrapInfo tcInfo) + | NodeToTypeCheck.ArtificialImplFile fileIndex -> + let parsedInput, _parseErrors, _ = parsedInputs[fileIndex] + let tcState = tcInfo.tcState + let prefixPathOpt = None + + let (tcEnv , topAttribs , checkedImplFileOpt , _moduleOrNamespaceType), newTcState = + // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. + AddSignatureResultToTcImplEnv(bootstrapInfo.TcImports, bootstrapInfo.TcGlobals, prefixPathOpt, TcResultsSink.NoSink, tcState, parsedInput) tcState + let tcInfo = + { tcInfo with + tcState = newTcState + tcEnvAtEndOfFile = tcEnv + + topAttribs = Some topAttribs + // we shouldn't need this with graph checking (?) + sigNameOpt = None + } + + node.Return(tcInfo, Unchecked.defaultof<_>, checkedImplFileOpt, parsedInput.FileName)) |> NodeCode.Parallel - + let nodes = layer |> Seq.map (function NodeToTypeCheck.PhysicalFile i -> fileNames[i] | NodeToTypeCheck.ArtificialImplFile i -> $"AIF : {fileNames[i]}") |> String.concat " " + Trace.TraceInformation $"Processed layer {nodes}" return! processLayer rest (mergeIntermediateResults bootstrapInfo (tcInfo, Unchecked.defaultof<_>, None, "") results |> p14) } diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index e4ee36efc92..ea679f5476d 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -91,6 +91,25 @@ let ``Parallel processing`` () = } +[] +let ``Parallel processing with signatures`` () = + + Activity.listenToAll () + + let project = SyntheticProject.Create( + sourceFile "A" [] |> addSignatureFile, + sourceFile "B" ["A"] |> addSignatureFile, + sourceFile "C" ["A"] |> addSignatureFile, + sourceFile "D" ["A"] |> addSignatureFile, + sourceFile "E" ["B"; "C"; "D"] |> addSignatureFile) + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + checkFile "C" expectOk + //updateFile "A" updatePublicSurface + //checkFile "E" expectSignatureChanged + } + + let makeTestProject () = SyntheticProject.Create( sourceFile "First" [], diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs index 757248d8add..dedac14ba76 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs @@ -76,7 +76,7 @@ let ``See what this does`` () = let transformed = TransformDependencyGraph (subGraph, filePairs) ignore (layers, transformed) - + return () } ) diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TypedTreeGraph.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TypedTreeGraph.fs index 284946eee9f..0eef5c79673 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TypedTreeGraph.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/TypedTreeGraph.fs @@ -140,7 +140,7 @@ let ``Create Graph from typed tree`` (projectArgumentsFilePath: string) = graphFromTypedTree |> Graph.map (fun n -> n,files.[n].File) - |> Graph.serialiseToMermaid $"{fileName}.typed-tree.deps.md" + |> Graph.writeMermaidToFile $"{fileName}.typed-tree.deps.md" let collectAllDeps (graph: Graph) = (Map.empty, [ 0 .. (sourceFiles.Length - 1) ]) @@ -162,7 +162,7 @@ let ``Create Graph from typed tree`` (projectArgumentsFilePath: string) = graphFromHeuristic |> Graph.map (fun n -> n, files.[n].File) - |> Graph.serialiseToMermaid $"{fileName}.heuristic-tree.deps.md" + |> Graph.writeMermaidToFile $"{fileName}.heuristic-tree.deps.md" let heuristicMap = collectAllDeps graphFromHeuristic diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 388e16185d4..0acd065dbe6 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -571,6 +571,8 @@ type WorkflowContext = let SaveAndCheckProject project checker = async { + use _ = + Activity.start "SaveAndCheckProject" [ Activity.Tags.project, project.Name ] do! saveProject project true checker From 91fad38a78148758fd5b416a18d8023e9304b1c4 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 10 May 2023 18:28:55 +0200 Subject: [PATCH 025/222] wip --- src/Compiler/Driver/ParseAndCheckInputs.fsi | 20 ++- src/Compiler/Service/TransparentCompiler.fs | 121 ++++++++---------- .../FSharpChecker/TransparentCompiler.fs | 30 ++++- .../ProjectGeneration.fs | 15 +++ 4 files changed, 109 insertions(+), 77 deletions(-) diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fsi b/src/Compiler/Driver/ParseAndCheckInputs.fsi index 718a9e3d746..37e76d82d48 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fsi +++ b/src/Compiler/Driver/ParseAndCheckInputs.fsi @@ -163,6 +163,9 @@ type TcState = member CreatesGeneratedProvidedTypes: bool + +type PartialResult = TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType + /// Get the initial type checking state for a set of inputs val GetInitialTcState: range * string * TcConfig * TcGlobals * TcImports * TcEnv * OpenDeclaration list -> TcState @@ -183,6 +186,18 @@ val CheckOneInput: input: ParsedInput -> Cancellable<(TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType) * TcState> +val CheckOneInputWithCallback: + checkForErrors: (unit -> bool) * + tcConfig: TcConfig * + tcImports: TcImports * + tcGlobals: TcGlobals * + prefixPathOpt: LongIdent option * + tcSink: TcResultsSink * + tcState: TcState * + input: ParsedInput * + _skipImplIfSigExists: bool -> Cancellable> + + val AddCheckResultsToTcState: tcGlobals: TcGlobals * amap: Import.ImportMap * @@ -195,8 +210,6 @@ val AddCheckResultsToTcState: tcState: TcState -> ModuleOrNamespaceType * TcState -type PartialResult = TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType - val AddSignatureResultToTcImplEnv: tcImports: TcImports * tcGlobals: TcGlobals * @@ -204,8 +217,7 @@ val AddSignatureResultToTcImplEnv: tcSink: TcResultsSink * tcState: TcState * input: ParsedInput -> - (TcState -> - PartialResult * TcState) + (TcState -> PartialResult * TcState) val TransformDependencyGraph: graph: Graph * filePairs: FilePairMap -> Graph diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index bf11cc15829..8df2390ea49 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -33,6 +33,7 @@ open FSharp.Compiler.CompilerDiagnostics open FSharp.Compiler.NameResolution open Internal.Utilities.Library.Extras open FSharp.Compiler.TypedTree +open FSharp.Compiler.CheckDeclarations type internal FSharpFile = { @@ -55,6 +56,23 @@ type internal BootstrapInfo = type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string + +/// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. +[] +type internal TcIntermediate = + { + finisher: Finisher + //tcEnvAtEndOfFile: TcEnv + + /// Disambiguation table for module names + moduleNamesDict: ModuleNamesDict + + /// Accumulated diagnostics, last file first + tcDiagnosticsRev:(PhasedDiagnostic * FSharpDiagnosticSeverity)[] list + + tcDependencyFiles: string list + } + type internal TransparentCompiler ( legacyReferenceResolver, @@ -602,7 +620,7 @@ type internal TransparentCompiler return TransformDependencyGraph(graph, filePairs), filePairs } - let ComputeTcIntermediate (parsedInput: ParsedInput, parseErrors) bootstrapInfo prevTcInfo _key = + let ComputeTcIntermediate (parsedInput: ParsedInput, parseErrors) bootstrapInfo (prevTcInfo: TcInfo) _key = node { let input = parsedInput let fileName = input.FileName @@ -633,8 +651,8 @@ type internal TransparentCompiler let input, moduleNamesDict = DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input - let! (tcEnvAtEndOfFile, topAttribs, implFile, ccuSigForFile), tcState = - CheckOneInput( + let! finisher = + CheckOneInputWithCallback( (fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), tcConfig, tcImports, @@ -642,7 +660,8 @@ type internal TransparentCompiler None, TcResultsSink.WithSink sink, prevTcInfo.tcState, - input + input, + false ) |> NodeCode.FromCancellable @@ -651,62 +670,27 @@ type internal TransparentCompiler let newErrors = Array.append parseErrors (capturingDiagnosticsLogger.Diagnostics |> List.toArray) - let tcEnvAtEndOfFile = - if keepAllBackgroundResolutions then - tcEnvAtEndOfFile - else - tcState.TcEnvFromImpls - - let tcInfo = + return { - tcState = tcState - tcEnvAtEndOfFile = tcEnvAtEndOfFile + finisher = finisher moduleNamesDict = moduleNamesDict - latestCcuSigForFile = Some ccuSigForFile tcDiagnosticsRev = [ newErrors ] - topAttribs = Some topAttribs tcDependencyFiles = [ fileName ] - // we shouldn't need this with graph checking (?) - sigNameOpt = None } - - return tcInfo, sink, implFile, fileName } - let mergeIntermediateResults bootstrapInfo = - Array.fold (fun (a: TcInfo, _, _, _) (b, sink, implFileOpt: CheckedImplFile option, name) -> - // TODO: proper merge - - let amap = bootstrapInfo.TcImports.GetImportMap() - - // TODO: figure out - let hadSig = false - - let prefixPathOpt = None - - let ccuSigForFile, tcState = - match implFileOpt with - | Some implFile -> - - let ccuSigForFile, tcState = - AddCheckResultsToTcState - (bootstrapInfo.TcGlobals, amap, hadSig, prefixPathOpt, TcResultsSink.NoSink, a.tcState.TcEnvFromImpls, implFile.QualifiedNameOfFile, implFile.Signature) - b.tcState - Some ccuSigForFile, tcState - | None -> - b.latestCcuSigForFile, b.tcState - - { a with + let mergeIntermediateResults = + Array.fold (fun (tcInfo: TcInfo) (tcIntermediate: TcIntermediate) -> + let (tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile), tcState = tcInfo.tcState |> tcIntermediate.finisher.Invoke + let tcEnvAtEndOfFile = if keepAllBackgroundResolutions then tcEnv else tcState.TcEnvFromImpls + { tcInfo with tcState = tcState - tcEnvAtEndOfFile = b.tcEnvAtEndOfFile - moduleNamesDict = b.moduleNamesDict - latestCcuSigForFile = ccuSigForFile - tcDiagnosticsRev = b.tcDiagnosticsRev @ a.tcDiagnosticsRev - topAttribs = b.topAttribs - tcDependencyFiles = b.tcDependencyFiles @ a.tcDependencyFiles - // we shouldn't need this with graph checking (?) - sigNameOpt = None - }, sink, implFileOpt, name) + tcEnvAtEndOfFile = tcEnvAtEndOfFile + topAttribs = Some topAttribs + tcDiagnosticsRev = tcIntermediate.tcDiagnosticsRev @ tcInfo.tcDiagnosticsRev + tcDependencyFiles = tcIntermediate.tcDependencyFiles @ tcInfo.tcDependencyFiles + latestCcuSigForFile = Some ccuSigForFile }) + // Type check everything that is needed to check given file let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) _userOpName _key = @@ -769,28 +753,25 @@ type internal TransparentCompiler let key = projectSnapshot.UpTo(fileIndex).Key TcIntermediateCache.Get(key, ComputeTcIntermediate (parsedInput, parseErrors) bootstrapInfo tcInfo) | NodeToTypeCheck.ArtificialImplFile fileIndex -> - let parsedInput, _parseErrors, _ = parsedInputs[fileIndex] - let tcState = tcInfo.tcState - let prefixPathOpt = None - - let (tcEnv , topAttribs , checkedImplFileOpt , _moduleOrNamespaceType), newTcState = + + let finisher tcState = + let parsedInput, _parseErrors, _ = parsedInputs[fileIndex] + let prefixPathOpt = None // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. AddSignatureResultToTcImplEnv(bootstrapInfo.TcImports, bootstrapInfo.TcGlobals, prefixPathOpt, TcResultsSink.NoSink, tcState, parsedInput) tcState - let tcInfo = - { tcInfo with - tcState = newTcState - tcEnvAtEndOfFile = tcEnv - - topAttribs = Some topAttribs - // we shouldn't need this with graph checking (?) - sigNameOpt = None + let tcIntermediate = + { + finisher = finisher + moduleNamesDict = tcInfo.moduleNamesDict + tcDiagnosticsRev = [] + tcDependencyFiles = [] } - node.Return(tcInfo, Unchecked.defaultof<_>, checkedImplFileOpt, parsedInput.FileName)) + node.Return(tcIntermediate)) |> NodeCode.Parallel - let nodes = layer |> Seq.map (function NodeToTypeCheck.PhysicalFile i -> fileNames[i] | NodeToTypeCheck.ArtificialImplFile i -> $"AIF : {fileNames[i]}") |> String.concat " " - Trace.TraceInformation $"Processed layer {nodes}" - return! processLayer rest (mergeIntermediateResults bootstrapInfo (tcInfo, Unchecked.defaultof<_>, None, "") results |> p14) + //let nodes = layer |> Seq.map (function NodeToTypeCheck.PhysicalFile i -> fileNames[i] | NodeToTypeCheck.ArtificialImplFile i -> $"AIF : {fileNames[i]}") |> String.concat " " + //Trace.TraceInformation $"Processed layer {nodes}" + return! processLayer rest (mergeIntermediateResults tcInfo results) } return! processLayer layers bootstrapInfo.InitialTcInfo @@ -836,7 +817,7 @@ type internal TransparentCompiler input = parseTree, parseHadErrors = (parseDiagnostics.Length > 0), // TODO: check if we really need this in parse results - dependencyFiles = [||] + dependencyFiles = Array.ofList tcInfo.tcDependencyFiles ) let! checkResults = diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index ea679f5476d..636e7cd7a64 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -41,6 +41,8 @@ module Activity = let listenToAll () = listen "" +open Activity + [] let ``Use Transparent Compiler`` () = @@ -88,6 +90,8 @@ let ``Parallel processing`` () = ProjectWorkflowBuilder(project, useTransparentCompiler = true) { checkFile "E" expectOk + updateFile "A" updatePublicSurface + checkFile "E" expectSignatureChanged } @@ -104,9 +108,15 @@ let ``Parallel processing with signatures`` () = sourceFile "E" ["B"; "C"; "D"] |> addSignatureFile) ProjectWorkflowBuilder(project, useTransparentCompiler = true) { - checkFile "C" expectOk - //updateFile "A" updatePublicSurface - //checkFile "E" expectSignatureChanged + checkFile "E" expectOk + updateFile "A" updatePublicSurface + checkFile "E" expectNoChanges + regenerateSignature "A" + regenerateSignature "B" + regenerateSignature "C" + regenerateSignature "D" + regenerateSignature "E" + checkFile "E" expectSignatureChanged } @@ -161,6 +171,20 @@ let ``Files depend on signature file if present`` () = checkFile "Second" expectNoChanges } +[] +let ``Signature update`` () = + listenToAll () + + let project = makeTestProject() |> updateFile "First" addSignatureFile + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + updateFile "First" updatePublicSurface + saveFile "First" + checkFile "Second" expectNoChanges + regenerateSignature "First" + checkFile "Second" expectSignatureChanged + } + [] let ``Adding a file`` () = testWorkflow() { diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 0acd065dbe6..4f8a36ebf06 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -696,6 +696,21 @@ type ProjectWorkflowBuilder return project }) + [] + member this.RegenerateSignature(workflow: Async, fileId: string) = + workflow + |> mapProjectAsync (fun project -> + async { + use _ = + Activity.start "ProjectWorkflowBuilder.RegenerateSignature" [ Activity.Tags.project, project.Name; "fileId", fileId ] + let project, file = project.FindInAllProjects fileId + let! result = checkFile fileId project checker + let signature = getSignature result + let signatureFileName = getSignatureFilePath project file + writeFileIfChanged signatureFileName signature + return project + }) + /// Add a file above given file in the project. [] member this.AddFileAbove(workflow: Async, addAboveId: string, newFile) = From 2273ffe93c9f17db26256d2735b7d2d422d90dd0 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 11 May 2023 12:48:54 +0200 Subject: [PATCH 026/222] wip --- src/Compiler/Driver/ParseAndCheckInputs.fsi | 21 +++++++++---------- .../FSharpChecker/TransparentCompiler.fs | 17 ++++++++------- .../TypeChecks/Graph/GraphOperations.fs | 2 +- .../ProjectGeneration.fs | 17 ++++++++------- 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fsi b/src/Compiler/Driver/ParseAndCheckInputs.fsi index 37e76d82d48..9758fc5d0b0 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fsi +++ b/src/Compiler/Driver/ParseAndCheckInputs.fsi @@ -163,7 +163,6 @@ type TcState = member CreatesGeneratedProvidedTypes: bool - type PartialResult = TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType /// Get the initial type checking state for a set of inputs @@ -187,16 +186,16 @@ val CheckOneInput: Cancellable<(TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType) * TcState> val CheckOneInputWithCallback: - checkForErrors: (unit -> bool) * - tcConfig: TcConfig * - tcImports: TcImports * - tcGlobals: TcGlobals * - prefixPathOpt: LongIdent option * - tcSink: TcResultsSink * - tcState: TcState * - input: ParsedInput * - _skipImplIfSigExists: bool -> Cancellable> - + checkForErrors: (unit -> bool) * + tcConfig: TcConfig * + tcImports: TcImports * + tcGlobals: TcGlobals * + prefixPathOpt: LongIdent option * + tcSink: TcResultsSink * + tcState: TcState * + input: ParsedInput * + _skipImplIfSigExists: bool -> + Cancellable> val AddCheckResultsToTcState: tcGlobals: TcGlobals * diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 636e7cd7a64..d474f5f0734 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -41,8 +41,6 @@ module Activity = let listenToAll () = listen "" -open Activity - [] let ``Use Transparent Compiler`` () = @@ -173,15 +171,18 @@ let ``Files depend on signature file if present`` () = [] let ``Signature update`` () = - listenToAll () + Activity.listenToAll () - let project = makeTestProject() |> updateFile "First" addSignatureFile + let project = SyntheticProject.Create( + { sourceFile "First" [] with + Source = "let f x = x" + SignatureFile = Custom "val f: x: int -> int" }, + { sourceFile "Second" ["First"] with + Source = "let a x = ModuleFirst.f x" }) ProjectWorkflowBuilder(project, useTransparentCompiler = true) { - updateFile "First" updatePublicSurface - saveFile "First" - checkFile "Second" expectNoChanges - regenerateSignature "First" + checkFile "Second" expectOk + updateFile "First" (fun f -> { f with SignatureFile = Custom "val f: x: string -> string" }) checkFile "Second" expectSignatureChanged } diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs index dedac14ba76..b71add124ca 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs @@ -41,7 +41,7 @@ let ``See what this does`` () = sourceFile "E" ["A"; "B"; "C"] |> addSignatureFile, sourceFile "F" ["C"; "D"] |> addSignatureFile ).Workflow { - withProject (fun project checker -> + withProject (fun project checker -> async { let options = project.GetProjectOptions checker let options, _ = checker.GetParsingOptionsFromProjectOptions options diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 4f8a36ebf06..8e1eac488e8 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -23,6 +23,7 @@ open FSharp.Compiler.Diagnostics open FSharp.Compiler.Text open Xunit open System.Collections.Concurrent +open System.Text #nowarn "57" // Experimental feature use @@ -362,22 +363,24 @@ module ProjectOperations = let getFileSnapshot (project: SyntheticProject) _options (path: string) = async { - let file, filePath = + let filePath = if path.EndsWith(".fsi") then let implFilePath = path[..path.Length - 2] let f = project.FindByPath implFilePath - f, getSignatureFilePath project f + getSignatureFilePath project f else let f = project.FindByPath path - f, getFilePath project f + getFilePath project f - let dependencies = file.DependsOn |> String.concat "|" - let version = $"{file.PublicVersion}|{file.InternalVersion}|{file.FunctionName}|{file.Source.GetHashCode()}|{file.ExtraSource.GetHashCode()}|{dependencies}" + let source = getSourceText project path + use md5 = System.Security.Cryptography.MD5.Create() + let inputBytes = Encoding.UTF8.GetBytes(source.ToString()) + let hash = md5.ComputeHash(inputBytes) |> Array.map (fun b -> b.ToString("X2")) |> String.concat "" return { FileName = filePath - Version = version - GetSource = fun () -> getSourceText project path |> Task.FromResult + Version = hash + GetSource = fun () -> source |> Task.FromResult } } From ff4d5e615b7f7e67915ff8f9ffa29788822870ef Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 12 May 2023 13:55:31 +0200 Subject: [PATCH 027/222] BootstrapInfoStatic, activity reorganization --- src/Compiler/Facilities/AsyncMemoize.fs | 27 +++------- src/Compiler/Service/BackgroundCompiler.fs | 6 +++ src/Compiler/Service/FSharpCheckerResults.fs | 5 ++ src/Compiler/Service/FSharpCheckerResults.fsi | 3 ++ src/Compiler/Service/TransparentCompiler.fs | 54 ++++++++++++++----- src/Compiler/Service/service.fs | 2 + src/Compiler/Service/service.fsi | 2 + src/Compiler/Utilities/Activity.fs | 1 + src/Compiler/Utilities/Activity.fsi | 1 + .../CompilerService/AsyncMemoize.fs | 12 ++--- .../FSharpChecker/TransparentCompiler.fs | 11 ++-- 11 files changed, 80 insertions(+), 44 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 941cacb8013..8211228e711 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -3,7 +3,6 @@ namespace Internal.Utilities.Collections open FSharp.Compiler.BuildGraph open System.Threading open System.Collections.Generic -open FSharp.Compiler.Diagnostics type internal Action<'TKey, 'TValue> = | GetOrCompute of ('TKey -> NodeCode<'TValue>) * CancellationToken @@ -16,23 +15,15 @@ type internal Job<'TValue> = | Running of NodeCode<'TValue> * CancellationTokenSource | Completed of NodeCode<'TValue> -type internal JobEvent<'TKey> = - | Started of 'TKey - | Finished of 'TKey - | Canceled of 'TKey +type internal JobEventType = + | Started + | Finished + | Canceled - member this.Key = - match this with - | Started key -> key - | Finished key -> key - | Canceled key -> key - -type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobEvent<'TKey> -> unit), ?name: string) = +type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobEventType * 'TKey -> unit)) = let tok = obj () - let name = name |> Option.defaultValue "Unnamed" - let cache = MruCache<_, 'TKey, Job<'TValue>>(keepStrongly = 10, areSame = (fun (x, y) -> x = y)) @@ -76,8 +67,7 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobE Async.StartAsTask( Async.AwaitNodeCode( node { - use _ = Activity.start $"AsyncMemoize.{name}" [||] - log (Started key) + log (Started, key) let! result = computation key post key JobCompleted return result @@ -106,7 +96,7 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobE cts.Cancel() cache.RemoveAnySimilar(tok, key) requestCounts.Remove key |> ignore - log (Canceled key) + log (Canceled, key) | CancelRequest, None | CancelRequest, Some (Completed _) -> () @@ -115,10 +105,9 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobE // TODO: should we re-wrap the result? cache.Set(tok, key, (Completed job)) requestCounts.Remove key |> ignore - log (Finished key) + log (Finished, key) | JobCompleted, _ -> failwith "If this happens there's a bug" - }) member _.Get(key, computation) = diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index ddf9d3f0c04..3b1039066eb 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -150,6 +150,8 @@ type internal IBackgroundCompiler = abstract member ProjectChecked: IEvent + abstract member CacheEvent: IEvent + type ParseCacheLockToken() = interface LockToken @@ -234,6 +236,8 @@ type internal BackgroundCompiler let fileChecked = Event() let projectChecked = Event() + let cacheEvent = Event() + // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.scriptClosureCache /// Information about the derived script closure. let scriptClosureCache = @@ -1378,6 +1382,8 @@ type internal BackgroundCompiler interface IBackgroundCompiler with + member _.CacheEvent = cacheEvent.Publish + member _.BeforeBackgroundFileCheck = self.BeforeBackgroundFileCheck member _.CheckFileInProject diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 9f7973d90c7..8a92f3b229d 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -218,6 +218,11 @@ type FSharpProjectSnapshot = member this.SourceFileNames = this.SourceFiles |> List.map (fun x -> x.FileName) + member this.WithoutFileVersions = + { this with + SourceFiles = this.SourceFiles |> List.map (fun x -> { x with Version = "" }) + } + override this.ToString() = "FSharpProjectSnapshot(" + this.ProjectFileName + ")" diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 43052e69f9c..2bd4cbbd990 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -121,6 +121,9 @@ type FSharpProjectSnapshot = /// A snapshot of the same project but only up to the given file (including). member UpTo: fileName: string -> FSharpProjectSnapshot + /// A snapshot of the same project with file versions removed. + member WithoutFileVersions: FSharpProjectSnapshot + member Key: FSharpProjectSnapshotKey and [] public FSharpReferencedProjectSnapshot = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 8df2390ea49..03f70792a0d 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -94,13 +94,17 @@ type internal TransparentCompiler // Is having just one of these ok? let lexResourceManager = Lexhelp.LexResourceManager() - let ParseFileCache = AsyncMemoize(name = "ParseFile") - let ParseAndCheckFileInProjectCache = AsyncMemoize(name = "ParseAndCheckFileInProject") - let FrameworkImportsCache = AsyncMemoize(name = "FrameworkImports") - let BootstrapInfoCache = AsyncMemoize(name = "BootstrapInfo") - let TcPriorCache = AsyncMemoize(name = "TcPrior") - let TcIntermediateCache = AsyncMemoize(name = "TcIntermediate") - let DependencyGraphForLastFileCache = AsyncMemoize(name = "DependencyGraphForLastFile") + let cacheEvent = new Event() + + let ParseFileCache = + AsyncMemoize(logEvent = fun (e, ((fileName, version), _, _)) -> cacheEvent.Trigger("ParseFile", e, [| fileName; version |] )) + let ParseAndCheckFileInProjectCache = AsyncMemoize() + let FrameworkImportsCache = AsyncMemoize() + let BootstrapInfoStaticCache = AsyncMemoize() + let BootstrapInfoCache = AsyncMemoize() + let TcPriorCache = AsyncMemoize() + let TcIntermediateCache = AsyncMemoize() + let DependencyGraphForLastFileCache = AsyncMemoize() // We currently share one global dependency provider for all scripts for the FSharpChecker. // For projects, one is used per project. @@ -170,6 +174,7 @@ type internal TransparentCompiler let ComputeFrameworkImports (tcConfig: TcConfig) frameworkDLLs nonFrameworkResolutions _key = node { + use _ = Activity.start "ComputeFrameworkImports" [] let tcConfigP = TcConfigProvider.Constant tcConfig return! TcImports.BuildFrameworkTcImports(tcConfigP, frameworkDLLs, nonFrameworkResolutions) } @@ -273,8 +278,11 @@ type internal TransparentCompiler return tcImports, tcInfo } - let computeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = + /// Bootstrap info that does not depend on contents of the files + let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) _key = node { + use _ = Activity.start "ComputeBootstrapInfoStatic" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] + let useSimpleResolutionSwitch = "--simpleresolution" let commandLineArgs = projectSnapshot.OtherOptions let defaultFSharpBinariesDir = FSharpCheckerResultsSettings.defaultFSharpBinariesDir @@ -455,9 +463,9 @@ type internal TransparentCompiler let tcConfigP = TcConfigProvider.Constant tcConfig -#if !NO_TYPEPROVIDERS + #if !NO_TYPEPROVIDERS let importsInvalidatedByTypeProvider = Event() -#endif + #endif // Check for the existence of loaded sources and prepend them to the sources list if present. let sourceFiles = @@ -506,10 +514,18 @@ type internal TransparentCompiler dependencyProvider, loadClosureOpt, basicDependencies -#if !NO_TYPEPROVIDERS + #if !NO_TYPEPROVIDERS ,importsInvalidatedByTypeProvider -#endif + #endif ) + return sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt + } + + let computeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = + node { + let bootstrapStaticKey = projectSnapshot.WithoutFileVersions.Key + + let! sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt = BootstrapInfoStaticCache.Get(bootstrapStaticKey, ComputeBootstrapInfoStatic projectSnapshot) let fileSnapshots = Map [ for f in projectSnapshot.SourceFiles -> f.FileName, f ] @@ -547,6 +563,7 @@ type internal TransparentCompiler let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) _key = node { + use _ = Activity.start "ComputeBootstrapInfo" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] // Trap and report diagnostics from creation. let delayedLogger = CapturingDiagnosticsLogger("IncrementalBuilderCreation") @@ -580,6 +597,8 @@ type internal TransparentCompiler let ComputeParseFile (file: FSharpFile) bootstrapInfo _key = node { + use _ = Activity.start "ComputeParseFile" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName; Activity.Tags.version, file.Source.Version |] + let tcConfig = bootstrapInfo.TcConfig let diagnosticsLogger = @@ -599,6 +618,7 @@ type internal TransparentCompiler let ComputeDependencyGraphForLastFile parsedInputs (tcConfig: TcConfig) _key = node { + let sourceFiles: FileInProject array = parsedInputs |> Seq.toArray @@ -609,6 +629,8 @@ type internal TransparentCompiler ParsedInput = input }) + use _ = Activity.start "ComputeDependencyGraphForLastFile" [| Activity.Tags.fileName, (sourceFiles |> Array.last).FileName |] + let filePairs = FilePairMap(sourceFiles) // TODO: we will probably want to cache and re-use larger graphs if available @@ -624,6 +646,9 @@ type internal TransparentCompiler node { let input = parsedInput let fileName = input.FileName + + use _ = Activity.start "ComputeTcIntermediate" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + let tcConfig = bootstrapInfo.TcConfig let tcGlobals = bootstrapInfo.TcGlobals let tcImports = bootstrapInfo.TcImports @@ -695,6 +720,8 @@ type internal TransparentCompiler // Type check everything that is needed to check given file let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) _userOpName _key = node { + + use _ = Activity.start "ComputeTcPrior" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName |] // parse required files let files = @@ -779,6 +806,8 @@ type internal TransparentCompiler let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) userOpName _key = node { + + use _ = Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] let! bootstrapInfoOpt, creationDiags = BootstrapInfoCache.Get(projectSnapshot.Key, ComputeBootstrapInfo projectSnapshot) @@ -854,6 +883,7 @@ type internal TransparentCompiler } interface IBackgroundCompiler with + member _.CacheEvent = cacheEvent.Publish member this.BeforeBackgroundFileCheck: IEvent = backgroundCompiler.BeforeBackgroundFileCheck diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 83a005c258e..dacc23cd47f 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -586,6 +586,8 @@ type FSharpChecker member _.ProjectChecked = backgroundCompiler.ProjectChecked + member _.CacheEvent = backgroundCompiler.CacheEvent + static member ActualParseFileCount = BackgroundCompiler.ActualParseFileCount static member ActualCheckFileCount = BackgroundCompiler.ActualCheckFileCount diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index 97bde85058f..d52e0c1f44e 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -428,6 +428,8 @@ type public FSharpChecker = /// The event may be raised on a background thread. member ProjectChecked: IEvent + member internal CacheEvent: IEvent + [] static member Instance: FSharpChecker member internal FrameworkImportsCache: FrameworkImportsCache diff --git a/src/Compiler/Utilities/Activity.fs b/src/Compiler/Utilities/Activity.fs index e3b29dd6fd0..b64100a02fd 100644 --- a/src/Compiler/Utilities/Activity.fs +++ b/src/Compiler/Utilities/Activity.fs @@ -33,6 +33,7 @@ module internal Activity = let gc2 = "gc2" let outputDllFile = "outputDllFile" let buildPhase = "buildPhase" + let version = "version" let AllKnownTags = [| diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index 94784c97f00..b05f0c4ba54 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -29,6 +29,7 @@ module internal Activity = val length: string val cache: string val buildPhase: string + val version: string module Events = val cacheHit: string diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 41880ac0d96..0def4960fd6 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -35,10 +35,10 @@ let ``Basics``() = Assert.Equal(expected, result) - let groups = eventLog |> Seq.groupBy (fun e -> e.Key) |> Seq.toList + let groups = eventLog |> Seq.groupBy snd |> Seq.toList Assert.Equal(3, groups.Length) for key, events in groups do - Assert.Equal array>([| Started key; Finished key |], events |> Seq.toArray) + Assert.Equal<(JobEventType * int) array>([| Started, key; Finished, key |], events |> Seq.toArray) [] let ``We can cancel a job`` () = @@ -67,20 +67,20 @@ let ``We can cancel a job`` () = resetEvent.WaitOne() |> ignore - Assert.Equal array>([| Started key |], eventLog |> Seq.toArray ) + Assert.Equal<(JobEventType * int) array>([| Started, key |], eventLog |> Seq.toArray ) cts1.Cancel() cts2.Cancel() Thread.Sleep 10 - Assert.Equal array>([| Started key |], eventLog |> Seq.toArray ) + Assert.Equal<(JobEventType * int) array>([| Started, key |], eventLog |> Seq.toArray ) cts3.Cancel() Thread.Sleep 100 - Assert.Equal array>([| Started key; Canceled key |], eventLog |> Seq.toArray ) + Assert.Equal<(JobEventType * int) array>([| Started, key; Canceled, key |], eventLog |> Seq.toArray ) try Task.WaitAll(_task1, _task2, _task3) @@ -117,5 +117,5 @@ let ``Job keeps running even if first requestor cancels`` () = Assert.Equal(2, result) Thread.Sleep 1 // Wait for event log to be updated - Assert.Equal array>([| Started key; Finished key |], eventLog |> Seq.toArray ) + Assert.Equal<(JobEventType * int) array>([| Started, key; Finished, key |], eventLog |> Seq.toArray ) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index d474f5f0734..9951aedfdb7 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -1,14 +1,11 @@ module FSharp.Compiler.ComponentTests.FSharpChecker.TransparentCompiler -open System -open System.IO +open System.Collections.Concurrent open System.Diagnostics open Xunit open FSharp.Test.ProjectGeneration -open FSharp.Compiler.Text -open FSharp.Compiler.CodeAnalysis module Activity = let listen (filter: string) = @@ -41,7 +38,6 @@ module Activity = let listenToAll () = listen "" - [] let ``Use Transparent Compiler`` () = @@ -92,7 +88,6 @@ let ``Parallel processing`` () = checkFile "E" expectSignatureChanged } - [] let ``Parallel processing with signatures`` () = @@ -105,7 +100,10 @@ let ``Parallel processing with signatures`` () = sourceFile "D" ["A"] |> addSignatureFile, sourceFile "E" ["B"; "C"; "D"] |> addSignatureFile) + let cacheEvents = ConcurrentBag<_>() + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + withChecker (fun checker -> checker.CacheEvent.Add cacheEvents.Add) checkFile "E" expectOk updateFile "A" updatePublicSurface checkFile "E" expectNoChanges @@ -117,7 +115,6 @@ let ``Parallel processing with signatures`` () = checkFile "E" expectSignatureChanged } - let makeTestProject () = SyntheticProject.Create( sourceFile "First" [], From 0b93dd14a9d2c0dc8bebf7214c259dd105d43760 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 12 May 2023 16:32:10 +0200 Subject: [PATCH 028/222] ... --- tests/FSharp.Test.Utilities/ProjectGeneration.fs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 8e1eac488e8..2c223126984 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -680,6 +680,14 @@ type ProjectWorkflowBuilder return project }) + [] + member this.WithChecker(workflow: Async, f) = + async { + let! ctx = workflow + f checker + return ctx + } + /// Change contents of given file using `processFile` function. /// Does not save the file to disk. [] @@ -737,10 +745,11 @@ type ProjectWorkflowBuilder [] member this.CheckFile(workflow: Async, fileId: string, processResults) = async { + let! ctx = workflow + use _ = Activity.start "ProjectWorkflowBuilder.CheckFile" [ Activity.Tags.project, initialProject.Name; "fileId", fileId ] - let! ctx = workflow let! results = checkFile fileId ctx.Project checker let oldSignature = ctx.Signatures[fileId] @@ -753,10 +762,10 @@ type ProjectWorkflowBuilder member this.CheckFile(workflow: Async, fileId: string, processResults) = async { + let! ctx = workflow use _ = Activity.start "ProjectWorkflowBuilder.CheckFile" [ Activity.Tags.project, initialProject.Name; "fileId", fileId ] - let! ctx = workflow let! results = checkFile fileId ctx.Project checker let typeCheckResults = getTypeCheckResult results From c088cbbace76170941b2e0b25e3c647baf1822b7 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 15 May 2023 14:24:41 +0200 Subject: [PATCH 029/222] Local activity tracing for tests --- eng/Versions.props | 2 +- .../FSharp.Test.Utilities.fsproj | 5 ++-- .../ProjectGeneration.fs | 29 ++++++++++++++++--- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 9a57897d2ad..2f81dd45ef5 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -97,7 +97,7 @@ 4.5.1 6.0.0 1.6.0 - 6.0.0 + 7.0.0 4.5.5 4.7.0 6.0.1 diff --git a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj index 0cd32d852f9..b6c455a5499 100644 --- a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj +++ b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj @@ -66,7 +66,7 @@ - + @@ -78,6 +78,7 @@ + @@ -107,6 +108,4 @@ - - diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index cd873ddbb0d..4501af8ffc1 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -23,6 +23,10 @@ open FSharp.Compiler.Text open Xunit open System.Collections.Concurrent +open OpenTelemetry +open OpenTelemetry.Resources +open OpenTelemetry.Trace + #nowarn "57" // Experimental feature use let private projectRoot = "test-projects" @@ -546,6 +550,8 @@ type ProjectWorkflowBuilder let useChangeNotifications = defaultArg useChangeNotifications false let mutable latestProject = initialProject + let mutable activity = None + let mutable tracerProvider = None let getSource (filePath: string) = if filePath.EndsWith(".fsi") then @@ -600,10 +606,21 @@ type ProjectWorkflowBuilder member this.Checker = checker - member this.Yield _ = - match initialContext with - | Some ctx -> async.Return ctx - | _ -> SaveAndCheckProject initialProject checker + member this.Yield _ = async { + let! ctx = + match initialContext with + | Some ctx -> async.Return ctx + | _ -> SaveAndCheckProject initialProject checker + tracerProvider <- + Sdk.CreateTracerProviderBuilder() + .AddSource("fsc") + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(serviceName="F#", serviceVersion = "1")) + .AddJaegerExporter() + .Build() + |> Some + activity <- Activity.start ctx.Project.Name [ Activity.Tags.project, ctx.Project.Name ] |> Some + return ctx + } member this.DeleteProjectDir() = if Directory.Exists initialProject.ProjectDir then @@ -615,6 +632,10 @@ type ProjectWorkflowBuilder finally if initialContext.IsNone then this.DeleteProjectDir() + activity |> Option.iter (fun x -> x.Dispose()) + tracerProvider |> Option.iter (fun x -> + x.ForceFlush() |> ignore + x.Dispose()) /// Change contents of given file using `processFile` function. /// Does not save the file to disk. From 33afd1f516f5392cd8db9d7f728fcb02ada0d5a0 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 15 May 2023 17:54:26 +0200 Subject: [PATCH 030/222] wip --- src/Compiler/Facilities/AsyncMemoize.fs | 4 +- src/Compiler/Service/BackgroundCompiler.fs | 7 ++ src/Compiler/Service/TransparentCompiler.fs | 59 ++++++++++++-- src/Compiler/Service/service.fs | 5 ++ src/Compiler/Service/service.fsi | 3 + .../CompilerService/AsyncMemoize.fs | 24 +++--- .../FSharpChecker/TransparentCompiler.fs | 2 +- .../LanguageService/WorkspaceExtensions.fs | 80 +++++++++++-------- .../FSharp.Editor/Options/EditorOptions.fs | 2 +- 9 files changed, 129 insertions(+), 57 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 8211228e711..581e6201193 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -5,7 +5,7 @@ open System.Threading open System.Collections.Generic type internal Action<'TKey, 'TValue> = - | GetOrCompute of ('TKey -> NodeCode<'TValue>) * CancellationToken + | GetOrCompute of NodeCode<'TValue> * CancellationToken | CancelRequest | JobCompleted @@ -68,7 +68,7 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobE Async.AwaitNodeCode( node { log (Started, key) - let! result = computation key + let! result = computation post key JobCompleted return result } diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 3b1039066eb..72bba6b4bc3 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -134,6 +134,10 @@ type internal IBackgroundCompiler = abstract member ParseFile: fileName: string * sourceText: ISourceText * options: FSharpParsingOptions * cache: bool * userOpName: string -> Async + + abstract member ParseFile: + fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> + NodeCode /// Try to get recent approximate type check results for a file. abstract member TryGetRecentCheckResultsForFile: @@ -1530,6 +1534,9 @@ type internal BackgroundCompiler ) : Async = self.ParseFile(fileName, sourceText, options, cache, userOpName) + member _.ParseFile(_fileName: string, _projectSnapshot: FSharpProjectSnapshot, _userOpName: string) = + raise (System.NotImplementedException()) + member _.ProjectChecked: IEvent = self.ProjectChecked member _.TryGetRecentCheckResultsForFile diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 03f70792a0d..771d707e43f 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -172,7 +172,7 @@ type internal TransparentCompiler } ] - let ComputeFrameworkImports (tcConfig: TcConfig) frameworkDLLs nonFrameworkResolutions _key = + let ComputeFrameworkImports (tcConfig: TcConfig) frameworkDLLs nonFrameworkResolutions = node { use _ = Activity.start "ComputeFrameworkImports" [] let tcConfigP = TcConfigProvider.Constant tcConfig @@ -279,7 +279,7 @@ type internal TransparentCompiler } /// Bootstrap info that does not depend on contents of the files - let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) _key = + let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) = node { use _ = Activity.start "ComputeBootstrapInfoStatic" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] @@ -561,7 +561,7 @@ type internal TransparentCompiler } } - let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) _key = + let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) = node { use _ = Activity.start "ComputeBootstrapInfo" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] @@ -595,7 +595,7 @@ type internal TransparentCompiler return bootstrapInfoOpt, diagnostics } - let ComputeParseFile (file: FSharpFile) bootstrapInfo _key = + let ComputeParseFile (file: FSharpFile) bootstrapInfo = node { use _ = Activity.start "ComputeParseFile" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName; Activity.Tags.version, file.Source.Version |] @@ -616,7 +616,7 @@ type internal TransparentCompiler return input, diagnosticsLogger.GetDiagnostics(), sourceText } - let ComputeDependencyGraphForLastFile parsedInputs (tcConfig: TcConfig) _key = + let ComputeDependencyGraphForLastFile parsedInputs (tcConfig: TcConfig) = node { let sourceFiles: FileInProject array = @@ -642,7 +642,7 @@ type internal TransparentCompiler return TransformDependencyGraph(graph, filePairs), filePairs } - let ComputeTcIntermediate (parsedInput: ParsedInput, parseErrors) bootstrapInfo (prevTcInfo: TcInfo) _key = + let ComputeTcIntermediate (parsedInput: ParsedInput, parseErrors) bootstrapInfo (prevTcInfo: TcInfo) = node { let input = parsedInput let fileName = input.FileName @@ -718,7 +718,7 @@ type internal TransparentCompiler // Type check everything that is needed to check given file - let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) _userOpName _key = + let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) _userOpName = node { use _ = Activity.start "ComputeTcPrior" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName |] @@ -804,7 +804,7 @@ type internal TransparentCompiler return! processLayer layers bootstrapInfo.InitialTcInfo } - let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) userOpName _key = + let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) userOpName = node { use _ = Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] @@ -876,6 +876,46 @@ type internal TransparentCompiler return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) } + member _.ParseFile(fileName, projectSnapshot: FSharpProjectSnapshot, _userOpName) = + node { + use _ = Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + + let! bootstrapInfoOpt, creationDiags = BootstrapInfoCache.Get(projectSnapshot.Key, ComputeBootstrapInfo projectSnapshot) + + match bootstrapInfoOpt with + | None -> + let parseTree = EmptyParsedInput(fileName, (false, false)) + return FSharpParseFileResults(creationDiags, parseTree, true, [||]) + + | Some bootstrapInfo -> + + let file = + bootstrapInfo.SourceFiles |> List.tryFind (fun f -> f.Source.FileName = fileName) |> Option.defaultWith (fun _ -> failwith ($"File {fileName} not found in project snapshot. Files in project: \n\n" + (bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n"))) + + let! parseTree, parseDiagnostics, _sourceText = + ParseFileCache.Get((file.Source.Key, file.IsLastCompiland, file.IsExe), ComputeParseFile file bootstrapInfo) + + let parseDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + bootstrapInfo.TcConfig.diagnosticsOptions, + false, + fileName, + parseDiagnostics, + suggestNamesForErrors + ) + + let diagnostics = [| yield! creationDiags; yield! parseDiagnostics |] + + return + FSharpParseFileResults( + diagnostics = diagnostics, + input = parseTree, + parseHadErrors = (parseDiagnostics.Length > 0), + // TODO: check if we really need this in parse results + dependencyFiles = [||] + ) + } + member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = node { let key = fileName, projectSnapshot.Key @@ -1031,6 +1071,9 @@ type internal TransparentCompiler member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = backgroundCompiler.ParseAndCheckProject(options, userOpName) + + member this.ParseFile(fileName, projectSnapshot, userOpName) = + this.ParseFile(fileName, projectSnapshot, userOpName) member _.ParseFile ( diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index dacc23cd47f..723ddeee6b1 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -307,6 +307,11 @@ type FSharpChecker let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.ParseFile(fileName, sourceText, options, cache, userOpName) + member _.ParseFile(fileName, projectSnapshot, ?userOpName) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.ParseFile(fileName, projectSnapshot, userOpName) + |> Async.AwaitNodeCode + member ic.ParseFileInProject(fileName, source: string, options, ?cache: bool, ?userOpName: string) = let parsingOptions, _ = ic.GetParsingOptionsFromProjectOptions(options) ic.ParseFile(fileName, SourceText.ofString source, parsingOptions, ?cache = cache, ?userOpName = userOpName) diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index d52e0c1f44e..98d5c429680 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -103,6 +103,9 @@ type public FSharpChecker = fileName: string * sourceText: ISourceText * options: FSharpParsingOptions * ?cache: bool * ?userOpName: string -> Async + member ParseFile: fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> + Async + /// /// Parses a source code for a file. Returns an AST that can be traversed for various features. /// diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 0def4960fd6..80c137cc603 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -22,12 +22,12 @@ let ``Basics``() = let task = NodeCode.Parallel(seq { - memoize.Get(5, computation) - memoize.Get(5, computation) - memoize.Get(2, computation) - memoize.Get(5, computation) - memoize.Get(3, computation) - memoize.Get(2, computation) + memoize.Get(5, computation 5) + memoize.Get(5, computation 5) + memoize.Get(2, computation 2) + memoize.Get(5, computation 5) + memoize.Get(3, computation 3) + memoize.Get(2, computation 2) }) |> NodeCode.StartAsTask_ForTesting let result = task.Result @@ -61,9 +61,9 @@ let ``We can cancel a job`` () = let key = 1 - let _task1 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts1.Token) - let _task2 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts2.Token) - let _task3 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts3.Token) + let _task1 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation key), cts1.Token) + let _task2 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation key), cts2.Token) + let _task3 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation key), cts3.Token) resetEvent.WaitOne() |> ignore @@ -104,9 +104,9 @@ let ``Job keeps running even if first requestor cancels`` () = let key = 1 - let _task1 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts1.Token) - let _task2 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts2.Token) - let _task3 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation), cts3.Token) + let _task1 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation key), cts1.Token) + let _task2 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation key), cts2.Token) + let _task3 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation key), cts3.Token) Thread.Sleep 10 diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 9951aedfdb7..d50980ce575 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -206,7 +206,7 @@ let ``Changes in a referenced project`` () = { makeTestProject() with DependsOn = [library] } |> updateFile "First" (addDependency "Library") - project.Workflow { + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { updateFile "Library" updatePublicSurface saveFile "Library" checkFile "Last" expectSignatureChanged diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 4e9f681dc96..5e623b51469 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -13,6 +13,40 @@ open FSharp.Compiler.Symbols [] module private CheckerExtensions = + let getProjectSnapshot (document: Document, options: FSharpProjectOptions) = + async { + let project = document.Project + let solution = project.Solution + // TODO cache? + let projects = + solution.Projects + |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) + |> Map + + let getFileSnapshot (options: FSharpProjectOptions) path = + async { + let project = projects[options.ProjectFileName] + let document = project[path] + let! version = document.GetTextVersionAsync() |> Async.AwaitTask + + let getSource () = + task { + let! sourceText = document.GetTextAsync() + return sourceText.ToFSharpSourceText() + } + + return + { + FileName = path + Version = version.ToString() + GetSource = getSource + } + } + + return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + } + + type FSharpChecker with /// Parse the source text from the Roslyn document. @@ -24,6 +58,12 @@ module private CheckerExtensions = return! checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName = userOpName) } + member checker.ParseDocumentUsingTransparentCompiler(document: Document, options: FSharpProjectOptions, userOpName: string) = + async { + let! projectSnapshot = getProjectSnapshot(document, options) + return! checker.ParseFile(document.FilePath, projectSnapshot, userOpName = userOpName) + } + member checker.ParseAndCheckDocumentUsingTransparentCompiler ( document: Document, @@ -31,36 +71,7 @@ module private CheckerExtensions = userOpName: string ) = async { - - let project = document.Project - let solution = project.Solution - // TODO cache? - let projects = - solution.Projects - |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) - |> Map - - let getFileSnapshot (options: FSharpProjectOptions) path = - async { - let project = projects[options.ProjectFileName] - let document = project[path] - let! version = document.GetTextVersionAsync() |> Async.AwaitTask - - let getSource () = - task { - let! sourceText = document.GetTextAsync() - return sourceText.ToFSharpSourceText() - } - - return - { - FileName = path - Version = version.ToString() - GetSource = getSource - } - } - - let! projectSnapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + let! projectSnapshot = getProjectSnapshot(document, options) let! (parseResults, checkFileAnswer) = checker.ParseAndCheckFileInProject(document.FilePath, projectSnapshot, userOpName) @@ -152,7 +163,7 @@ module private CheckerExtensions = ) = async { - if document.Project.UseTransparentCompiiler then + if document.Project.UseTransparentCompiler then return! checker.ParseAndCheckDocumentUsingTransparentCompiler(document, options, userOpName) else let allowStaleResults = @@ -251,8 +262,11 @@ type Document with /// Parses the given F# document. member this.GetFSharpParseResultsAsync(userOpName) = async { - let! checker, _, parsingOptions, _ = this.GetFSharpCompilationOptionsAsync(userOpName) - return! checker.ParseDocument(this, parsingOptions, userOpName) + let! checker, _, parsingOptions, options = this.GetFSharpCompilationOptionsAsync(userOpName) + if this.Project.UseTransparentCompiler then + return! checker.ParseDocumentUsingTransparentCompiler(this, options, userOpName) + else + return! checker.ParseDocument(this, parsingOptions, userOpName) } /// Parses and checks the given F# document. diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index cd07b46b18e..bcc51aa6561 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -241,5 +241,5 @@ module EditorOptionsExtensions = member this.IsFastFindReferencesEnabled = this.EditorOptions.LanguageServicePerformance.EnableFastFindReferencesAndRename - member this.UseTransparentCompiiler = + member this.UseTransparentCompiler = this.EditorOptions.Advanced.UseTransparentCompiler From 83ee2043bb15bc75c3dcd2833ad6491d0e7f1dc9 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 16 May 2023 17:17:21 +0200 Subject: [PATCH 031/222] wip --- src/Compiler/Service/BackgroundCompiler.fs | 5 +- src/Compiler/Service/service.fs | 1 + src/Compiler/Service/service.fsi | 4 +- .../FSharpChecker/TransparentCompiler.fs | 37 ----------- ...ervice.SurfaceArea.netstandard20.debug.bsl | 3 + ...vice.SurfaceArea.netstandard20.release.bsl | 3 + .../ProjectGeneration.fs | 15 +++-- .../LanguageService/LanguageService.fs | 3 +- .../LanguageService/WorkspaceExtensions.fs | 64 +++++++++---------- .../FSharp.Editor/Options/EditorOptions.fs | 3 +- 10 files changed, 56 insertions(+), 82 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 72bba6b4bc3..a7e6924d3cb 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -134,10 +134,9 @@ type internal IBackgroundCompiler = abstract member ParseFile: fileName: string * sourceText: ISourceText * options: FSharpParsingOptions * cache: bool * userOpName: string -> Async - + abstract member ParseFile: - fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> - NodeCode + fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> NodeCode /// Try to get recent approximate type check results for a file. abstract member TryGetRecentCheckResultsForFile: diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 723ddeee6b1..a12bfdccdf8 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -309,6 +309,7 @@ type FSharpChecker member _.ParseFile(fileName, projectSnapshot, ?userOpName) = let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.ParseFile(fileName, projectSnapshot, userOpName) |> Async.AwaitNodeCode diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index 98d5c429680..fe484532dfa 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -103,8 +103,8 @@ type public FSharpChecker = fileName: string * sourceText: ISourceText * options: FSharpParsingOptions * ?cache: bool * ?userOpName: string -> Async - member ParseFile: fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> - Async + member ParseFile: + fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async /// /// Parses a source code for a file. Returns an AST that can be traversed for various features. diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index d50980ce575..23edbee1cfe 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -7,42 +7,10 @@ open Xunit open FSharp.Test.ProjectGeneration -module Activity = - let listen (filter: string) = - let indent (activity: Activity) = - let rec loop (activity: Activity) n = - if activity.Parent <> null then - loop (activity.Parent) (n + 1) - else - n - - String.replicate (loop activity 0) " " - - let collectTags (activity: Activity) = - [ for tag in activity.Tags -> $"{tag.Key}: %A{tag.Value}" ] - |> String.concat ", " - - let listener = - new ActivityListener( - ShouldListenTo = (fun source -> source.Name = FSharp.Compiler.Diagnostics.ActivityNames.FscSourceName), - Sample = - (fun context -> - if context.Name.Contains(filter) then - ActivitySamplingResult.AllDataAndRecorded - else - ActivitySamplingResult.None), - ActivityStarted = (fun a -> Trace.TraceInformation $"{indent a}{a.OperationName} {collectTags a}") - ) - - ActivitySource.AddActivityListener(listener) - - let listenToAll () = listen "" [] let ``Use Transparent Compiler`` () = - Activity.listenToAll () - let size = 20 let project = @@ -73,8 +41,6 @@ let ``Use Transparent Compiler`` () = [] let ``Parallel processing`` () = - Activity.listenToAll () - let project = SyntheticProject.Create( sourceFile "A" [], sourceFile "B" ["A"], @@ -91,8 +57,6 @@ let ``Parallel processing`` () = [] let ``Parallel processing with signatures`` () = - Activity.listenToAll () - let project = SyntheticProject.Create( sourceFile "A" [] |> addSignatureFile, sourceFile "B" ["A"] |> addSignatureFile, @@ -168,7 +132,6 @@ let ``Files depend on signature file if present`` () = [] let ``Signature update`` () = - Activity.listenToAll () let project = SyntheticProject.Create( { sourceFile "First" [] with diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 011026eee11..03aeafb299a 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2055,6 +2055,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualParseFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer] CheckFileInProject(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] GetBackgroundParseResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpParsingOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFileInProject(System.String, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] CheckFileInProjectAllowingStaleCachedResults(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2200,6 +2201,8 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_UseScriptResolut FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions ToOptions() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(Int32) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(System.String) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot WithoutFileVersions +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot get_WithoutFileVersions() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey Key FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey get_Key() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode() diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 011026eee11..03aeafb299a 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2055,6 +2055,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualParseFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer] CheckFileInProject(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] GetBackgroundParseResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpParsingOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFileInProject(System.String, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] CheckFileInProjectAllowingStaleCachedResults(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2200,6 +2201,8 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_UseScriptResolut FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions ToOptions() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(Int32) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(System.String) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot WithoutFileVersions +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot get_WithoutFileVersions() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey Key FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey get_Key() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode() diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 74356203b27..29e845d1ed8 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -126,6 +126,11 @@ type SyntheticProject = |> List.tryFind (fun f -> this.ProjectDir ++ f.FileName = path) |> Option.defaultWith (fun () -> failwith $"File {path} not found in project {this.Name}.") + member this.FindInAllProjectsByPath path = + this.GetAllFiles() + |> List.tryFind (fun (p, f) -> p.ProjectDir ++ f.FileName = path) + |> Option.defaultWith (fun () -> failwith $"File {path} not found in any project.") + member this.ProjectFileName = this.ProjectDir ++ $"{this.Name}.fsproj" member this.OutputFilename = this.ProjectDir ++ $"{this.Name}.dll" @@ -367,14 +372,14 @@ module ProjectOperations = let getFileSnapshot (project: SyntheticProject) _options (path: string) = async { - let filePath = + let project, filePath = if path.EndsWith(".fsi") then let implFilePath = path[..path.Length - 2] - let f = project.FindByPath implFilePath - getSignatureFilePath project f + let p, f = project.FindInAllProjectsByPath implFilePath + p, getSignatureFilePath p f else - let f = project.FindByPath path - getFilePath project f + let p, f = project.FindInAllProjectsByPath path + p, getFilePath p f let source = getSourceText project path use md5 = System.Security.Cryptography.MD5.Create() diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 1501fac454a..5d4ebce7420 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -177,7 +177,8 @@ type internal FSharpWorkspaceServiceFactory [ diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 5e623b51469..15304a77a34 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -15,38 +15,37 @@ module private CheckerExtensions = let getProjectSnapshot (document: Document, options: FSharpProjectOptions) = async { - let project = document.Project - let solution = project.Solution - // TODO cache? - let projects = - solution.Projects - |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) - |> Map - - let getFileSnapshot (options: FSharpProjectOptions) path = - async { - let project = projects[options.ProjectFileName] - let document = project[path] - let! version = document.GetTextVersionAsync() |> Async.AwaitTask - - let getSource () = - task { - let! sourceText = document.GetTextAsync() - return sourceText.ToFSharpSourceText() - } - - return - { - FileName = path - Version = version.ToString() - GetSource = getSource - } - } - - return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + let project = document.Project + let solution = project.Solution + // TODO cache? + let projects = + solution.Projects + |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) + |> Map + + let getFileSnapshot (options: FSharpProjectOptions) path = + async { + let project = projects[options.ProjectFileName] + let document = project[path] + let! version = document.GetTextVersionAsync() |> Async.AwaitTask + + let getSource () = + task { + let! sourceText = document.GetTextAsync() + return sourceText.ToFSharpSourceText() + } + + return + { + FileName = path + Version = version.ToString() + GetSource = getSource + } + } + + return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) } - type FSharpChecker with /// Parse the source text from the Roslyn document. @@ -60,7 +59,7 @@ module private CheckerExtensions = member checker.ParseDocumentUsingTransparentCompiler(document: Document, options: FSharpProjectOptions, userOpName: string) = async { - let! projectSnapshot = getProjectSnapshot(document, options) + let! projectSnapshot = getProjectSnapshot (document, options) return! checker.ParseFile(document.FilePath, projectSnapshot, userOpName = userOpName) } @@ -71,7 +70,7 @@ module private CheckerExtensions = userOpName: string ) = async { - let! projectSnapshot = getProjectSnapshot(document, options) + let! projectSnapshot = getProjectSnapshot (document, options) let! (parseResults, checkFileAnswer) = checker.ParseAndCheckFileInProject(document.FilePath, projectSnapshot, userOpName) @@ -263,6 +262,7 @@ type Document with member this.GetFSharpParseResultsAsync(userOpName) = async { let! checker, _, parsingOptions, options = this.GetFSharpCompilationOptionsAsync(userOpName) + if this.Project.UseTransparentCompiler then return! checker.ParseDocumentUsingTransparentCompiler(this, options, userOpName) else diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index b965e5595d8..ee9b6c65b65 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -243,5 +243,4 @@ module EditorOptionsExtensions = member this.IsFastFindReferencesEnabled = this.EditorOptions.LanguageServicePerformance.EnableFastFindReferencesAndRename - member this.UseTransparentCompiler = - this.EditorOptions.Advanced.UseTransparentCompiler + member this.UseTransparentCompiler = this.EditorOptions.Advanced.UseTransparentCompiler From 348d6203fabeac70a7e7d2abf0b9f98d1423b1af Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 16 May 2023 18:15:10 +0200 Subject: [PATCH 032/222] move cache key creation --- src/Compiler/Service/TransparentCompiler.fs | 170 +++++++++----------- 1 file changed, 79 insertions(+), 91 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 771d707e43f..50daf0beafd 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -96,7 +96,7 @@ type internal TransparentCompiler let cacheEvent = new Event() - let ParseFileCache = + let ParseFileCache = AsyncMemoize(logEvent = fun (e, ((fileName, version), _, _)) -> cacheEvent.Trigger("ParseFile", e, [| fileName; version |] )) let ParseAndCheckFileInProjectCache = AsyncMemoize() let FrameworkImportsCache = AsyncMemoize() @@ -173,11 +173,28 @@ type internal TransparentCompiler ] let ComputeFrameworkImports (tcConfig: TcConfig) frameworkDLLs nonFrameworkResolutions = - node { + let frameworkDLLsKey = + frameworkDLLs + |> List.map (fun ar -> ar.resolvedPath) // The cache key. Just the minimal data. + |> List.sort // Sort to promote cache hits. + + // The data elements in this key are very important. There should be nothing else in the TcConfig that logically affects + // the import of a set of framework DLLs into F# CCUs. That is, the F# CCUs that result from a set of DLLs (including + // FSharp.Core.dll and mscorlib.dll) must be logically invariant of all the other compiler configuration parameters. + let key = + FrameworkImportsCacheKey( + frameworkDLLsKey, + tcConfig.primaryAssembly.Name, + tcConfig.GetTargetFrameworkDirectories(), + tcConfig.fsharpBinariesDir, + tcConfig.langVersion.SpecifiedVersion + ) + + FrameworkImportsCache.Get(key, node { use _ = Activity.start "ComputeFrameworkImports" [] let tcConfigP = TcConfigProvider.Constant tcConfig return! TcImports.BuildFrameworkTcImports(tcConfigP, frameworkDLLs, nonFrameworkResolutions) - } + }) // Link all the assemblies together and produce the input typecheck accumulator let CombineImportedAssembliesTask @@ -280,7 +297,10 @@ type internal TransparentCompiler /// Bootstrap info that does not depend on contents of the files let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) = - node { + + let key = projectSnapshot.WithoutFileVersions.Key + + BootstrapInfoStaticCache.Get(key, node { use _ = Activity.start "ComputeBootstrapInfoStatic" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] let useSimpleResolutionSwitch = "--simpleresolution" @@ -413,27 +433,8 @@ type internal TransparentCompiler let frameworkDLLs, nonFrameworkResolutions, unresolvedReferences = TcAssemblyResolutions.SplitNonFoundationalResolutions(tcConfig) - let frameworkDLLsKey = - frameworkDLLs - |> List.map (fun ar -> ar.resolvedPath) // The cache key. Just the minimal data. - |> List.sort // Sort to promote cache hits. - // Prepare the frameworkTcImportsCache - // - // The data elements in this key are very important. There should be nothing else in the TcConfig that logically affects - // the import of a set of framework DLLs into F# CCUs. That is, the F# CCUs that result from a set of DLLs (including - // FSharp.Core.dll and mscorlib.dll) must be logically invariant of all the other compiler configuration parameters. - let key = - FrameworkImportsCacheKey( - frameworkDLLsKey, - tcConfig.primaryAssembly.Name, - tcConfig.GetTargetFrameworkDirectories(), - tcConfig.fsharpBinariesDir, - tcConfig.langVersion.SpecifiedVersion - ) - - let! tcGlobals, frameworkTcImports = - FrameworkImportsCache.Get(key, ComputeFrameworkImports tcConfig frameworkDLLs nonFrameworkResolutions) + let! tcGlobals, frameworkTcImports = ComputeFrameworkImports tcConfig frameworkDLLs nonFrameworkResolutions // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. // This is ok because not much can actually go wrong here. @@ -519,13 +520,11 @@ type internal TransparentCompiler #endif ) return sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt - } + }) let computeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = node { - let bootstrapStaticKey = projectSnapshot.WithoutFileVersions.Key - - let! sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt = BootstrapInfoStaticCache.Get(bootstrapStaticKey, ComputeBootstrapInfoStatic projectSnapshot) + let! sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt = ComputeBootstrapInfoStatic projectSnapshot let fileSnapshots = Map [ for f in projectSnapshot.SourceFiles -> f.FileName, f ] @@ -562,7 +561,7 @@ type internal TransparentCompiler } let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) = - node { + BootstrapInfoCache.Get(projectSnapshot.Key, node { use _ = Activity.start "ComputeBootstrapInfo" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] // Trap and report diagnostics from creation. @@ -593,12 +592,13 @@ type internal TransparentCompiler FSharpDiagnostic.CreateFromException(diagnostic, severity, range.Zero, suggestNamesForErrors)) return bootstrapInfoOpt, diagnostics - } + }) - let ComputeParseFile (file: FSharpFile) bootstrapInfo = - node { + let ComputeParseFile bootstrapInfo (file: FSharpFile) = + let key = file.Source.Key, file.IsLastCompiland, file.IsExe + ParseFileCache.Get(key, node { use _ = Activity.start "ComputeParseFile" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName; Activity.Tags.version, file.Source.Version |] - + let tcConfig = bootstrapInfo.TcConfig let diagnosticsLogger = @@ -614,10 +614,11 @@ type internal TransparentCompiler ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) return input, diagnosticsLogger.GetDiagnostics(), sourceText - } + }) - let ComputeDependencyGraphForLastFile parsedInputs (tcConfig: TcConfig) = - node { + let ComputeDependencyGraphForLastFile (priorSnapshot: FSharpProjectSnapshot) parsedInputs (tcConfig: TcConfig) = + let key = priorSnapshot.SourceFiles |> List.map (fun s -> s.Key) + DependencyGraphForLastFileCache.Get(key, node { let sourceFiles: FileInProject array = parsedInputs @@ -629,8 +630,8 @@ type internal TransparentCompiler ParsedInput = input }) - use _ = Activity.start "ComputeDependencyGraphForLastFile" [| Activity.Tags.fileName, (sourceFiles |> Array.last).FileName |] - + use _ = Activity.start "ComputeDependencyGraphForLastFile" [| Activity.Tags.fileName, (sourceFiles |> Array.last).FileName |] + let filePairs = FilePairMap(sourceFiles) // TODO: we will probably want to cache and re-use larger graphs if available @@ -639,16 +640,18 @@ type internal TransparentCompiler |> fst |> Graph.subGraphFor (sourceFiles |> Array.last).Idx - return TransformDependencyGraph(graph, filePairs), filePairs - } + return TransformDependencyGraph(graph, filePairs) + }) - let ComputeTcIntermediate (parsedInput: ParsedInput, parseErrors) bootstrapInfo (prevTcInfo: TcInfo) = - node { + let ComputeTcIntermediate (projectSnapshot: FSharpProjectSnapshot) (fileIndex: FileIndex) (parsedInput: ParsedInput, parseErrors) bootstrapInfo (prevTcInfo: TcInfo) = + // TODO: we need to construct cache key based on only relevant files from the dependency graph + let key = projectSnapshot.UpTo(fileIndex).Key + TcIntermediateCache.Get(key, node { let input = parsedInput let fileName = input.FileName - + use _ = Activity.start "ComputeTcIntermediate" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - + let tcConfig = bootstrapInfo.TcConfig let tcGlobals = bootstrapInfo.TcGlobals let tcImports = bootstrapInfo.TcImports @@ -702,10 +705,10 @@ type internal TransparentCompiler tcDiagnosticsRev = [ newErrors ] tcDependencyFiles = [ fileName ] } - } + }) let mergeIntermediateResults = - Array.fold (fun (tcInfo: TcInfo) (tcIntermediate: TcIntermediate) -> + Array.fold (fun (tcInfo: TcInfo) (tcIntermediate: TcIntermediate) -> let (tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile), tcState = tcInfo.tcState |> tcIntermediate.finisher.Invoke let tcEnvAtEndOfFile = if keepAllBackgroundResolutions then tcEnv else tcState.TcEnvFromImpls { tcInfo with @@ -716,11 +719,13 @@ type internal TransparentCompiler tcDependencyFiles = tcIntermediate.tcDependencyFiles @ tcInfo.tcDependencyFiles latestCcuSigForFile = Some ccuSigForFile }) - // Type check everything that is needed to check given file let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) _userOpName = - node { - + + let priorSnapshot = projectSnapshot.UpTo file.Source.FileName + let key = priorSnapshot.Key + + TcPriorCache.Get(key, node { use _ = Activity.start "ComputeTcPrior" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName |] // parse required files @@ -732,28 +737,17 @@ type internal TransparentCompiler let! parsedInputs = files - |> Seq.map (fun f -> - let key = f.Source.Key, f.IsLastCompiland, f.IsExe - ParseFileCache.Get(key, ComputeParseFile f bootstrapInfo)) + |> Seq.map (ComputeParseFile bootstrapInfo) |> NodeCode.Parallel - // compute dependency graph - let graphKey = - projectSnapshot.UpTo(file.Source.FileName).SourceFiles - |> List.map (fun s -> s.Key) - - let! graph, _filePairs = - DependencyGraphForLastFileCache.Get( - graphKey, - ComputeDependencyGraphForLastFile (parsedInputs |> Seq.map p13) bootstrapInfo.TcConfig - ) + let! graph = ComputeDependencyGraphForLastFile priorSnapshot (parsedInputs |> Seq.map p13) bootstrapInfo.TcConfig - let fileNames = - parsedInputs + let fileNames = + parsedInputs |> Seq.mapi (fun idx (input, _, _) -> idx, Path.GetFileName input.FileName) |> Map.ofSeq - let debugGraph = + let debugGraph = graph |> Graph.map (function NodeToTypeCheck.PhysicalFile i -> i, fileNames[i] | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF : {fileNames[i]}") @@ -777,11 +771,10 @@ type internal TransparentCompiler match fileNode with | NodeToTypeCheck.PhysicalFile fileIndex -> let parsedInput, parseErrors, _ = parsedInputs[fileIndex] - let key = projectSnapshot.UpTo(fileIndex).Key - TcIntermediateCache.Get(key, ComputeTcIntermediate (parsedInput, parseErrors) bootstrapInfo tcInfo) + ComputeTcIntermediate projectSnapshot fileIndex (parsedInput, parseErrors) bootstrapInfo tcInfo | NodeToTypeCheck.ArtificialImplFile fileIndex -> - let finisher tcState = + let finisher tcState = let parsedInput, _parseErrors, _ = parsedInputs[fileIndex] let prefixPathOpt = None // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. @@ -796,20 +789,20 @@ type internal TransparentCompiler node.Return(tcIntermediate)) |> NodeCode.Parallel - //let nodes = layer |> Seq.map (function NodeToTypeCheck.PhysicalFile i -> fileNames[i] | NodeToTypeCheck.ArtificialImplFile i -> $"AIF : {fileNames[i]}") |> String.concat " " - //Trace.TraceInformation $"Processed layer {nodes}" + return! processLayer rest (mergeIntermediateResults tcInfo results) } return! processLayer layers bootstrapInfo.InitialTcInfo - } + }) let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) userOpName = - node { - + let key = fileName, projectSnapshot.Key + ParseAndCheckFileInProjectCache.Get(key, node { + use _ = Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - let! bootstrapInfoOpt, creationDiags = BootstrapInfoCache.Get(projectSnapshot.Key, ComputeBootstrapInfo projectSnapshot) + let! bootstrapInfoOpt, creationDiags = ComputeBootstrapInfo projectSnapshot match bootstrapInfoOpt with | None -> @@ -822,12 +815,10 @@ type internal TransparentCompiler let file = bootstrapInfo.SourceFiles |> List.tryFind (fun f -> f.Source.FileName = fileName) |> Option.defaultWith (fun _ -> failwith ($"File {fileName} not found in project snapshot. Files in project: \n\n" + (bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n"))) - let priorSnapshot = projectSnapshot.UpTo fileName - let! tcInfo = TcPriorCache.Get(priorSnapshot.Key, ComputeTcPrior file bootstrapInfo priorSnapshot userOpName) + let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot userOpName // We could also bubble this through ComputeTcPrior - let! parseTree, parseDiagnostics, sourceText = - ParseFileCache.Get((file.Source.Key, file.IsLastCompiland, file.IsExe), ComputeParseFile file bootstrapInfo) + let! parseTree, parseDiagnostics, sourceText = ComputeParseFile bootstrapInfo file let parseDiagnostics = DiagnosticHelpers.CreateDiagnostics( @@ -874,13 +865,13 @@ type internal TransparentCompiler |> NodeCode.FromCancellable return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) - } + }) - member _.ParseFile(fileName, projectSnapshot: FSharpProjectSnapshot, _userOpName) = + member _.ParseFile(fileName, projectSnapshot: FSharpProjectSnapshot, _userOpName) = node { use _ = Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - - let! bootstrapInfoOpt, creationDiags = BootstrapInfoCache.Get(projectSnapshot.Key, ComputeBootstrapInfo projectSnapshot) + + let! bootstrapInfoOpt, creationDiags = ComputeBootstrapInfo projectSnapshot match bootstrapInfoOpt with | None -> @@ -888,12 +879,11 @@ type internal TransparentCompiler return FSharpParseFileResults(creationDiags, parseTree, true, [||]) | Some bootstrapInfo -> - + let file = bootstrapInfo.SourceFiles |> List.tryFind (fun f -> f.Source.FileName = fileName) |> Option.defaultWith (fun _ -> failwith ($"File {fileName} not found in project snapshot. Files in project: \n\n" + (bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n"))) - let! parseTree, parseDiagnostics, _sourceText = - ParseFileCache.Get((file.Source.Key, file.IsLastCompiland, file.IsExe), ComputeParseFile file bootstrapInfo) + let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file let parseDiagnostics = DiagnosticHelpers.CreateDiagnostics( @@ -917,10 +907,8 @@ type internal TransparentCompiler } member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = - node { - let key = fileName, projectSnapshot.Key - return! ParseAndCheckFileInProjectCache.Get(key, ComputeParseAndCheckFileInProject fileName projectSnapshot userOpName) - } + ComputeParseAndCheckFileInProject fileName projectSnapshot userOpName + interface IBackgroundCompiler with member _.CacheEvent = cacheEvent.Publish @@ -1071,8 +1059,8 @@ type internal TransparentCompiler member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = backgroundCompiler.ParseAndCheckProject(options, userOpName) - - member this.ParseFile(fileName, projectSnapshot, userOpName) = + + member this.ParseFile(fileName, projectSnapshot, userOpName) = this.ParseFile(fileName, projectSnapshot, userOpName) member _.ParseFile From 35135ceb8f676872404a380e808978c76c218c30 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 17 May 2023 16:52:57 +0200 Subject: [PATCH 033/222] Background semantic classification --- src/Compiler/Service/BackgroundCompiler.fs | 16 +++- src/Compiler/Service/FSharpCheckerResults.fs | 11 ++- src/Compiler/Service/FSharpCheckerResults.fsi | 4 + src/Compiler/Service/TransparentCompiler.fs | 79 ++++++++++++++----- src/Compiler/Service/service.fs | 6 ++ src/Compiler/Service/service.fsi | 13 ++- .../FSharpChecker/TransparentCompiler.fs | 34 +++++++- ...vice.SurfaceArea.netstandard20.release.bsl | 4 + .../LanguageService/WorkspaceExtensions.fs | 13 ++- 9 files changed, 152 insertions(+), 28 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index a7e6924d3cb..0fcf7918cc2 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -113,6 +113,10 @@ type internal IBackgroundCompiler = fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode + abstract member GetSemanticClassificationForFile: + fileName: string * snapshot: FSharpProjectSnapshot * userOpName: string -> + NodeCode + abstract member InvalidateConfiguration: options: FSharpProjectOptions * userOpName: string -> unit abstract member NotifyFileChanged: fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode @@ -153,7 +157,7 @@ type internal IBackgroundCompiler = abstract member ProjectChecked: IEvent - abstract member CacheEvent: IEvent + abstract member CacheEvent: IEvent type ParseCacheLockToken() = interface LockToken @@ -239,7 +243,7 @@ type internal BackgroundCompiler let fileChecked = Event() let projectChecked = Event() - let cacheEvent = Event() + let cacheEvent = Event() // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.scriptClosureCache /// Information about the derived script closure. @@ -1493,6 +1497,14 @@ type internal BackgroundCompiler ) : NodeCode = self.GetSemanticClassificationForFile(fileName, options, userOpName) + member _.GetSemanticClassificationForFile + ( + fileName: string, + snapshot: FSharpProjectSnapshot, + userOpName: string + ) : NodeCode = + self.GetSemanticClassificationForFile(fileName, snapshot.ToOptions(), userOpName) + member _.InvalidateConfiguration(options: FSharpProjectOptions, userOpName: string) : unit = self.InvalidateConfiguration(options, userOpName) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 673e08d0807..ab2d2f554d5 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -129,6 +129,8 @@ type FSharpProjectSnapshotKey = IsIncompleteTypeCheckEnvironment: bool UseScriptResolutionRules: bool } + member this.LastFile = this.SourceFiles |> List.last + [] type FSharpFileSnapshot = @@ -194,14 +196,17 @@ type FSharpProjectSnapshot = member po.ProjectDirectory = Path.GetDirectoryName(po.ProjectFileName) + member this.IndexOf fileName = + this.SourceFiles + |> List.tryFindIndex (fun x -> x.FileName = fileName) + |> Option.defaultWith (fun () -> failwith (sprintf "Unable to find file %s in project %s" fileName this.ProjectFileName)) + member this.UpTo fileIndex = { this with SourceFiles = this.SourceFiles[..fileIndex] } - member this.UpTo fileName = - let fileIndex = this.SourceFiles |> List.findIndex (fun x -> x.FileName = fileName) - this.UpTo fileIndex + member this.UpTo fileName = this.UpTo (this.IndexOf fileName) member this.Key = { diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 2bd4cbbd990..ea55a296ec4 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -52,6 +52,8 @@ type FSharpProjectSnapshotKey = IsIncompleteTypeCheckEnvironment: bool UseScriptResolutionRules: bool } + member LastFile: FSharpFileKey + [] type FSharpFileSnapshot = { FileName: string @@ -115,6 +117,8 @@ type FSharpProjectSnapshot = member SourceFileNames: string list + member IndexOf: fileName: string -> FileIndex + /// A snapshot of the same project but only up to the given file index (including). member UpTo: fileIndex: int -> FSharpProjectSnapshot diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 50daf0beafd..0ccd19d7846 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -34,6 +34,7 @@ open FSharp.Compiler.NameResolution open Internal.Utilities.Library.Extras open FSharp.Compiler.TypedTree open FSharp.Compiler.CheckDeclarations +open FSharp.Compiler.EditorServices type internal FSharpFile = { @@ -71,6 +72,8 @@ type internal TcIntermediate = tcDiagnosticsRev:(PhasedDiagnostic * FSharpDiagnosticSeverity)[] list tcDependencyFiles: string list + + sink: TcResultsSinkImpl } type internal TransparentCompiler @@ -94,17 +97,19 @@ type internal TransparentCompiler // Is having just one of these ok? let lexResourceManager = Lexhelp.LexResourceManager() - let cacheEvent = new Event() + let cacheEvent = new Event() + let triggerCacheEvent name (e, k) = cacheEvent.Trigger(name, e, k) let ParseFileCache = - AsyncMemoize(logEvent = fun (e, ((fileName, version), _, _)) -> cacheEvent.Trigger("ParseFile", e, [| fileName; version |] )) - let ParseAndCheckFileInProjectCache = AsyncMemoize() - let FrameworkImportsCache = AsyncMemoize() - let BootstrapInfoStaticCache = AsyncMemoize() - let BootstrapInfoCache = AsyncMemoize() - let TcPriorCache = AsyncMemoize() - let TcIntermediateCache = AsyncMemoize() - let DependencyGraphForLastFileCache = AsyncMemoize() + AsyncMemoize(triggerCacheEvent "ParseFile") + let ParseAndCheckFileInProjectCache = AsyncMemoize(triggerCacheEvent "ParseAndCheckFileInProject") + let FrameworkImportsCache = AsyncMemoize(triggerCacheEvent "FrameworkImports") + let BootstrapInfoStaticCache = AsyncMemoize(triggerCacheEvent "BootstrapInfoStatic") + let BootstrapInfoCache = AsyncMemoize(triggerCacheEvent "BootstrapInfo") + let TcPriorCache = AsyncMemoize(triggerCacheEvent "TcPrior") + let TcIntermediateCache = AsyncMemoize(triggerCacheEvent "TcIntermediate") + let DependencyGraphForLastFileCache = AsyncMemoize(triggerCacheEvent "DependencyGraphForLastFile") + let SemanticClassificationCache = AsyncMemoize(triggerCacheEvent "SemanticClassification") // We currently share one global dependency provider for all scripts for the FSharpChecker. // For projects, one is used per project. @@ -704,6 +709,7 @@ type internal TransparentCompiler moduleNamesDict = moduleNamesDict tcDiagnosticsRev = [ newErrors ] tcDependencyFiles = [ fileName ] + sink = sink } }) @@ -720,7 +726,7 @@ type internal TransparentCompiler latestCcuSigForFile = Some ccuSigForFile }) // Type check everything that is needed to check given file - let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) _userOpName = + let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = let priorSnapshot = projectSnapshot.UpTo file.Source.FileName let key = priorSnapshot.Key @@ -785,6 +791,7 @@ type internal TransparentCompiler moduleNamesDict = tcInfo.moduleNamesDict tcDiagnosticsRev = [] tcDependencyFiles = [] + sink = Unchecked.defaultof<_> } node.Return(tcIntermediate)) @@ -796,26 +803,24 @@ type internal TransparentCompiler return! processLayer layers bootstrapInfo.InitialTcInfo }) - let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) userOpName = + let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = let key = fileName, projectSnapshot.Key ParseAndCheckFileInProjectCache.Get(key, node { use _ = Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - let! bootstrapInfoOpt, creationDiags = ComputeBootstrapInfo projectSnapshot - - match bootstrapInfoOpt with - | None -> + match! ComputeBootstrapInfo projectSnapshot with + | None, creationDiags -> let parseTree = EmptyParsedInput(fileName, (false, false)) let parseResults = FSharpParseFileResults(creationDiags, parseTree, true, [||]) return (parseResults, FSharpCheckFileAnswer.Aborted) - | Some bootstrapInfo -> + | Some bootstrapInfo, creationDiags -> let file = bootstrapInfo.SourceFiles |> List.tryFind (fun f -> f.Source.FileName = fileName) |> Option.defaultWith (fun _ -> failwith ($"File {fileName} not found in project snapshot. Files in project: \n\n" + (bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n"))) - let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot userOpName + let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot // We could also bubble this through ComputeTcPrior let! parseTree, parseDiagnostics, sourceText = ComputeParseFile bootstrapInfo file @@ -867,6 +872,34 @@ type internal TransparentCompiler return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) }) + let ComputeSemanticClassification(fileName: string, projectSnapshot: FSharpProjectSnapshot): NodeCode = + let key = (projectSnapshot.UpTo fileName).Key + SemanticClassificationCache.Get(key, node { + use _ = Activity.start "ComputeSemanticClassification" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + + match! ComputeBootstrapInfo projectSnapshot with + | None, _ -> return None + | Some bootstrapInfo, _creationDiags -> + + let file = + bootstrapInfo.SourceFiles |> List.tryFind (fun f -> f.Source.FileName = fileName) |> Option.defaultWith (fun _ -> failwith ($"File {fileName} not found in project snapshot. Files in project: \n\n" + (bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n"))) + + let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot + let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file + + let fileIndex = projectSnapshot.IndexOf fileName + let! { sink = sink } = ComputeTcIntermediate projectSnapshot fileIndex (parseTree, parseDiagnostics) bootstrapInfo tcInfo + + let sResolutions = sink.GetResolutions() + + let semanticClassification = sResolutions.GetSemanticClassification(bootstrapInfo.TcGlobals, bootstrapInfo.TcImports.GetImportMap(), sink.GetFormatSpecifierLocations(), None) + + let sckBuilder = SemanticClassificationKeyStoreBuilder() + sckBuilder.WriteAll semanticClassification + + return sckBuilder.TryBuildAndReset() |> Option.map (fun sck -> sck.GetView()) + }) + member _.ParseFile(fileName, projectSnapshot: FSharpProjectSnapshot, _userOpName) = node { use _ = Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] @@ -907,7 +940,8 @@ type internal TransparentCompiler } member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = - ComputeParseAndCheckFileInProject fileName projectSnapshot userOpName + ignore userOpName + ComputeParseAndCheckFileInProject fileName projectSnapshot interface IBackgroundCompiler with @@ -1026,6 +1060,15 @@ type internal TransparentCompiler userOpName ) + member _.GetSemanticClassificationForFile + ( + fileName: string, + snapshot: FSharpProjectSnapshot, + userOpName: string + ) = + ignore userOpName + ComputeSemanticClassification(fileName, snapshot) + member _.GetSemanticClassificationForFile ( fileName: string, diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index a12bfdccdf8..ae15a32bb00 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -485,6 +485,12 @@ type FSharpChecker backgroundCompiler.GetSemanticClassificationForFile(fileName, options, userOpName) |> Async.AwaitNodeCode + member _.GetBackgroundSemanticClassificationForFile(fileName: string, snapshot: FSharpProjectSnapshot, ?userOpName) = + let userOpName = defaultArg userOpName "Unknown" + + backgroundCompiler.GetSemanticClassificationForFile(fileName, snapshot, userOpName) + |> Async.AwaitNodeCode + /// For a given script file, get the ProjectOptions implied by the #load closure member _.GetProjectOptionsFromScript ( diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index fe484532dfa..f7a035d9471 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -346,6 +346,17 @@ type public FSharpChecker = fileName: string * options: FSharpProjectOptions * ?userOpName: string -> Async + /// + /// Get semantic classification for a file. + /// + /// + /// The file name for the file. + /// The project snapshot for which we want to get the semantic classification. + /// An optional string used for tracing compiler operations associated with this request. + member GetBackgroundSemanticClassificationForFile: + fileName: string * snapshot: FSharpProjectSnapshot * ?userOpName: string -> + Async + /// /// Compile using the given flags. Source files names are resolved via the FileSystem API. /// The output file must be given by a -o flag. @@ -431,7 +442,7 @@ type public FSharpChecker = /// The event may be raised on a background thread. member ProjectChecked: IEvent - member internal CacheEvent: IEvent + member internal CacheEvent: IEvent [] static member Instance: FSharpChecker diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 23edbee1cfe..acb508dea41 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -2,10 +2,13 @@ open System.Collections.Concurrent open System.Diagnostics +open FSharp.Compiler.CodeAnalysis +open Internal.Utilities.Collections open Xunit open FSharp.Test.ProjectGeneration +open System.IO [] @@ -173,4 +176,33 @@ let ``Changes in a referenced project`` () = updateFile "Library" updatePublicSurface saveFile "Library" checkFile "Last" expectSignatureChanged - } \ No newline at end of file + } + +[] +let ``We don't check files that are not depended on`` () = + let project = SyntheticProject.Create( + sourceFile "First" [], + sourceFile "Second" ["First"], + sourceFile "Third" ["First"], + sourceFile "Last" ["Third"]) + + let cacheEvents = ResizeArray() + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + withChecker (fun checker -> checker.CacheEvent.Add cacheEvents.Add) + updateFile "First" updatePublicSurface + checkFile "Last" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheEvents + |> Seq.choose (function + | ("TcIntermediate", e, k) -> Some ((k :?> FSharpProjectSnapshotKey).LastFile |> fst |> Path.GetFileName, e) + | _ -> None) + |> Seq.groupBy fst + |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) + |> Map + + Assert.Equal([Started; Finished], intermediateTypeChecks["FileFirst.fs"]) + Assert.Equal([Started; Finished], intermediateTypeChecks["FileThird.fs"]) + Assert.False (intermediateTypeChecks.ContainsKey "FileSecond.fs") diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 03aeafb299a..5415b502e06 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2060,6 +2060,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFileInProject(System.String, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] CheckFileInProjectAllowingStaleCachedResults(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyFileChanged(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyProjectCleaned(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2207,6 +2208,7 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey get_Key() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 IndexOf(System.String) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot] SourceFiles FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot] get_SourceFiles() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot] ReferencedProjects @@ -2251,6 +2253,8 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collecti FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String ProjectFileName FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String get_ProjectFileName() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.Tuple`2[System.String,System.String] LastFile +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.Tuple`2[System.String,System.String] get_LastFile() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Void .ctor(System.String, Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey], Boolean, Boolean) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: Boolean Equals(System.Object) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: FSharp.Compiler.CodeAnalysis.FSharpReferencedProject CreateFSharp(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 15304a77a34..beeb7465daa 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -284,9 +284,16 @@ type Document with async { let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync(userOpName) - match! checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectOptions) with - | Some results -> return results - | _ -> return raise (System.OperationCanceledException("Unable to get FSharp semantic classification.")) + let! result = + if this.Project.UseTransparentCompiler then + async { + let! projectSnapshot = getProjectSnapshot (this, projectOptions) + return! checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectSnapshot) + } + else + checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectOptions) + + return result |> Option.defaultWith (fun _ -> raise (System.OperationCanceledException("Unable to get FSharp semantic classification."))) } /// Find F# references in the given F# document. From 6cb3b2f33a68f87159a1a40cac20c348852a8a8c Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 17 May 2023 17:47:18 +0200 Subject: [PATCH 034/222] .. --- src/Compiler/Service/FSharpCheckerResults.fs | 4 ++-- .../src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index ab2d2f554d5..c2271aa1de4 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -129,8 +129,8 @@ type FSharpProjectSnapshotKey = IsIncompleteTypeCheckEnvironment: bool UseScriptResolutionRules: bool } - member this.LastFile = this.SourceFiles |> List.last + member this.LastFile = this.SourceFiles |> List.last [] type FSharpFileSnapshot = @@ -206,7 +206,7 @@ type FSharpProjectSnapshot = SourceFiles = this.SourceFiles[..fileIndex] } - member this.UpTo fileName = this.UpTo (this.IndexOf fileName) + member this.UpTo fileName = this.UpTo(this.IndexOf fileName) member this.Key = { diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index beeb7465daa..6d696487345 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -293,7 +293,9 @@ type Document with else checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectOptions) - return result |> Option.defaultWith (fun _ -> raise (System.OperationCanceledException("Unable to get FSharp semantic classification."))) + return + result + |> Option.defaultWith (fun _ -> raise (System.OperationCanceledException("Unable to get FSharp semantic classification."))) } /// Find F# references in the given F# document. From cbbb2bb023d616714a8cb1a3ac5dd32080e93d7a Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 17 May 2023 20:09:34 +0200 Subject: [PATCH 035/222] find references --- src/Compiler/Service/BackgroundCompiler.fs | 7 ++ src/Compiler/Service/TransparentCompiler.fs | 68 ++++++++++++++++--- src/Compiler/Service/service.fs | 16 +++++ src/Compiler/Service/service.fsi | 4 ++ .../LanguageService/WorkspaceExtensions.fs | 21 ++++-- 5 files changed, 101 insertions(+), 15 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 0fcf7918cc2..cc25ed2bed3 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -80,6 +80,10 @@ type internal IBackgroundCompiler = userOpName: string -> NodeCode> + abstract member FindReferencesInFile: + fileName: string * projectSnapshot: FSharpProjectSnapshot * symbol: FSharp.Compiler.Symbols.FSharpSymbol * userOpName: string -> + NodeCode> + abstract member GetAssemblyData: options: FSharpProjectOptions * userOpName: string -> NodeCode @@ -1431,6 +1435,9 @@ type internal BackgroundCompiler ) : NodeCode> = self.FindReferencesInFile(fileName, options, symbol, canInvalidateProject, userOpName) + member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = + this.FindReferencesInFile(fileName, projectSnapshot.ToOptions(), symbol, true, userOpName) + member _.FrameworkImportsCache: FrameworkImportsCache = self.FrameworkImportsCache member _.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 0ccd19d7846..d7affa00913 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1,6 +1,7 @@ namespace FSharp.Compiler.CodeAnalysis.TransparentCompiler open System +open System.Collections.Generic open System.Diagnostics open System.IO @@ -35,6 +36,7 @@ open Internal.Utilities.Library.Extras open FSharp.Compiler.TypedTree open FSharp.Compiler.CheckDeclarations open FSharp.Compiler.EditorServices +open FSharp.Compiler.CodeAnalysis type internal FSharpFile = { @@ -110,6 +112,7 @@ type internal TransparentCompiler let TcIntermediateCache = AsyncMemoize(triggerCacheEvent "TcIntermediate") let DependencyGraphForLastFileCache = AsyncMemoize(triggerCacheEvent "DependencyGraphForLastFile") let SemanticClassificationCache = AsyncMemoize(triggerCacheEvent "SemanticClassification") + let ItemKeyStoreCache = AsyncMemoize(triggerCacheEvent "ItemKeyStore") // We currently share one global dependency provider for all scripts for the FSharpChecker. // For projects, one is used per project. @@ -872,9 +875,8 @@ type internal TransparentCompiler return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) }) - let ComputeSemanticClassification(fileName: string, projectSnapshot: FSharpProjectSnapshot): NodeCode = - let key = (projectSnapshot.UpTo fileName).Key - SemanticClassificationCache.Get(key, node { + let tryGetSink fileName (projectSnapshot: FSharpProjectSnapshot) = + node { use _ = Activity.start "ComputeSemanticClassification" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] match! ComputeBootstrapInfo projectSnapshot with @@ -890,14 +892,48 @@ type internal TransparentCompiler let fileIndex = projectSnapshot.IndexOf fileName let! { sink = sink } = ComputeTcIntermediate projectSnapshot fileIndex (parseTree, parseDiagnostics) bootstrapInfo tcInfo - let sResolutions = sink.GetResolutions() + return Some (sink, bootstrapInfo) + } + + let ComputeSemanticClassification(fileName: string, projectSnapshot: FSharpProjectSnapshot) = + let key = (projectSnapshot.UpTo fileName).Key + SemanticClassificationCache.Get(key, node { + use _ = Activity.start "ComputeSemanticClassification" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + let! sinkOpt = tryGetSink fileName projectSnapshot + return + sinkOpt + |> Option.bind (fun (sink, bootstrapInfo) -> + let sResolutions = sink.GetResolutions() + let semanticClassification = sResolutions.GetSemanticClassification(bootstrapInfo.TcGlobals, bootstrapInfo.TcImports.GetImportMap(), sink.GetFormatSpecifierLocations(), None) - let semanticClassification = sResolutions.GetSemanticClassification(bootstrapInfo.TcGlobals, bootstrapInfo.TcImports.GetImportMap(), sink.GetFormatSpecifierLocations(), None) + let sckBuilder = SemanticClassificationKeyStoreBuilder() + sckBuilder.WriteAll semanticClassification - let sckBuilder = SemanticClassificationKeyStoreBuilder() - sckBuilder.WriteAll semanticClassification + sckBuilder.TryBuildAndReset()) + |> Option.map (fun sck -> sck.GetView()) + }) - return sckBuilder.TryBuildAndReset() |> Option.map (fun sck -> sck.GetView()) + let ComputeItemKeyStore(fileName: string, projectSnapshot: FSharpProjectSnapshot) = + let key = (projectSnapshot.UpTo fileName).Key + ItemKeyStoreCache.Get(key, node { + use _ = Activity.start "ComputeItemKeyStore" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + let! sinkOpt = tryGetSink fileName projectSnapshot + return + sinkOpt + |> Option.bind (fun (sink, _) -> + let sResolutions = sink.GetResolutions() + + let builder = ItemKeyStoreBuilder() + let preventDuplicates = HashSet({ new IEqualityComparer with + member _.Equals((s1, e1): struct(pos * pos), (s2, e2): struct(pos * pos)) = Position.posEq s1 s2 && Position.posEq e1 e2 + member _.GetHashCode o = o.GetHashCode() }) + sResolutions.CapturedNameResolutions + |> Seq.iter (fun cnr -> + let r = cnr.Range + if preventDuplicates.Add struct(r.Start, r.End) then + builder.Write(cnr.Range, cnr.Item)) + + builder.TryBuildAndReset()) }) member _.ParseFile(fileName, projectSnapshot: FSharpProjectSnapshot, _userOpName) = @@ -943,8 +979,22 @@ type internal TransparentCompiler ignore userOpName ComputeParseAndCheckFileInProject fileName projectSnapshot + member _.FindReferencesInFile + ( + fileName: string, + projectSnapshot: FSharpProjectSnapshot, + symbol: FSharpSymbol, + userOpName: string + ) = + ignore userOpName + node { + match! ComputeItemKeyStore(fileName, projectSnapshot) with + | None -> return Seq.empty + | Some itemKeyStore -> return itemKeyStore.FindAll symbol.Item + } interface IBackgroundCompiler with + member _.CacheEvent = cacheEvent.Publish member this.BeforeBackgroundFileCheck: IEvent = @@ -1001,6 +1051,8 @@ type internal TransparentCompiler ) : NodeCode> = backgroundCompiler.FindReferencesInFile(fileName, options, symbol, canInvalidateProject, userOpName) + member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) + member _.FrameworkImportsCache: FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index ae15a32bb00..ad2d1d0ee2a 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -479,6 +479,22 @@ type FSharpChecker } |> Async.AwaitNodeCode + member _.FindBackgroundReferencesInFile(fileName: string, projectSnapshot: FSharpProjectSnapshot, symbol: FSharpSymbol, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + + node { + let! parseResults = backgroundCompiler.ParseFile(fileName, projectSnapshot, userOpName) + + if + parseResults.ParseTree.Identifiers |> Set.contains symbol.DisplayNameCore + || parseResults.ParseTree.Identifiers |> NamesContainAttribute symbol + then + return! backgroundCompiler.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) + else + return Seq.empty + } + |> Async.AwaitNodeCode + member _.GetBackgroundSemanticClassificationForFile(fileName: string, options: FSharpProjectOptions, ?userOpName) = let userOpName = defaultArg userOpName "Unknown" diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index f7a035d9471..30ce574813f 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -333,6 +333,10 @@ type public FSharpChecker = ?userOpName: string -> Async + member FindBackgroundReferencesInFile: + fileName: string * projectSnapshot: FSharpProjectSnapshot * symbol: FSharpSymbol * ?userOpName: string -> + Async + /// /// Get semantic classification for a file. /// All files are read from the FileSystem API, including the file being checked. diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 6d696487345..2c015d68e11 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -304,13 +304,20 @@ type Document with let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync(userOpName) let! symbolUses = - checker.FindBackgroundReferencesInFile( - this.FilePath, - projectOptions, - symbol, - canInvalidateProject = false, - fastCheck = this.Project.IsFastFindReferencesEnabled - ) + + if this.Project.UseTransparentCompiler then + async { + let! projectSnapshot = getProjectSnapshot (this, projectOptions) + return! checker.FindBackgroundReferencesInFile(this.FilePath, projectSnapshot, symbol) + } + else + checker.FindBackgroundReferencesInFile( + this.FilePath, + projectOptions, + symbol, + canInvalidateProject = false, + fastCheck = this.Project.IsFastFindReferencesEnabled + ) for symbolUse in symbolUses do do! onFound symbolUse From 371ee99d24f4af65cb31a8f46320b7b98229e824 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 18 May 2023 11:26:30 +0200 Subject: [PATCH 036/222] wip --- src/Compiler/Service/TransparentCompiler.fs | 128 +++++++++----------- 1 file changed, 54 insertions(+), 74 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index d7affa00913..8ef1500f04e 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -57,6 +57,10 @@ type internal BootstrapInfo = LoadClosure: LoadClosure option } + member this.GetFile fileName = + this.SourceFiles |> List.tryFind (fun f -> f.Source.FileName = fileName) |> Option.defaultWith (fun _ -> failwith ($"File {fileName} not found in project snapshot. Files in project: \n\n" + (this.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n"))) + + type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string @@ -216,10 +220,8 @@ type internal TransparentCompiler unresolvedReferences, dependencyProvider, loadClosureOpt: LoadClosure option, - basicDependencies -#if !NO_TYPEPROVIDERS - ,importsInvalidatedByTypeProvider: Event -#endif + basicDependencies, + importsInvalidatedByTypeProvider: Event ) = node { @@ -240,6 +242,7 @@ type internal TransparentCompiler dependencyProvider ) #if !NO_TYPEPROVIDERS + // TODO: review and handle the event tcImports.GetCcusExcludingBase() |> Seq.iter (fun ccu -> // When a CCU reports an invalidation, merge them together and just report a @@ -472,9 +475,9 @@ type internal TransparentCompiler let tcConfigP = TcConfigProvider.Constant tcConfig - #if !NO_TYPEPROVIDERS + let importsInvalidatedByTypeProvider = Event() - #endif + // Check for the existence of loaded sources and prepend them to the sources list if present. let sourceFiles = @@ -522,17 +525,15 @@ type internal TransparentCompiler unresolvedReferences, dependencyProvider, loadClosureOpt, - basicDependencies - #if !NO_TYPEPROVIDERS - ,importsInvalidatedByTypeProvider - #endif + basicDependencies, + importsInvalidatedByTypeProvider ) - return sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt + return sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt, importsInvalidatedByTypeProvider }) let computeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = node { - let! sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt = ComputeBootstrapInfoStatic projectSnapshot + let! sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt, _importsInvalidatedByTypeProvider = ComputeBootstrapInfoStatic projectSnapshot let fileSnapshots = Map [ for f in projectSnapshot.SourceFiles -> f.FileName, f ] @@ -565,6 +566,7 @@ type internal TransparentCompiler InitialTcInfo = initialTcInfo SourceFiles = sourceFiles LoadClosure = loadClosureOpt + //ImportsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider } } @@ -806,6 +808,37 @@ type internal TransparentCompiler return! processLayer layers bootstrapInfo.InitialTcInfo }) + let getParseResult (bootstrapInfo: BootstrapInfo) creationDiags fileName = + node { + let file = bootstrapInfo.GetFile fileName + + let! parseTree, parseDiagnostics, sourceText = ComputeParseFile bootstrapInfo file + + let parseDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + bootstrapInfo.TcConfig.diagnosticsOptions, + false, + fileName, + parseDiagnostics, + suggestNamesForErrors + ) + + let diagnostics = [| yield! creationDiags; yield! parseDiagnostics |] + + return + FSharpParseFileResults( + diagnostics = diagnostics, + input = parseTree, + parseHadErrors = (parseDiagnostics.Length > 0), + // TODO: check if we really need this in parse results + dependencyFiles = [||] + ), sourceText + } + + let emptyParseResult fileName diagnostics = + let parseTree = EmptyParsedInput(fileName, (false, false)) + FSharpParseFileResults(diagnostics, parseTree, true, [||]) + let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = let key = fileName, projectSnapshot.Key ParseAndCheckFileInProjectCache.Get(key, node { @@ -814,39 +847,15 @@ type internal TransparentCompiler match! ComputeBootstrapInfo projectSnapshot with | None, creationDiags -> - let parseTree = EmptyParsedInput(fileName, (false, false)) - let parseResults = FSharpParseFileResults(creationDiags, parseTree, true, [||]) - return (parseResults, FSharpCheckFileAnswer.Aborted) + return emptyParseResult fileName creationDiags, FSharpCheckFileAnswer.Aborted | Some bootstrapInfo, creationDiags -> - let file = - bootstrapInfo.SourceFiles |> List.tryFind (fun f -> f.Source.FileName = fileName) |> Option.defaultWith (fun _ -> failwith ($"File {fileName} not found in project snapshot. Files in project: \n\n" + (bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n"))) - - let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot + let file = bootstrapInfo.GetFile fileName - // We could also bubble this through ComputeTcPrior - let! parseTree, parseDiagnostics, sourceText = ComputeParseFile bootstrapInfo file + let! parseResults, sourceText = getParseResult bootstrapInfo creationDiags fileName - let parseDiagnostics = - DiagnosticHelpers.CreateDiagnostics( - bootstrapInfo.TcConfig.diagnosticsOptions, - false, - fileName, - parseDiagnostics, - suggestNamesForErrors - ) - - let diagnostics = [| yield! creationDiags; yield! parseDiagnostics |] - - let parseResults = - FSharpParseFileResults( - diagnostics = diagnostics, - input = parseTree, - parseHadErrors = (parseDiagnostics.Length > 0), - // TODO: check if we really need this in parse results - dependencyFiles = Array.ofList tcInfo.tcDependencyFiles - ) + let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot let! checkResults = FSharpCheckFileResults.CheckOneFile( @@ -939,40 +948,11 @@ type internal TransparentCompiler member _.ParseFile(fileName, projectSnapshot: FSharpProjectSnapshot, _userOpName) = node { use _ = Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - - let! bootstrapInfoOpt, creationDiags = ComputeBootstrapInfo projectSnapshot - - match bootstrapInfoOpt with - | None -> - let parseTree = EmptyParsedInput(fileName, (false, false)) - return FSharpParseFileResults(creationDiags, parseTree, true, [||]) - - | Some bootstrapInfo -> - - let file = - bootstrapInfo.SourceFiles |> List.tryFind (fun f -> f.Source.FileName = fileName) |> Option.defaultWith (fun _ -> failwith ($"File {fileName} not found in project snapshot. Files in project: \n\n" + (bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n"))) - - let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file - - let parseDiagnostics = - DiagnosticHelpers.CreateDiagnostics( - bootstrapInfo.TcConfig.diagnosticsOptions, - false, - fileName, - parseDiagnostics, - suggestNamesForErrors - ) - - let diagnostics = [| yield! creationDiags; yield! parseDiagnostics |] - - return - FSharpParseFileResults( - diagnostics = diagnostics, - input = parseTree, - parseHadErrors = (parseDiagnostics.Length > 0), - // TODO: check if we really need this in parse results - dependencyFiles = [||] - ) + match! ComputeBootstrapInfo projectSnapshot with + | None, creationDiags -> return emptyParseResult fileName creationDiags + | Some bootstrapInfo, creationDiags -> + let! parseResult, _ = getParseResult bootstrapInfo creationDiags fileName + return parseResult } member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = From 3c6d3420f52cc4eb7d9bb37f47954383dda27201 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 18 May 2023 11:28:06 +0200 Subject: [PATCH 037/222] fantomas --- .fantomasignore | 1 - src/Compiler/Service/TransparentCompiler.fs | 1116 ++++++++++--------- 2 files changed, 611 insertions(+), 506 deletions(-) diff --git a/.fantomasignore b/.fantomasignore index 8b224eb8f65..45eb387fa16 100644 --- a/.fantomasignore +++ b/.fantomasignore @@ -95,7 +95,6 @@ src/FSharp.Core/seqcore.fs src/Compiler/AbstractIL/ilwrite.fs src/Compiler/Utilities/lib.fs src/Compiler/Service/IncrementalBuild.fs -src/Compiler/Service/TransparentCompiler.fs src/Compiler/Service/ServiceAssemblyContent.fs src/Compiler/Service/ServiceDeclarationLists.fs src/Compiler/Service/ServiceErrorResolutionHints.fs diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 8ef1500f04e..bd0aa272ae8 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -58,12 +58,16 @@ type internal BootstrapInfo = } member this.GetFile fileName = - this.SourceFiles |> List.tryFind (fun f -> f.Source.FileName = fileName) |> Option.defaultWith (fun _ -> failwith ($"File {fileName} not found in project snapshot. Files in project: \n\n" + (this.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n"))) - + this.SourceFiles + |> List.tryFind (fun f -> f.Source.FileName = fileName) + |> Option.defaultWith (fun _ -> + failwith ( + $"File {fileName} not found in project snapshot. Files in project: \n\n" + + (this.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n") + )) type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string - /// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. [] type internal TcIntermediate = @@ -75,7 +79,7 @@ type internal TcIntermediate = moduleNamesDict: ModuleNamesDict /// Accumulated diagnostics, last file first - tcDiagnosticsRev:(PhasedDiagnostic * FSharpDiagnosticSeverity)[] list + tcDiagnosticsRev: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] list tcDependencyFiles: string list @@ -106,16 +110,23 @@ type internal TransparentCompiler let cacheEvent = new Event() let triggerCacheEvent name (e, k) = cacheEvent.Trigger(name, e, k) - let ParseFileCache = - AsyncMemoize(triggerCacheEvent "ParseFile") - let ParseAndCheckFileInProjectCache = AsyncMemoize(triggerCacheEvent "ParseAndCheckFileInProject") + let ParseFileCache = AsyncMemoize(triggerCacheEvent "ParseFile") + + let ParseAndCheckFileInProjectCache = + AsyncMemoize(triggerCacheEvent "ParseAndCheckFileInProject") + let FrameworkImportsCache = AsyncMemoize(triggerCacheEvent "FrameworkImports") let BootstrapInfoStaticCache = AsyncMemoize(triggerCacheEvent "BootstrapInfoStatic") let BootstrapInfoCache = AsyncMemoize(triggerCacheEvent "BootstrapInfo") let TcPriorCache = AsyncMemoize(triggerCacheEvent "TcPrior") let TcIntermediateCache = AsyncMemoize(triggerCacheEvent "TcIntermediate") - let DependencyGraphForLastFileCache = AsyncMemoize(triggerCacheEvent "DependencyGraphForLastFile") - let SemanticClassificationCache = AsyncMemoize(triggerCacheEvent "SemanticClassification") + + let DependencyGraphForLastFileCache = + AsyncMemoize(triggerCacheEvent "DependencyGraphForLastFile") + + let SemanticClassificationCache = + AsyncMemoize(triggerCacheEvent "SemanticClassification") + let ItemKeyStoreCache = AsyncMemoize(triggerCacheEvent "ItemKeyStore") // We currently share one global dependency provider for all scripts for the FSharpChecker. @@ -202,11 +213,14 @@ type internal TransparentCompiler tcConfig.langVersion.SpecifiedVersion ) - FrameworkImportsCache.Get(key, node { - use _ = Activity.start "ComputeFrameworkImports" [] - let tcConfigP = TcConfigProvider.Constant tcConfig - return! TcImports.BuildFrameworkTcImports(tcConfigP, frameworkDLLs, nonFrameworkResolutions) - }) + FrameworkImportsCache.Get( + key, + node { + use _ = Activity.start "ComputeFrameworkImports" [] + let tcConfigP = TcConfigProvider.Constant tcConfig + return! TcImports.BuildFrameworkTcImports(tcConfigP, frameworkDLLs, nonFrameworkResolutions) + } + ) // Link all the assemblies together and produce the input typecheck accumulator let CombineImportedAssembliesTask @@ -311,229 +325,235 @@ type internal TransparentCompiler let key = projectSnapshot.WithoutFileVersions.Key - BootstrapInfoStaticCache.Get(key, node { - use _ = Activity.start "ComputeBootstrapInfoStatic" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] - - let useSimpleResolutionSwitch = "--simpleresolution" - let commandLineArgs = projectSnapshot.OtherOptions - let defaultFSharpBinariesDir = FSharpCheckerResultsSettings.defaultFSharpBinariesDir - let useScriptResolutionRules = projectSnapshot.UseScriptResolutionRules - - let projectReferences = getProjectReferences projectSnapshot "ComputeBootstrapInfo" - - // TODO: script support - let loadClosureOpt: LoadClosure option = None - - let tcConfigB, sourceFiles = - - let getSwitchValue (switchString: string) = - match commandLineArgs |> List.tryFindIndex (fun s -> s.StartsWithOrdinal switchString) with - | Some idx -> Some(commandLineArgs[ idx ].Substring(switchString.Length)) - | _ -> None - - let sdkDirOverride = - match loadClosureOpt with - | None -> None - | Some loadClosure -> loadClosure.SdkDirOverride - - // see also fsc.fs: runFromCommandLineToImportingAssemblies(), as there are many similarities to where the PS creates a tcConfigB - let tcConfigB = - TcConfigBuilder.CreateNew( - legacyReferenceResolver, - defaultFSharpBinariesDir, - implicitIncludeDir = projectSnapshot.ProjectDirectory, - reduceMemoryUsage = ReduceMemoryFlag.Yes, - isInteractive = useScriptResolutionRules, - isInvalidationSupported = true, - defaultCopyFSharpCore = CopyFSharpCoreFlag.No, - tryGetMetadataSnapshot = tryGetMetadataSnapshot, - sdkDirOverride = sdkDirOverride, - rangeForErrors = range0 - ) - - tcConfigB.primaryAssembly <- - match loadClosureOpt with - | None -> PrimaryAssembly.Mscorlib - | Some loadClosure -> - if loadClosure.UseDesktopFramework then - PrimaryAssembly.Mscorlib - else - PrimaryAssembly.System_Runtime - - tcConfigB.resolutionEnvironment <- (LegacyResolutionEnvironment.EditingOrCompilation true) - - tcConfigB.conditionalDefines <- - let define = - if useScriptResolutionRules then - "INTERACTIVE" - else - "COMPILED" - - define :: tcConfigB.conditionalDefines - - tcConfigB.projectReferences <- projectReferences - - tcConfigB.useSimpleResolution <- (getSwitchValue useSimpleResolutionSwitch) |> Option.isSome - - // Apply command-line arguments and collect more source files if they are in the arguments - let sourceFilesNew = - ApplyCommandLineArgs(tcConfigB, projectSnapshot.SourceFileNames, commandLineArgs) - - // Never open PDB files for the language service, even if --standalone is specified - tcConfigB.openDebugInformationForLaterStaticLinking <- false - - tcConfigB.xmlDocInfoLoader <- - { new IXmlDocumentationInfoLoader with - /// Try to load xml documentation associated with an assembly by the same file path with the extension ".xml". - member _.TryLoad(assemblyFileName) = - let xmlFileName = Path.ChangeExtension(assemblyFileName, ".xml") - - // REVIEW: File IO - Will eventually need to change this to use a file system interface of some sort. - XmlDocumentationInfo.TryCreateFromFile(xmlFileName) - } - |> Some - - tcConfigB.parallelReferenceResolution <- parallelReferenceResolution - tcConfigB.captureIdentifiersWhenParsing <- captureIdentifiersWhenParsing - - tcConfigB, sourceFilesNew - - // If this is a builder for a script, re-apply the settings inferred from the - // script and its load closure to the configuration. - // - // NOTE: it would probably be cleaner and more accurate to re-run the load closure at this point. - let setupConfigFromLoadClosure () = - match loadClosureOpt with - | Some loadClosure -> - let dllReferences = - [ - for reference in tcConfigB.referencedDLLs do - // If there's (one or more) resolutions of closure references then yield them all - match - loadClosure.References - |> List.tryFind (fun (resolved, _) -> resolved = reference.Text) - with - | Some (resolved, closureReferences) -> - for closureReference in closureReferences do - yield AssemblyReference(closureReference.originalReference.Range, resolved, None) - | None -> yield reference - ] - - tcConfigB.referencedDLLs <- [] + BootstrapInfoStaticCache.Get( + key, + node { + use _ = + Activity.start + "ComputeBootstrapInfoStatic" + [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] + + let useSimpleResolutionSwitch = "--simpleresolution" + let commandLineArgs = projectSnapshot.OtherOptions + let defaultFSharpBinariesDir = FSharpCheckerResultsSettings.defaultFSharpBinariesDir + let useScriptResolutionRules = projectSnapshot.UseScriptResolutionRules + + let projectReferences = getProjectReferences projectSnapshot "ComputeBootstrapInfo" + + // TODO: script support + let loadClosureOpt: LoadClosure option = None + + let tcConfigB, sourceFiles = + + let getSwitchValue (switchString: string) = + match commandLineArgs |> List.tryFindIndex (fun s -> s.StartsWithOrdinal switchString) with + | Some idx -> Some(commandLineArgs[ idx ].Substring(switchString.Length)) + | _ -> None + + let sdkDirOverride = + match loadClosureOpt with + | None -> None + | Some loadClosure -> loadClosure.SdkDirOverride + + // see also fsc.fs: runFromCommandLineToImportingAssemblies(), as there are many similarities to where the PS creates a tcConfigB + let tcConfigB = + TcConfigBuilder.CreateNew( + legacyReferenceResolver, + defaultFSharpBinariesDir, + implicitIncludeDir = projectSnapshot.ProjectDirectory, + reduceMemoryUsage = ReduceMemoryFlag.Yes, + isInteractive = useScriptResolutionRules, + isInvalidationSupported = true, + defaultCopyFSharpCore = CopyFSharpCoreFlag.No, + tryGetMetadataSnapshot = tryGetMetadataSnapshot, + sdkDirOverride = sdkDirOverride, + rangeForErrors = range0 + ) tcConfigB.primaryAssembly <- - (if loadClosure.UseDesktopFramework then - PrimaryAssembly.Mscorlib - else - PrimaryAssembly.System_Runtime) - // Add one by one to remove duplicates - dllReferences - |> List.iter (fun dllReference -> tcConfigB.AddReferencedAssemblyByPath(dllReference.Range, dllReference.Text)) - - tcConfigB.knownUnresolvedReferences <- loadClosure.UnresolvedReferences - | None -> () - - setupConfigFromLoadClosure () - - let tcConfig = TcConfig.Create(tcConfigB, validate = true) - let _outfile, _, assemblyName = tcConfigB.DecideNames sourceFiles + match loadClosureOpt with + | None -> PrimaryAssembly.Mscorlib + | Some loadClosure -> + if loadClosure.UseDesktopFramework then + PrimaryAssembly.Mscorlib + else + PrimaryAssembly.System_Runtime - // Resolve assemblies and create the framework TcImports. This caches a level of "system" references. No type providers are - // included in these references. + tcConfigB.resolutionEnvironment <- (LegacyResolutionEnvironment.EditingOrCompilation true) - let frameworkDLLs, nonFrameworkResolutions, unresolvedReferences = - TcAssemblyResolutions.SplitNonFoundationalResolutions(tcConfig) + tcConfigB.conditionalDefines <- + let define = + if useScriptResolutionRules then + "INTERACTIVE" + else + "COMPILED" - // Prepare the frameworkTcImportsCache - let! tcGlobals, frameworkTcImports = ComputeFrameworkImports tcConfig frameworkDLLs nonFrameworkResolutions - - // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. - // This is ok because not much can actually go wrong here. - let diagnosticsLogger = - CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) - - use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) + define :: tcConfigB.conditionalDefines - // TODO: might need to put something like this somewhere - //// Get the names and time stamps of all the non-framework referenced assemblies, which will act - //// as inputs to one of the nodes in the build. - //// - //// This operation is done when constructing the builder itself, rather than as an incremental task. - //let nonFrameworkAssemblyInputs = - // // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. - // // This is ok because not much can actually go wrong here. - // let diagnosticsLogger = CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) - // // Return the disposable object that cleans up - // use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) + tcConfigB.projectReferences <- projectReferences - // [ for r in nonFrameworkResolutions do - // let fileName = r.resolvedPath - // yield (Choice1Of2 fileName, (fun (cache: TimeStampCache) -> cache.GetFileTimeStamp fileName)) + tcConfigB.useSimpleResolution <- (getSwitchValue useSimpleResolutionSwitch) |> Option.isSome - // for pr in projectReferences do - // yield Choice2Of2 pr, (fun (cache: TimeStampCache) -> cache.GetProjectReferenceTimeStamp pr) ] + // Apply command-line arguments and collect more source files if they are in the arguments + let sourceFilesNew = + ApplyCommandLineArgs(tcConfigB, projectSnapshot.SourceFileNames, commandLineArgs) - let tcConfigP = TcConfigProvider.Constant tcConfig + // Never open PDB files for the language service, even if --standalone is specified + tcConfigB.openDebugInformationForLaterStaticLinking <- false + tcConfigB.xmlDocInfoLoader <- + { new IXmlDocumentationInfoLoader with + /// Try to load xml documentation associated with an assembly by the same file path with the extension ".xml". + member _.TryLoad(assemblyFileName) = + let xmlFileName = Path.ChangeExtension(assemblyFileName, ".xml") - let importsInvalidatedByTypeProvider = Event() + // REVIEW: File IO - Will eventually need to change this to use a file system interface of some sort. + XmlDocumentationInfo.TryCreateFromFile(xmlFileName) + } + |> Some + tcConfigB.parallelReferenceResolution <- parallelReferenceResolution + tcConfigB.captureIdentifiersWhenParsing <- captureIdentifiersWhenParsing - // Check for the existence of loaded sources and prepend them to the sources list if present. - let sourceFiles = - tcConfig.GetAvailableLoadedSources() - @ (sourceFiles |> List.map (fun s -> rangeStartup, s)) + tcConfigB, sourceFilesNew - // Mark up the source files with an indicator flag indicating if they are the last source file in the project - let sourceFiles = - let flags, isExe = tcConfig.ComputeCanContainEntryPoint(sourceFiles |> List.map snd) - ((sourceFiles, flags) ||> List.map2 (fun (m, nm) flag -> (m, nm, (flag, isExe)))) + // If this is a builder for a script, re-apply the settings inferred from the + // script and its load closure to the configuration. + // + // NOTE: it would probably be cleaner and more accurate to re-run the load closure at this point. + let setupConfigFromLoadClosure () = + match loadClosureOpt with + | Some loadClosure -> + let dllReferences = + [ + for reference in tcConfigB.referencedDLLs do + // If there's (one or more) resolutions of closure references then yield them all + match + loadClosure.References + |> List.tryFind (fun (resolved, _) -> resolved = reference.Text) + with + | Some (resolved, closureReferences) -> + for closureReference in closureReferences do + yield AssemblyReference(closureReference.originalReference.Range, resolved, None) + | None -> yield reference + ] + + tcConfigB.referencedDLLs <- [] + + tcConfigB.primaryAssembly <- + (if loadClosure.UseDesktopFramework then + PrimaryAssembly.Mscorlib + else + PrimaryAssembly.System_Runtime) + // Add one by one to remove duplicates + dllReferences + |> List.iter (fun dllReference -> tcConfigB.AddReferencedAssemblyByPath(dllReference.Range, dllReference.Text)) + + tcConfigB.knownUnresolvedReferences <- loadClosure.UnresolvedReferences + | None -> () - let basicDependencies = - [ - for UnresolvedAssemblyReference (referenceText, _) in unresolvedReferences do - // Exclude things that are definitely not a file name - if not (FileSystem.IsInvalidPathShim referenceText) then - let file = - if FileSystem.IsPathRootedShim referenceText then - referenceText - else - Path.Combine(projectSnapshot.ProjectDirectory, referenceText) - - yield file - - for r in nonFrameworkResolutions do - yield r.resolvedPath - ] + setupConfigFromLoadClosure () + + let tcConfig = TcConfig.Create(tcConfigB, validate = true) + let _outfile, _, assemblyName = tcConfigB.DecideNames sourceFiles + + // Resolve assemblies and create the framework TcImports. This caches a level of "system" references. No type providers are + // included in these references. + + let frameworkDLLs, nonFrameworkResolutions, unresolvedReferences = + TcAssemblyResolutions.SplitNonFoundationalResolutions(tcConfig) + + // Prepare the frameworkTcImportsCache + let! tcGlobals, frameworkTcImports = ComputeFrameworkImports tcConfig frameworkDLLs nonFrameworkResolutions + + // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. + // This is ok because not much can actually go wrong here. + let diagnosticsLogger = + CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) + + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) + + // TODO: might need to put something like this somewhere + //// Get the names and time stamps of all the non-framework referenced assemblies, which will act + //// as inputs to one of the nodes in the build. + //// + //// This operation is done when constructing the builder itself, rather than as an incremental task. + //let nonFrameworkAssemblyInputs = + // // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. + // // This is ok because not much can actually go wrong here. + // let diagnosticsLogger = CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) + // // Return the disposable object that cleans up + // use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) + + // [ for r in nonFrameworkResolutions do + // let fileName = r.resolvedPath + // yield (Choice1Of2 fileName, (fun (cache: TimeStampCache) -> cache.GetFileTimeStamp fileName)) + + // for pr in projectReferences do + // yield Choice2Of2 pr, (fun (cache: TimeStampCache) -> cache.GetProjectReferenceTimeStamp pr) ] + + let tcConfigP = TcConfigProvider.Constant tcConfig + + let importsInvalidatedByTypeProvider = Event() + + // Check for the existence of loaded sources and prepend them to the sources list if present. + let sourceFiles = + tcConfig.GetAvailableLoadedSources() + @ (sourceFiles |> List.map (fun s -> rangeStartup, s)) + + // Mark up the source files with an indicator flag indicating if they are the last source file in the project + let sourceFiles = + let flags, isExe = tcConfig.ComputeCanContainEntryPoint(sourceFiles |> List.map snd) + ((sourceFiles, flags) ||> List.map2 (fun (m, nm) flag -> (m, nm, (flag, isExe)))) + + let basicDependencies = + [ + for UnresolvedAssemblyReference (referenceText, _) in unresolvedReferences do + // Exclude things that are definitely not a file name + if not (FileSystem.IsInvalidPathShim referenceText) then + let file = + if FileSystem.IsPathRootedShim referenceText then + referenceText + else + Path.Combine(projectSnapshot.ProjectDirectory, referenceText) + + yield file + + for r in nonFrameworkResolutions do + yield r.resolvedPath + ] + + // For scripts, the dependency provider is already available. + // For projects create a fresh one for the project. + let dependencyProvider = + if projectSnapshot.UseScriptResolutionRules then + dependencyProviderForScripts + else + new DependencyProvider() + + let! tcImports, initialTcInfo = + CombineImportedAssembliesTask( + assemblyName, + tcConfig, + tcConfigP, + tcGlobals, + frameworkTcImports, + nonFrameworkResolutions, + unresolvedReferences, + dependencyProvider, + loadClosureOpt, + basicDependencies, + importsInvalidatedByTypeProvider + ) - // For scripts, the dependency provider is already available. - // For projects create a fresh one for the project. - let dependencyProvider = - if projectSnapshot.UseScriptResolutionRules then - dependencyProviderForScripts - else - new DependencyProvider() - - let! tcImports, initialTcInfo = - CombineImportedAssembliesTask( - assemblyName, - tcConfig, - tcConfigP, - tcGlobals, - frameworkTcImports, - nonFrameworkResolutions, - unresolvedReferences, - dependencyProvider, - loadClosureOpt, - basicDependencies, - importsInvalidatedByTypeProvider - ) - return sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt, importsInvalidatedByTypeProvider - }) + return sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt, importsInvalidatedByTypeProvider + } + ) let computeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = node { - let! sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt, _importsInvalidatedByTypeProvider = ComputeBootstrapInfoStatic projectSnapshot + let! sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt, _importsInvalidatedByTypeProvider = + ComputeBootstrapInfoStatic projectSnapshot let fileSnapshots = Map [ for f in projectSnapshot.SourceFiles -> f.FileName, f ] @@ -566,169 +586,207 @@ type internal TransparentCompiler InitialTcInfo = initialTcInfo SourceFiles = sourceFiles LoadClosure = loadClosureOpt - //ImportsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider + //ImportsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider } } let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) = - BootstrapInfoCache.Get(projectSnapshot.Key, node { - use _ = Activity.start "ComputeBootstrapInfo" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] - - // Trap and report diagnostics from creation. - let delayedLogger = CapturingDiagnosticsLogger("IncrementalBuilderCreation") - use _ = new CompilationGlobalsScope(delayedLogger, BuildPhase.Parameter) - - let! bootstrapInfoOpt = - node { - try - return! computeBootstrapInfoInner projectSnapshot - with exn -> - errorRecoveryNoRange exn - return None - } + BootstrapInfoCache.Get( + projectSnapshot.Key, + node { + use _ = + Activity.start "ComputeBootstrapInfo" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] + + // Trap and report diagnostics from creation. + let delayedLogger = CapturingDiagnosticsLogger("IncrementalBuilderCreation") + use _ = new CompilationGlobalsScope(delayedLogger, BuildPhase.Parameter) + + let! bootstrapInfoOpt = + node { + try + return! computeBootstrapInfoInner projectSnapshot + with exn -> + errorRecoveryNoRange exn + return None + } - let diagnostics = - match bootstrapInfoOpt with - | Some bootstrapInfo -> - let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions + let diagnostics = + match bootstrapInfoOpt with + | Some bootstrapInfo -> + let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions - let diagnosticsLogger = - CompilationDiagnosticLogger("IncrementalBuilderCreation", diagnosticsOptions) + let diagnosticsLogger = + CompilationDiagnosticLogger("IncrementalBuilderCreation", diagnosticsOptions) - delayedLogger.CommitDelayedDiagnostics diagnosticsLogger - diagnosticsLogger.GetDiagnostics() - | _ -> Array.ofList delayedLogger.Diagnostics - |> Array.map (fun (diagnostic, severity) -> - FSharpDiagnostic.CreateFromException(diagnostic, severity, range.Zero, suggestNamesForErrors)) + delayedLogger.CommitDelayedDiagnostics diagnosticsLogger + diagnosticsLogger.GetDiagnostics() + | _ -> Array.ofList delayedLogger.Diagnostics + |> Array.map (fun (diagnostic, severity) -> + FSharpDiagnostic.CreateFromException(diagnostic, severity, range.Zero, suggestNamesForErrors)) - return bootstrapInfoOpt, diagnostics - }) + return bootstrapInfoOpt, diagnostics + } + ) - let ComputeParseFile bootstrapInfo (file: FSharpFile) = + let ComputeParseFile bootstrapInfo (file: FSharpFile) = let key = file.Source.Key, file.IsLastCompiland, file.IsExe - ParseFileCache.Get(key, node { - use _ = Activity.start "ComputeParseFile" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName; Activity.Tags.version, file.Source.Version |] - - let tcConfig = bootstrapInfo.TcConfig - - let diagnosticsLogger = - CompilationDiagnosticLogger("Parse", tcConfig.diagnosticsOptions) - // Return the disposable object that cleans up - use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parse) - let flags = file.IsLastCompiland, file.IsExe - let fileName = file.Source.FileName - let! sourceText = file.Source.GetSource() |> NodeCode.AwaitTask - - let input = - ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) - - return input, diagnosticsLogger.GetDiagnostics(), sourceText - }) + ParseFileCache.Get( + key, + node { + use _ = + Activity.start + "ComputeParseFile" + [| + Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName + Activity.Tags.version, file.Source.Version + |] + + let tcConfig = bootstrapInfo.TcConfig + + let diagnosticsLogger = + CompilationDiagnosticLogger("Parse", tcConfig.diagnosticsOptions) + // Return the disposable object that cleans up + use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parse) + + let flags = file.IsLastCompiland, file.IsExe + let fileName = file.Source.FileName + let! sourceText = file.Source.GetSource() |> NodeCode.AwaitTask + + let input = + ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) + + return input, diagnosticsLogger.GetDiagnostics(), sourceText + } + ) let ComputeDependencyGraphForLastFile (priorSnapshot: FSharpProjectSnapshot) parsedInputs (tcConfig: TcConfig) = let key = priorSnapshot.SourceFiles |> List.map (fun s -> s.Key) - DependencyGraphForLastFileCache.Get(key, node { - - let sourceFiles: FileInProject array = - parsedInputs - |> Seq.toArray - |> Array.mapi (fun idx (input: ParsedInput) -> - { - Idx = idx - FileName = input.FileName - ParsedInput = input - }) - - use _ = Activity.start "ComputeDependencyGraphForLastFile" [| Activity.Tags.fileName, (sourceFiles |> Array.last).FileName |] - - let filePairs = FilePairMap(sourceFiles) - - // TODO: we will probably want to cache and re-use larger graphs if available - let graph = - DependencyResolution.mkGraph tcConfig.compilingFSharpCore filePairs sourceFiles - |> fst - |> Graph.subGraphFor (sourceFiles |> Array.last).Idx - return TransformDependencyGraph(graph, filePairs) - }) + DependencyGraphForLastFileCache.Get( + key, + node { + + let sourceFiles: FileInProject array = + parsedInputs + |> Seq.toArray + |> Array.mapi (fun idx (input: ParsedInput) -> + { + Idx = idx + FileName = input.FileName + ParsedInput = input + }) + + use _ = + Activity.start "ComputeDependencyGraphForLastFile" [| Activity.Tags.fileName, (sourceFiles |> Array.last).FileName |] + + let filePairs = FilePairMap(sourceFiles) + + // TODO: we will probably want to cache and re-use larger graphs if available + let graph = + DependencyResolution.mkGraph tcConfig.compilingFSharpCore filePairs sourceFiles + |> fst + |> Graph.subGraphFor (sourceFiles |> Array.last).Idx + + return TransformDependencyGraph(graph, filePairs) + } + ) - let ComputeTcIntermediate (projectSnapshot: FSharpProjectSnapshot) (fileIndex: FileIndex) (parsedInput: ParsedInput, parseErrors) bootstrapInfo (prevTcInfo: TcInfo) = + let ComputeTcIntermediate + (projectSnapshot: FSharpProjectSnapshot) + (fileIndex: FileIndex) + (parsedInput: ParsedInput, parseErrors) + bootstrapInfo + (prevTcInfo: TcInfo) + = // TODO: we need to construct cache key based on only relevant files from the dependency graph let key = projectSnapshot.UpTo(fileIndex).Key - TcIntermediateCache.Get(key, node { - let input = parsedInput - let fileName = input.FileName - use _ = Activity.start "ComputeTcIntermediate" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + TcIntermediateCache.Get( + key, + node { + let input = parsedInput + let fileName = input.FileName - let tcConfig = bootstrapInfo.TcConfig - let tcGlobals = bootstrapInfo.TcGlobals - let tcImports = bootstrapInfo.TcImports + use _ = + Activity.start "ComputeTcIntermediate" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - let capturingDiagnosticsLogger = CapturingDiagnosticsLogger("TypeCheck") + let tcConfig = bootstrapInfo.TcConfig + let tcGlobals = bootstrapInfo.TcGlobals + let tcImports = bootstrapInfo.TcImports - let diagnosticsLogger = - GetDiagnosticsLoggerFilteringByScopedPragmas( - false, - input.ScopedPragmas, - tcConfig.diagnosticsOptions, - capturingDiagnosticsLogger - ) + let capturingDiagnosticsLogger = CapturingDiagnosticsLogger("TypeCheck") - use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.TypeCheck) + let diagnosticsLogger = + GetDiagnosticsLoggerFilteringByScopedPragmas( + false, + input.ScopedPragmas, + tcConfig.diagnosticsOptions, + capturingDiagnosticsLogger + ) - //beforeFileChecked.Trigger fileName + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.TypeCheck) - ApplyMetaCommandsFromInputToTcConfig(tcConfig, input, Path.GetDirectoryName fileName, tcImports.DependencyProvider) - |> ignore + //beforeFileChecked.Trigger fileName - let sink = TcResultsSinkImpl(tcGlobals) - let hadParseErrors = not (Array.isEmpty parseErrors) + ApplyMetaCommandsFromInputToTcConfig(tcConfig, input, Path.GetDirectoryName fileName, tcImports.DependencyProvider) + |> ignore - let input, moduleNamesDict = - DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input + let sink = TcResultsSinkImpl(tcGlobals) + let hadParseErrors = not (Array.isEmpty parseErrors) - let! finisher = - CheckOneInputWithCallback( - (fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), - tcConfig, - tcImports, - tcGlobals, - None, - TcResultsSink.WithSink sink, - prevTcInfo.tcState, - input, - false - ) - |> NodeCode.FromCancellable + let input, moduleNamesDict = + DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input - //fileChecked.Trigger fileName + let! finisher = + CheckOneInputWithCallback( + (fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), + tcConfig, + tcImports, + tcGlobals, + None, + TcResultsSink.WithSink sink, + prevTcInfo.tcState, + input, + false + ) + |> NodeCode.FromCancellable - let newErrors = - Array.append parseErrors (capturingDiagnosticsLogger.Diagnostics |> List.toArray) + //fileChecked.Trigger fileName - return - { - finisher = finisher - moduleNamesDict = moduleNamesDict - tcDiagnosticsRev = [ newErrors ] - tcDependencyFiles = [ fileName ] - sink = sink - } - }) + let newErrors = + Array.append parseErrors (capturingDiagnosticsLogger.Diagnostics |> List.toArray) + + return + { + finisher = finisher + moduleNamesDict = moduleNamesDict + tcDiagnosticsRev = [ newErrors ] + tcDependencyFiles = [ fileName ] + sink = sink + } + } + ) let mergeIntermediateResults = Array.fold (fun (tcInfo: TcInfo) (tcIntermediate: TcIntermediate) -> - let (tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile), tcState = tcInfo.tcState |> tcIntermediate.finisher.Invoke - let tcEnvAtEndOfFile = if keepAllBackgroundResolutions then tcEnv else tcState.TcEnvFromImpls + let (tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile), tcState = + tcInfo.tcState |> tcIntermediate.finisher.Invoke + + let tcEnvAtEndOfFile = + if keepAllBackgroundResolutions then + tcEnv + else + tcState.TcEnvFromImpls + { tcInfo with tcState = tcState tcEnvAtEndOfFile = tcEnvAtEndOfFile topAttribs = Some topAttribs tcDiagnosticsRev = tcIntermediate.tcDiagnosticsRev @ tcInfo.tcDiagnosticsRev tcDependencyFiles = tcIntermediate.tcDependencyFiles @ tcInfo.tcDependencyFiles - latestCcuSigForFile = Some ccuSigForFile }) + latestCcuSigForFile = Some ccuSigForFile + }) // Type check everything that is needed to check given file let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = @@ -736,77 +794,88 @@ type internal TransparentCompiler let priorSnapshot = projectSnapshot.UpTo file.Source.FileName let key = priorSnapshot.Key - TcPriorCache.Get(key, node { - use _ = Activity.start "ComputeTcPrior" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName |] - - // parse required files - let files = - seq { - yield! bootstrapInfo.SourceFiles |> Seq.takeWhile ((<>) file) - file - } - - let! parsedInputs = - files - |> Seq.map (ComputeParseFile bootstrapInfo) - |> NodeCode.Parallel - - let! graph = ComputeDependencyGraphForLastFile priorSnapshot (parsedInputs |> Seq.map p13) bootstrapInfo.TcConfig - - let fileNames = - parsedInputs - |> Seq.mapi (fun idx (input, _, _) -> idx, Path.GetFileName input.FileName) - |> Map.ofSeq - - let debugGraph = - graph - |> Graph.map (function NodeToTypeCheck.PhysicalFile i -> i, fileNames[i] | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF : {fileNames[i]}") - - Trace.TraceInformation("\n" + (debugGraph |> Graph.serialiseToMermaid)) - - // layers that can be processed in parallel - let layers = Graph.leafSequence graph |> Seq.toList - - // remove the final layer, which should be the target file - let layers = layers |> List.take (layers.Length - 1) + TcPriorCache.Get( + key, + node { + use _ = + Activity.start "ComputeTcPrior" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName |] + + // parse required files + let files = + seq { + yield! bootstrapInfo.SourceFiles |> Seq.takeWhile ((<>) file) + file + } - let rec processLayer (layers: Set list) tcInfo = - node { - match layers with - | [] -> return tcInfo - | layer :: rest -> - let! results = - layer - |> Seq.map (fun fileNode -> - - match fileNode with - | NodeToTypeCheck.PhysicalFile fileIndex -> - let parsedInput, parseErrors, _ = parsedInputs[fileIndex] - ComputeTcIntermediate projectSnapshot fileIndex (parsedInput, parseErrors) bootstrapInfo tcInfo - | NodeToTypeCheck.ArtificialImplFile fileIndex -> - - let finisher tcState = - let parsedInput, _parseErrors, _ = parsedInputs[fileIndex] - let prefixPathOpt = None - // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. - AddSignatureResultToTcImplEnv(bootstrapInfo.TcImports, bootstrapInfo.TcGlobals, prefixPathOpt, TcResultsSink.NoSink, tcState, parsedInput) tcState - let tcIntermediate = - { - finisher = finisher - moduleNamesDict = tcInfo.moduleNamesDict - tcDiagnosticsRev = [] - tcDependencyFiles = [] - sink = Unchecked.defaultof<_> - } - - node.Return(tcIntermediate)) - |> NodeCode.Parallel - - return! processLayer rest (mergeIntermediateResults tcInfo results) - } + let! parsedInputs = files |> Seq.map (ComputeParseFile bootstrapInfo) |> NodeCode.Parallel + + let! graph = ComputeDependencyGraphForLastFile priorSnapshot (parsedInputs |> Seq.map p13) bootstrapInfo.TcConfig + + let fileNames = + parsedInputs + |> Seq.mapi (fun idx (input, _, _) -> idx, Path.GetFileName input.FileName) + |> Map.ofSeq + + let debugGraph = + graph + |> Graph.map (function + | NodeToTypeCheck.PhysicalFile i -> i, fileNames[i] + | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF : {fileNames[i]}") + + Trace.TraceInformation("\n" + (debugGraph |> Graph.serialiseToMermaid)) + + // layers that can be processed in parallel + let layers = Graph.leafSequence graph |> Seq.toList + + // remove the final layer, which should be the target file + let layers = layers |> List.take (layers.Length - 1) + + let rec processLayer (layers: Set list) tcInfo = + node { + match layers with + | [] -> return tcInfo + | layer :: rest -> + let! results = + layer + |> Seq.map (fun fileNode -> + + match fileNode with + | NodeToTypeCheck.PhysicalFile fileIndex -> + let parsedInput, parseErrors, _ = parsedInputs[fileIndex] + ComputeTcIntermediate projectSnapshot fileIndex (parsedInput, parseErrors) bootstrapInfo tcInfo + | NodeToTypeCheck.ArtificialImplFile fileIndex -> + + let finisher tcState = + let parsedInput, _parseErrors, _ = parsedInputs[fileIndex] + let prefixPathOpt = None + // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. + AddSignatureResultToTcImplEnv + (bootstrapInfo.TcImports, + bootstrapInfo.TcGlobals, + prefixPathOpt, + TcResultsSink.NoSink, + tcState, + parsedInput) + tcState + + let tcIntermediate = + { + finisher = finisher + moduleNamesDict = tcInfo.moduleNamesDict + tcDiagnosticsRev = [] + tcDependencyFiles = [] + sink = Unchecked.defaultof<_> + } + + node.Return(tcIntermediate)) + |> NodeCode.Parallel + + return! processLayer rest (mergeIntermediateResults tcInfo results) + } - return! processLayer layers bootstrapInfo.InitialTcInfo - }) + return! processLayer layers bootstrapInfo.InitialTcInfo + } + ) let getParseResult (bootstrapInfo: BootstrapInfo) creationDiags fileName = node { @@ -832,7 +901,8 @@ type internal TransparentCompiler parseHadErrors = (parseDiagnostics.Length > 0), // TODO: check if we really need this in parse results dependencyFiles = [||] - ), sourceText + ), + sourceText } let emptyParseResult fileName diagnostics = @@ -841,59 +911,72 @@ type internal TransparentCompiler let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = let key = fileName, projectSnapshot.Key - ParseAndCheckFileInProjectCache.Get(key, node { - - use _ = Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - - match! ComputeBootstrapInfo projectSnapshot with - | None, creationDiags -> - return emptyParseResult fileName creationDiags, FSharpCheckFileAnswer.Aborted - - | Some bootstrapInfo, creationDiags -> - - let file = bootstrapInfo.GetFile fileName - - let! parseResults, sourceText = getParseResult bootstrapInfo creationDiags fileName - let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot - - let! checkResults = - FSharpCheckFileResults.CheckOneFile( - parseResults, - sourceText, - fileName, - projectSnapshot.ProjectFileName, - bootstrapInfo.TcConfig, - bootstrapInfo.TcGlobals, - bootstrapInfo.TcImports, - tcInfo.tcState, - tcInfo.moduleNamesDict, - bootstrapInfo.LoadClosure, - tcInfo.TcDiagnostics, - projectSnapshot.IsIncompleteTypeCheckEnvironment, - projectSnapshot.ToOptions(), - None, - Array.ofList tcInfo.tcDependencyFiles, - creationDiags, - parseResults.Diagnostics, - keepAssemblyContents, - suggestNamesForErrors - ) - |> NodeCode.FromCancellable - - return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) - }) + ParseAndCheckFileInProjectCache.Get( + key, + node { + + use _ = + Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + + match! ComputeBootstrapInfo projectSnapshot with + | None, creationDiags -> return emptyParseResult fileName creationDiags, FSharpCheckFileAnswer.Aborted + + | Some bootstrapInfo, creationDiags -> + + let file = bootstrapInfo.GetFile fileName + + let! parseResults, sourceText = getParseResult bootstrapInfo creationDiags fileName + + let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot + + let! checkResults = + FSharpCheckFileResults.CheckOneFile( + parseResults, + sourceText, + fileName, + projectSnapshot.ProjectFileName, + bootstrapInfo.TcConfig, + bootstrapInfo.TcGlobals, + bootstrapInfo.TcImports, + tcInfo.tcState, + tcInfo.moduleNamesDict, + bootstrapInfo.LoadClosure, + tcInfo.TcDiagnostics, + projectSnapshot.IsIncompleteTypeCheckEnvironment, + projectSnapshot.ToOptions(), + None, + Array.ofList tcInfo.tcDependencyFiles, + creationDiags, + parseResults.Diagnostics, + keepAssemblyContents, + suggestNamesForErrors + ) + |> NodeCode.FromCancellable + + return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) + } + ) let tryGetSink fileName (projectSnapshot: FSharpProjectSnapshot) = node { - use _ = Activity.start "ComputeSemanticClassification" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + use _ = + Activity.start "ComputeSemanticClassification" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] match! ComputeBootstrapInfo projectSnapshot with | None, _ -> return None | Some bootstrapInfo, _creationDiags -> let file = - bootstrapInfo.SourceFiles |> List.tryFind (fun f -> f.Source.FileName = fileName) |> Option.defaultWith (fun _ -> failwith ($"File {fileName} not found in project snapshot. Files in project: \n\n" + (bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n"))) + bootstrapInfo.SourceFiles + |> List.tryFind (fun f -> f.Source.FileName = fileName) + |> Option.defaultWith (fun _ -> + failwith ( + $"File {fileName} not found in project snapshot. Files in project: \n\n" + + (bootstrapInfo.SourceFiles + |> Seq.map (fun f -> f.Source.FileName) + |> String.concat " \n") + )) let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file @@ -901,53 +984,85 @@ type internal TransparentCompiler let fileIndex = projectSnapshot.IndexOf fileName let! { sink = sink } = ComputeTcIntermediate projectSnapshot fileIndex (parseTree, parseDiagnostics) bootstrapInfo tcInfo - return Some (sink, bootstrapInfo) + return Some(sink, bootstrapInfo) } - let ComputeSemanticClassification(fileName: string, projectSnapshot: FSharpProjectSnapshot) = + let ComputeSemanticClassification (fileName: string, projectSnapshot: FSharpProjectSnapshot) = let key = (projectSnapshot.UpTo fileName).Key - SemanticClassificationCache.Get(key, node { - use _ = Activity.start "ComputeSemanticClassification" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - let! sinkOpt = tryGetSink fileName projectSnapshot - return - sinkOpt - |> Option.bind (fun (sink, bootstrapInfo) -> - let sResolutions = sink.GetResolutions() - let semanticClassification = sResolutions.GetSemanticClassification(bootstrapInfo.TcGlobals, bootstrapInfo.TcImports.GetImportMap(), sink.GetFormatSpecifierLocations(), None) - let sckBuilder = SemanticClassificationKeyStoreBuilder() - sckBuilder.WriteAll semanticClassification + SemanticClassificationCache.Get( + key, + node { + use _ = + Activity.start "ComputeSemanticClassification" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + + let! sinkOpt = tryGetSink fileName projectSnapshot - sckBuilder.TryBuildAndReset()) - |> Option.map (fun sck -> sck.GetView()) - }) + return + sinkOpt + |> Option.bind (fun (sink, bootstrapInfo) -> + let sResolutions = sink.GetResolutions() - let ComputeItemKeyStore(fileName: string, projectSnapshot: FSharpProjectSnapshot) = + let semanticClassification = + sResolutions.GetSemanticClassification( + bootstrapInfo.TcGlobals, + bootstrapInfo.TcImports.GetImportMap(), + sink.GetFormatSpecifierLocations(), + None + ) + + let sckBuilder = SemanticClassificationKeyStoreBuilder() + sckBuilder.WriteAll semanticClassification + + sckBuilder.TryBuildAndReset()) + |> Option.map (fun sck -> sck.GetView()) + } + ) + + let ComputeItemKeyStore (fileName: string, projectSnapshot: FSharpProjectSnapshot) = let key = (projectSnapshot.UpTo fileName).Key - ItemKeyStoreCache.Get(key, node { - use _ = Activity.start "ComputeItemKeyStore" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - let! sinkOpt = tryGetSink fileName projectSnapshot - return - sinkOpt - |> Option.bind (fun (sink, _) -> - let sResolutions = sink.GetResolutions() - - let builder = ItemKeyStoreBuilder() - let preventDuplicates = HashSet({ new IEqualityComparer with - member _.Equals((s1, e1): struct(pos * pos), (s2, e2): struct(pos * pos)) = Position.posEq s1 s2 && Position.posEq e1 e2 - member _.GetHashCode o = o.GetHashCode() }) - sResolutions.CapturedNameResolutions - |> Seq.iter (fun cnr -> - let r = cnr.Range - if preventDuplicates.Add struct(r.Start, r.End) then - builder.Write(cnr.Range, cnr.Item)) - - builder.TryBuildAndReset()) - }) + + ItemKeyStoreCache.Get( + key, + node { + use _ = + Activity.start "ComputeItemKeyStore" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + + let! sinkOpt = tryGetSink fileName projectSnapshot + + return + sinkOpt + |> Option.bind (fun (sink, _) -> + let sResolutions = sink.GetResolutions() + + let builder = ItemKeyStoreBuilder() + + let preventDuplicates = + HashSet( + { new IEqualityComparer with + member _.Equals((s1, e1): struct (pos * pos), (s2, e2): struct (pos * pos)) = + Position.posEq s1 s2 && Position.posEq e1 e2 + + member _.GetHashCode o = o.GetHashCode() + } + ) + + sResolutions.CapturedNameResolutions + |> Seq.iter (fun cnr -> + let r = cnr.Range + + if preventDuplicates.Add struct (r.Start, r.End) then + builder.Write(cnr.Range, cnr.Item)) + + builder.TryBuildAndReset()) + } + ) member _.ParseFile(fileName, projectSnapshot: FSharpProjectSnapshot, _userOpName) = node { - use _ = Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + use _ = + Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + match! ComputeBootstrapInfo projectSnapshot with | None, creationDiags -> return emptyParseResult fileName creationDiags | Some bootstrapInfo, creationDiags -> @@ -959,14 +1074,9 @@ type internal TransparentCompiler ignore userOpName ComputeParseAndCheckFileInProject fileName projectSnapshot - member _.FindReferencesInFile - ( - fileName: string, - projectSnapshot: FSharpProjectSnapshot, - symbol: FSharpSymbol, - userOpName: string - ) = + member _.FindReferencesInFile(fileName: string, projectSnapshot: FSharpProjectSnapshot, symbol: FSharpSymbol, userOpName: string) = ignore userOpName + node { match! ComputeItemKeyStore(fileName, projectSnapshot) with | None -> return Seq.empty @@ -1031,7 +1141,8 @@ type internal TransparentCompiler ) : NodeCode> = backgroundCompiler.FindReferencesInFile(fileName, options, symbol, canInvalidateProject, userOpName) - member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) + member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = + this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) member _.FrameworkImportsCache: FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache @@ -1092,12 +1203,7 @@ type internal TransparentCompiler userOpName ) - member _.GetSemanticClassificationForFile - ( - fileName: string, - snapshot: FSharpProjectSnapshot, - userOpName: string - ) = + member _.GetSemanticClassificationForFile(fileName: string, snapshot: FSharpProjectSnapshot, userOpName: string) = ignore userOpName ComputeSemanticClassification(fileName, snapshot) From c4329248c6d0601733cc149605d5ce428f02b743 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 18 May 2023 12:17:02 +0200 Subject: [PATCH 038/222] ... --- src/Compiler/Service/TransparentCompiler.fs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index bd0aa272ae8..0edb0c46172 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -279,6 +279,9 @@ type internal TransparentCompiler match capturedImportsInvalidated.TryGetTarget() with | true, tg -> tg.Trigger() | _ -> ())) +#endif +#if NO_TYPEPROVIDERS + ignore importsInvalidatedByTypeProvider #endif return tcImports with exn -> @@ -924,10 +927,9 @@ type internal TransparentCompiler | Some bootstrapInfo, creationDiags -> - let file = bootstrapInfo.GetFile fileName - let! parseResults, sourceText = getParseResult bootstrapInfo creationDiags fileName + let file = bootstrapInfo.GetFile fileName let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot let! checkResults = @@ -954,7 +956,7 @@ type internal TransparentCompiler ) |> NodeCode.FromCancellable - return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) + return parseResults, FSharpCheckFileAnswer.Succeeded checkResults } ) @@ -967,16 +969,7 @@ type internal TransparentCompiler | None, _ -> return None | Some bootstrapInfo, _creationDiags -> - let file = - bootstrapInfo.SourceFiles - |> List.tryFind (fun f -> f.Source.FileName = fileName) - |> Option.defaultWith (fun _ -> - failwith ( - $"File {fileName} not found in project snapshot. Files in project: \n\n" - + (bootstrapInfo.SourceFiles - |> Seq.map (fun f -> f.Source.FileName) - |> String.concat " \n") - )) + let file = bootstrapInfo.GetFile fileName let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file From 127ed3b604d6c04513ae57f06575c82f508d5f0d Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 18 May 2023 13:54:52 +0200 Subject: [PATCH 039/222] Files that are not depended on don't ivalidate cache --- src/Compiler/Service/FSharpCheckerResults.fs | 9 ++++ src/Compiler/Service/FSharpCheckerResults.fsi | 3 ++ src/Compiler/Service/TransparentCompiler.fs | 41 ++++++++++++------- .../FSharpChecker/TransparentCompiler.fs | 39 ++++++++++++++++++ ...ervice.SurfaceArea.netstandard20.debug.bsl | 6 +++ ...vice.SurfaceArea.netstandard20.release.bsl | 2 + 6 files changed, 85 insertions(+), 15 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index c2271aa1de4..449f50f45ba 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -208,6 +208,15 @@ type FSharpProjectSnapshot = member this.UpTo fileName = this.UpTo(this.IndexOf fileName) + member this.OnlyWith fileIndexes = + { this with + SourceFiles = + fileIndexes + |> Set.toList + |> List.sort + |> List.choose (fun x -> this.SourceFiles |> List.tryItem x) + } + member this.Key = { ProjectFileName = this.ProjectFileName diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index ea55a296ec4..a84a9a7c3aa 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -125,6 +125,9 @@ type FSharpProjectSnapshot = /// A snapshot of the same project but only up to the given file (including). member UpTo: fileName: string -> FSharpProjectSnapshot + /// A snapshot of the same project but only with source files specified by given indexes. + member OnlyWith: fileIndexes: Set -> FSharpProjectSnapshot + /// A snapshot of the same project with file versions removed. member WithoutFileVersions: FSharpProjectSnapshot diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 0edb0c46172..51be01b1d73 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -121,8 +121,7 @@ type internal TransparentCompiler let TcPriorCache = AsyncMemoize(triggerCacheEvent "TcPrior") let TcIntermediateCache = AsyncMemoize(triggerCacheEvent "TcIntermediate") - let DependencyGraphForLastFileCache = - AsyncMemoize(triggerCacheEvent "DependencyGraphForLastFile") + let DependencyGraphCache = AsyncMemoize(triggerCacheEvent "DependencyGraph") let SemanticClassificationCache = AsyncMemoize(triggerCacheEvent "SemanticClassification") @@ -663,10 +662,10 @@ type internal TransparentCompiler } ) - let ComputeDependencyGraphForLastFile (priorSnapshot: FSharpProjectSnapshot) parsedInputs (tcConfig: TcConfig) = + let ComputeDependencyGraph (priorSnapshot: FSharpProjectSnapshot) parsedInputs (tcConfig: TcConfig) = let key = priorSnapshot.SourceFiles |> List.map (fun s -> s.Key) - DependencyGraphForLastFileCache.Get( + DependencyGraphCache.Get( key, node { @@ -681,7 +680,7 @@ type internal TransparentCompiler }) use _ = - Activity.start "ComputeDependencyGraphForLastFile" [| Activity.Tags.fileName, (sourceFiles |> Array.last).FileName |] + Activity.start "ComputeDependencyGraph" [| Activity.Tags.fileName, (sourceFiles |> Array.last).FileName |] let filePairs = FilePairMap(sourceFiles) @@ -691,22 +690,24 @@ type internal TransparentCompiler |> fst |> Graph.subGraphFor (sourceFiles |> Array.last).Idx - return TransformDependencyGraph(graph, filePairs) + let fileIndexes = + graph.Values |> Seq.collect id |> Seq.append graph.Keys |> Seq.distinct |> Set + + return TransformDependencyGraph(graph, filePairs), fileIndexes } ) let ComputeTcIntermediate (projectSnapshot: FSharpProjectSnapshot) + (dependencyFiles: Set) (fileIndex: FileIndex) (parsedInput: ParsedInput, parseErrors) bootstrapInfo (prevTcInfo: TcInfo) = - // TODO: we need to construct cache key based on only relevant files from the dependency graph - let key = projectSnapshot.UpTo(fileIndex).Key TcIntermediateCache.Get( - key, + projectSnapshot.UpTo(fileIndex).OnlyWith(dependencyFiles).Key, node { let input = parsedInput let fileName = input.FileName @@ -812,7 +813,7 @@ type internal TransparentCompiler let! parsedInputs = files |> Seq.map (ComputeParseFile bootstrapInfo) |> NodeCode.Parallel - let! graph = ComputeDependencyGraphForLastFile priorSnapshot (parsedInputs |> Seq.map p13) bootstrapInfo.TcConfig + let! graph, dependencyFiles = ComputeDependencyGraph priorSnapshot (parsedInputs |> Seq.map p13) bootstrapInfo.TcConfig let fileNames = parsedInputs @@ -845,7 +846,14 @@ type internal TransparentCompiler match fileNode with | NodeToTypeCheck.PhysicalFile fileIndex -> let parsedInput, parseErrors, _ = parsedInputs[fileIndex] - ComputeTcIntermediate projectSnapshot fileIndex (parsedInput, parseErrors) bootstrapInfo tcInfo + + ComputeTcIntermediate + projectSnapshot + dependencyFiles + fileIndex + (parsedInput, parseErrors) + bootstrapInfo + tcInfo | NodeToTypeCheck.ArtificialImplFile fileIndex -> let finisher tcState = @@ -876,7 +884,8 @@ type internal TransparentCompiler return! processLayer rest (mergeIntermediateResults tcInfo results) } - return! processLayer layers bootstrapInfo.InitialTcInfo + let! tcInfo = processLayer layers bootstrapInfo.InitialTcInfo + return tcInfo, dependencyFiles } ) @@ -930,7 +939,7 @@ type internal TransparentCompiler let! parseResults, sourceText = getParseResult bootstrapInfo creationDiags fileName let file = bootstrapInfo.GetFile fileName - let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot + let! tcInfo, _ = ComputeTcPrior file bootstrapInfo projectSnapshot let! checkResults = FSharpCheckFileResults.CheckOneFile( @@ -971,11 +980,13 @@ type internal TransparentCompiler let file = bootstrapInfo.GetFile fileName - let! tcInfo = ComputeTcPrior file bootstrapInfo projectSnapshot + let! tcInfo, dependencyFiles = ComputeTcPrior file bootstrapInfo projectSnapshot let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file let fileIndex = projectSnapshot.IndexOf fileName - let! { sink = sink } = ComputeTcIntermediate projectSnapshot fileIndex (parseTree, parseDiagnostics) bootstrapInfo tcInfo + + let! { sink = sink } = + ComputeTcIntermediate projectSnapshot dependencyFiles fileIndex (parseTree, parseDiagnostics) bootstrapInfo tcInfo return Some(sink, bootstrapInfo) } diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index acb508dea41..44f7f6da304 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -206,3 +206,42 @@ let ``We don't check files that are not depended on`` () = Assert.Equal([Started; Finished], intermediateTypeChecks["FileFirst.fs"]) Assert.Equal([Started; Finished], intermediateTypeChecks["FileThird.fs"]) Assert.False (intermediateTypeChecks.ContainsKey "FileSecond.fs") + +[] +let ``Files that are not depended on don't ivalidate cache`` () = + let project = SyntheticProject.Create( + sourceFile "First" [], + sourceFile "Second" ["First"], + sourceFile "Third" ["First"], + sourceFile "Last" ["Third"]) + + let cacheEvents = ResizeArray() + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + checkFile "Last" expectOk + withChecker (fun checker -> checker.CacheEvent.Add cacheEvents.Add) + updateFile "Second" updatePublicSurface + checkFile "Last" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheEvents + |> Seq.choose (function + | ("TcIntermediate", e, k) -> Some ((k :?> FSharpProjectSnapshotKey).LastFile |> fst |> Path.GetFileName, e) + | _ -> None) + |> Seq.groupBy fst + |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) + |> Map + + let graphConstructions = + cacheEvents + |> Seq.choose (function + | ("DependencyGraph", e, k) -> Some ((k :?> FSharpFileKey list) |> List.last |> fst |> Path.GetFileName, e) + | _ -> None) + |> Seq.groupBy fst + |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) + |> Map + + Assert.Equal([Started; Finished], graphConstructions["FileLast.fs"]) + + Assert.True intermediateTypeChecks.IsEmpty diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 03aeafb299a..3fb4db8a604 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2060,9 +2060,11 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFileInProject(System.String, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] CheckFileInProjectAllowingStaleCachedResults(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyFileChanged(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyProjectCleaned(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2199,6 +2201,7 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean UseScriptResolutionR FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_IsIncompleteTypeCheckEnvironment() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_UseScriptResolutionRules() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions ToOptions() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot OnlyWith(Microsoft.FSharp.Collections.FSharpSet`1[System.Int32]) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(Int32) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(System.String) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot WithoutFileVersions @@ -2207,6 +2210,7 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey get_Key() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 IndexOf(System.String) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot] SourceFiles FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot] get_SourceFiles() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot] ReferencedProjects @@ -2251,6 +2255,8 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collecti FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String ProjectFileName FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String get_ProjectFileName() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.Tuple`2[System.String,System.String] LastFile +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.Tuple`2[System.String,System.String] get_LastFile() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Void .ctor(System.String, Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey], Boolean, Boolean) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: Boolean Equals(System.Object) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: FSharp.Compiler.CodeAnalysis.FSharpReferencedProject CreateFSharp(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 5415b502e06..3fb4db8a604 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2064,6 +2064,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyFileChanged(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyProjectCleaned(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2200,6 +2201,7 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean UseScriptResolutionR FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_IsIncompleteTypeCheckEnvironment() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_UseScriptResolutionRules() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions ToOptions() +FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot OnlyWith(Microsoft.FSharp.Collections.FSharpSet`1[System.Int32]) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(Int32) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(System.String) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot WithoutFileVersions From fe60fb2f02acd46b49e4be6a84b5162cc227ab37 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 18 May 2023 14:21:35 +0200 Subject: [PATCH 040/222] try deflake linux test --- .../FSharpChecker/TransparentCompiler.fs | 8 ++++++-- tests/FSharp.Test.Utilities/ProjectGeneration.fs | 10 +++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 44f7f6da304..842dcefaf5c 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -189,7 +189,11 @@ let ``We don't check files that are not depended on`` () = let cacheEvents = ResizeArray() ProjectWorkflowBuilder(project, useTransparentCompiler = true) { - withChecker (fun checker -> checker.CacheEvent.Add cacheEvents.Add) + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.CacheEvent.Add cacheEvents.Add + }) updateFile "First" updatePublicSurface checkFile "Last" expectOk } |> ignore @@ -208,7 +212,7 @@ let ``We don't check files that are not depended on`` () = Assert.False (intermediateTypeChecks.ContainsKey "FileSecond.fs") [] -let ``Files that are not depended on don't ivalidate cache`` () = +let ``Files that are not depended on don't invalidate cache`` () = let project = SyntheticProject.Create( sourceFile "First" [], sourceFile "Second" ["First"], diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 29e845d1ed8..6861c256a26 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -700,7 +700,7 @@ type ProjectWorkflowBuilder [] member this.WithProject(workflow: Async, f) = - workflow |> mapProjectAsync (fun project -> + workflow |> mapProjectAsync (fun project -> async { do! f project checker return project @@ -714,6 +714,14 @@ type ProjectWorkflowBuilder return ctx } + [] + member this.WithChecker(workflow: Async, f) = + async { + let! ctx = workflow + do! f checker + return ctx + } + /// Change contents of given file using `processFile` function. /// Does not save the file to disk. [] From 02717609200c2caebc31b7af61428fc30b301335 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 18 May 2023 19:21:03 +0200 Subject: [PATCH 041/222] reduce surface area --- src/Compiler/Service/BackgroundCompiler.fs | 2 +- src/Compiler/Service/FSharpCheckerResults.fsi | 8 +-- src/Compiler/Service/IncrementalBuild.fsi | 2 +- ...ervice.SurfaceArea.netstandard20.debug.bsl | 57 ------------------- ...vice.SurfaceArea.netstandard20.release.bsl | 57 ------------------- 5 files changed, 6 insertions(+), 120 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index cc25ed2bed3..ed8675b41fb 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -184,7 +184,7 @@ module EnvMisc = GetEnvInteger "FCS_frameworkTcImportsCacheStrongSizeDefault" 8 [] -module Helpers = +module internal Helpers = /// Determine whether two (fileName,options) keys are identical w.r.t. affect on checking let AreSameForChecking2 ((fileName1: string, options1: FSharpProjectOptions), (fileName2, options2)) = diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index a84a9a7c3aa..6f77a0bcc99 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -40,9 +40,9 @@ type internal DelayedILModuleReader = /// Unused in this API type public FSharpUnresolvedReferencesSet = internal FSharpUnresolvedReferencesSet of UnresolvedAssemblyReference list -type FSharpFileKey = string * string +type internal FSharpFileKey = string * string -type FSharpProjectSnapshotKey = +type internal FSharpProjectSnapshotKey = { ProjectFileName: string SourceFiles: FSharpFileKey list OtherOptions: string list @@ -60,7 +60,7 @@ type FSharpFileSnapshot = Version: string GetSource: unit -> Task } - member Key: FSharpFileKey + member internal Key: FSharpFileKey [] type FSharpProjectSnapshot = @@ -131,7 +131,7 @@ type FSharpProjectSnapshot = /// A snapshot of the same project with file versions removed. member WithoutFileVersions: FSharpProjectSnapshot - member Key: FSharpProjectSnapshotKey + member internal Key: FSharpProjectSnapshotKey and [] public FSharpReferencedProjectSnapshot = internal diff --git a/src/Compiler/Service/IncrementalBuild.fsi b/src/Compiler/Service/IncrementalBuild.fsi index bd54cf4f3f5..50c7201195c 100644 --- a/src/Compiler/Service/IncrementalBuild.fsi +++ b/src/Compiler/Service/IncrementalBuild.fsi @@ -23,7 +23,7 @@ open FSharp.Compiler.Text open FSharp.Compiler.TypedTree open FSharp.Compiler.BuildGraph -type FrameworkImportsCacheKey = +type internal FrameworkImportsCacheKey = | FrameworkImportsCacheKey of resolvedpath: string list * assemblyName: string * diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 3fb4db8a604..db548f930f9 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2098,8 +2098,6 @@ FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String Version FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String get_FileName() FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String get_Version() -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.Tuple`2[System.String,System.String] Key -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.Tuple`2[System.String,System.String] get_Key() FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]]) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsBindingALambdaAtPosition(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPosContainedInApplication(FSharp.Compiler.Text.Position) @@ -2206,8 +2204,6 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(System.String) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot WithoutFileVersions FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot get_WithoutFileVersions() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey Key -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey get_Key() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode(System.Collections.IEqualityComparer) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 IndexOf(System.String) @@ -2234,30 +2230,6 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String ProjectFileNam FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String get_ProjectFileName() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean Equals(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean Equals(System.Object) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean Equals(System.Object, System.Collections.IEqualityComparer) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean IsIncompleteTypeCheckEnvironment -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean UseScriptResolutionRules -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean get_IsIncompleteTypeCheckEnvironment() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean get_UseScriptResolutionRules() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 CompareTo(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 CompareTo(System.Object) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 CompareTo(System.Object, System.Collections.IComparer) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 GetHashCode() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 GetHashCode(System.Collections.IEqualityComparer) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey] ReferencedProjects -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey] get_ReferencedProjects() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] OtherOptions -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_OtherOptions() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]] SourceFiles -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]] get_SourceFiles() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String ProjectFileName -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String ToString() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String get_ProjectFileName() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.Tuple`2[System.String,System.String] LastFile -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.Tuple`2[System.String,System.String] get_LastFile() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Void .ctor(System.String, Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey], Boolean, Boolean) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: Boolean Equals(System.Object) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: FSharp.Compiler.CodeAnalysis.FSharpReferencedProject CreateFSharp(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: FSharp.Compiler.CodeAnalysis.FSharpReferencedProject CreateFromILModuleReader(System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.AbstractIL.ILBinaryReader+ILModuleReader]) @@ -2309,35 +2281,6 @@ FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: Boolean Equals(Syste FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: Int32 GetHashCode() FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: Int32 GetHashCode(System.Collections.IEqualityComparer) FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: System.String ToString() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Boolean Equals(FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Boolean Equals(System.Object) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Boolean Equals(System.Object, System.Collections.IEqualityComparer) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey NewFrameworkImportsCacheKey(Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, System.Decimal) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 CompareTo(FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 CompareTo(System.Object) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 CompareTo(System.Object, System.Collections.IComparer) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 GetHashCode() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 GetHashCode(System.Collections.IEqualityComparer) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 Tag -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 get_Tag() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_resolvedpath() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_targetFrameworkDirectories() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] resolvedpath -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] targetFrameworkDirectories -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.Decimal get_langVersion() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.Decimal langVersion -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String ToString() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String assemblyName -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String fsharpBinaries -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String get_assemblyName() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String get_fsharpBinaries() -FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSameForChecking2(System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) -FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSameForChecking3(System.Tuple`3[System.String,System.Int64,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`3[System.String,System.Int64,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) -FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSameForParsing[a](System.Tuple`3[System.String,System.Int64,a], System.Tuple`3[System.String,System.Int64,a]) -FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSimilarForParsing[a,b,c,d,e](System.Tuple`3[a,b,c], System.Tuple`3[a,d,e]) -FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSubsumable2(System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) -FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSubsumable3[a,b](System.Tuple`3[System.String,a,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`3[System.String,b,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) -FSharp.Compiler.CodeAnalysis.Helpers: Boolean NamesContainAttribute(FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Collections.FSharpSet`1[System.String]) FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver: FSharp.Compiler.CodeAnalysis.LegacyResolvedFile[] Resolve(FSharp.Compiler.CodeAnalysis.LegacyResolutionEnvironment, System.Tuple`2[System.String,System.String][], System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpFunc`2[System.Boolean,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.Unit]]]) FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver: System.String DotNetFrameworkReferenceAssembliesRootDirectory FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver: System.String HighestInstalledNetFrameworkVersion() diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 3fb4db8a604..db548f930f9 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2098,8 +2098,6 @@ FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String Version FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String get_FileName() FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String get_Version() -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.Tuple`2[System.String,System.String] Key -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.Tuple`2[System.String,System.String] get_Key() FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]]) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsBindingALambdaAtPosition(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPosContainedInApplication(FSharp.Compiler.Text.Position) @@ -2206,8 +2204,6 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(System.String) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot WithoutFileVersions FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot get_WithoutFileVersions() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey Key -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey get_Key() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode(System.Collections.IEqualityComparer) FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 IndexOf(System.String) @@ -2234,30 +2230,6 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String ProjectFileNam FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String get_ProjectFileName() FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean Equals(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean Equals(System.Object) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean Equals(System.Object, System.Collections.IEqualityComparer) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean IsIncompleteTypeCheckEnvironment -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean UseScriptResolutionRules -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean get_IsIncompleteTypeCheckEnvironment() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Boolean get_UseScriptResolutionRules() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 CompareTo(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 CompareTo(System.Object) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 CompareTo(System.Object, System.Collections.IComparer) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 GetHashCode() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Int32 GetHashCode(System.Collections.IEqualityComparer) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey] ReferencedProjects -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey] get_ReferencedProjects() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] OtherOptions -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_OtherOptions() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]] SourceFiles -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]] get_SourceFiles() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String ProjectFileName -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String ToString() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.String get_ProjectFileName() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.Tuple`2[System.String,System.String] LastFile -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: System.Tuple`2[System.String,System.String] get_LastFile() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey: Void .ctor(System.String, Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[System.String,System.String]], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshotKey], Boolean, Boolean) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: Boolean Equals(System.Object) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: FSharp.Compiler.CodeAnalysis.FSharpReferencedProject CreateFSharp(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: FSharp.Compiler.CodeAnalysis.FSharpReferencedProject CreateFromILModuleReader(System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.DateTime], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,FSharp.Compiler.AbstractIL.ILBinaryReader+ILModuleReader]) @@ -2309,35 +2281,6 @@ FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: Boolean Equals(Syste FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: Int32 GetHashCode() FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: Int32 GetHashCode(System.Collections.IEqualityComparer) FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet: System.String ToString() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Boolean Equals(FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Boolean Equals(System.Object) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Boolean Equals(System.Object, System.Collections.IEqualityComparer) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey NewFrameworkImportsCacheKey(Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, System.Decimal) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 CompareTo(FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 CompareTo(System.Object) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 CompareTo(System.Object, System.Collections.IComparer) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 GetHashCode() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 GetHashCode(System.Collections.IEqualityComparer) -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 Tag -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Int32 get_Tag() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_resolvedpath() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_targetFrameworkDirectories() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] resolvedpath -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: Microsoft.FSharp.Collections.FSharpList`1[System.String] targetFrameworkDirectories -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.Decimal get_langVersion() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.Decimal langVersion -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String ToString() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String assemblyName -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String fsharpBinaries -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String get_assemblyName() -FSharp.Compiler.CodeAnalysis.FrameworkImportsCacheKey: System.String get_fsharpBinaries() -FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSameForChecking2(System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) -FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSameForChecking3(System.Tuple`3[System.String,System.Int64,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`3[System.String,System.Int64,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) -FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSameForParsing[a](System.Tuple`3[System.String,System.Int64,a], System.Tuple`3[System.String,System.Int64,a]) -FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSimilarForParsing[a,b,c,d,e](System.Tuple`3[a,b,c], System.Tuple`3[a,d,e]) -FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSubsumable2(System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`2[System.String,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) -FSharp.Compiler.CodeAnalysis.Helpers: Boolean AreSubsumable3[a,b](System.Tuple`3[System.String,a,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], System.Tuple`3[System.String,b,FSharp.Compiler.CodeAnalysis.FSharpProjectOptions]) -FSharp.Compiler.CodeAnalysis.Helpers: Boolean NamesContainAttribute(FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Collections.FSharpSet`1[System.String]) FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver: FSharp.Compiler.CodeAnalysis.LegacyResolvedFile[] Resolve(FSharp.Compiler.CodeAnalysis.LegacyResolutionEnvironment, System.Tuple`2[System.String,System.String][], System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, System.String, Microsoft.FSharp.Collections.FSharpList`1[System.String], System.String, Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpFunc`2[System.Boolean,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.Unit]]]) FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver: System.String DotNetFrameworkReferenceAssembliesRootDirectory FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver: System.String HighestInstalledNetFrameworkVersion() From b0758ed8d6260182848de78c65d1d8c9aa833fd3 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 19 May 2023 18:42:53 +0200 Subject: [PATCH 042/222] benchmark --- src/Compiler/Service/TransparentCompiler.fs | 34 ++++-- .../ProjectGeneration.fs | 28 +++-- .../BackgroundCompilerBenchmarks.fs | 112 ++++++++++++++++++ 3 files changed, 149 insertions(+), 25 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 51be01b1d73..5f32e4325cf 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -690,10 +690,30 @@ type internal TransparentCompiler |> fst |> Graph.subGraphFor (sourceFiles |> Array.last).Idx + let fileIndexes = graph.Values |> Seq.collect id |> Seq.append graph.Keys |> Seq.distinct |> Set - return TransformDependencyGraph(graph, filePairs), fileIndexes + let nodeGraph = TransformDependencyGraph(graph, filePairs) + + let fileNames = + parsedInputs + |> Seq.mapi (fun idx input -> idx, Path.GetFileName input.FileName) + |> Map.ofSeq + + let debugGraph = + nodeGraph + |> Graph.map (function + | NodeToTypeCheck.PhysicalFile i -> i, fileNames[i] + | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF : {fileNames[i]}") + |> Graph.serialiseToMermaid + + Trace.TraceInformation("\n" + debugGraph) + + if Activity.Current <> null then + Activity.Current.AddTag("graph", debugGraph) |> ignore + + return nodeGraph, fileIndexes } ) @@ -815,18 +835,6 @@ type internal TransparentCompiler let! graph, dependencyFiles = ComputeDependencyGraph priorSnapshot (parsedInputs |> Seq.map p13) bootstrapInfo.TcConfig - let fileNames = - parsedInputs - |> Seq.mapi (fun idx (input, _, _) -> idx, Path.GetFileName input.FileName) - |> Map.ofSeq - - let debugGraph = - graph - |> Graph.map (function - | NodeToTypeCheck.PhysicalFile i -> i, fileNames[i] - | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF : {fileNames[i]}") - - Trace.TraceInformation("\n" + (debugGraph |> Graph.serialiseToMermaid)) // layers that can be processed in parallel let layers = Graph.leafSequence graph |> Seq.toList diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 6861c256a26..0a001ec3b88 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -616,12 +616,13 @@ type ProjectWorkflowBuilder ?useGetSource, ?useChangeNotifications, ?useSyntaxTreeCache, - ?useTransparentCompiler + ?useTransparentCompiler, + ?runTimeout ) = - let useGetSource = defaultArg useGetSource false - let useChangeNotifications = defaultArg useChangeNotifications false let useTransparentCompiler = defaultArg useTransparentCompiler false + let useGetSource = not useTransparentCompiler && defaultArg useGetSource false + let useChangeNotifications = not useTransparentCompiler && defaultArg useChangeNotifications false let mutable latestProject = initialProject let mutable activity = None @@ -652,26 +653,29 @@ type ProjectWorkflowBuilder let mapProject f = mapProjectAsync (f >> async.Return) + let getInitialContext() = + match initialContext with + | Some ctx -> async.Return ctx + | None -> SaveAndCheckProject initialProject checker + /// Creates a ProjectWorkflowBuilder which will already have the project /// saved and checked so time won't be spent on that. /// Also the project won't be deleted after the computation expression is evaluated member this.CreateBenchmarkBuilder() = - let ctx = this.Yield() |> Async.RunSynchronously + let ctx = getInitialContext() |> Async.RunSynchronously ProjectWorkflowBuilder( ctx.Project, ctx, useGetSource = useGetSource, - useChangeNotifications = useChangeNotifications - ) + useChangeNotifications = useChangeNotifications, + useTransparentCompiler = useTransparentCompiler, + ?runTimeout = runTimeout) member this.Checker = checker member this.Yield _ = async { - let! ctx = - match initialContext with - | Some ctx -> async.Return ctx - | _ -> SaveAndCheckProject initialProject checker + let! ctx = getInitialContext() tracerProvider <- Sdk.CreateTracerProviderBuilder() .AddSource("fsc") @@ -679,7 +683,7 @@ type ProjectWorkflowBuilder .AddJaegerExporter() .Build() |> Some - activity <- Activity.start ctx.Project.Name [ Activity.Tags.project, ctx.Project.Name ] |> Some + activity <- Activity.start ctx.Project.Name [ Activity.Tags.project, ctx.Project.Name; "UsingTransparentCompiler", useTransparentCompiler.ToString() ] |> Some return ctx } @@ -689,7 +693,7 @@ type ProjectWorkflowBuilder member this.Run(workflow: Async) = try - Async.RunSynchronously workflow + Async.RunSynchronously(workflow, timeout = defaultArg runTimeout 60_000) finally if initialContext.IsNone then this.DeleteProjectDir() diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs index 3b44c1d37bf..b555ac27508 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs @@ -6,6 +6,7 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Text open FSharp.Compiler.Diagnostics open FSharp.Test.ProjectGeneration +open BenchmarkDotNet.Engines [] @@ -221,3 +222,114 @@ type NoFileSystemCheckerBenchmark() = [] member this.Cleanup() = benchmark.DeleteProjectDir() + + + +type TestProjectType = + | DependencyChain = 1 + | DependentGroups = 2 + | ParallelGroups = 3 + + +[] +[] +[] +[] +type TransparentCompilerBenchmark() = + + let size = 30 + + let groups = 6 + let filesPerGroup = size / groups + let somethingToCompile = File.ReadAllText (__SOURCE_DIRECTORY__ ++ "SomethingToCompileSmaller.fs") + + let projects = Map [ + + TestProjectType.DependencyChain, + SyntheticProject.Create("SingleDependencyChain", [| + sourceFile $"File%03d{0}" [] + for i in 1..size do + { sourceFile $"File%03d{i}" [$"File%03d{i-1}"] with ExtraSource = somethingToCompile } + |]) + + TestProjectType.DependentGroups, + SyntheticProject.Create("GroupDependenciesProject", [| + for group in 1..groups do + for i in 1..filesPerGroup do + { sourceFile $"G{group}_F%03d{i}" [ + if group > 1 then $"G1_F%03d{1}" + if i > 1 then $"G{group}_F%03d{i - 1}" ] + with ExtraSource = somethingToCompile } + |]) + + TestProjectType.ParallelGroups, + SyntheticProject.Create("ParallelGroupsProject", [| + for group in 1..groups do + for i in 1..filesPerGroup do + { sourceFile $"G{group}_F%03d{i}" [ + if group > 1 then + for i in 1..filesPerGroup do + $"G{group-1}_F%03d{i}" ] + with ExtraSource = somethingToCompile } + |]) + ] + + let mutable benchmark : ProjectWorkflowBuilder = Unchecked.defaultof<_> + + member val UseGetSource = true with get,set + + member val UseChangeNotifications = true with get,set + + //[] + member val EmptyCache = false with get,set + + [] + member val UseTransparentCompiler = true with get,set + + [] + member val ProjectType = TestProjectType.ParallelGroups with get,set + + member this.Project = projects[this.ProjectType] + + [] + member this.Setup() = + benchmark <- + ProjectWorkflowBuilder( + this.Project, + useGetSource = this.UseGetSource, + useChangeNotifications = this.UseChangeNotifications, + useTransparentCompiler = this.UseTransparentCompiler, + runTimeout = 15_000).CreateBenchmarkBuilder() + + [] + member this.EditFirstFile_OnlyInternalChange() = + if this.EmptyCache then + benchmark.Checker.InvalidateAll() + benchmark.Checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() + + [] + member this.ExampleWorkflow() = + + use _ = Activity.start "Benchmark" [ + "UseTransparentCompiler", this.UseTransparentCompiler.ToString() + ] + + let first = this.Project.SourceFiles[0].Id + let middle = this.Project.SourceFiles[size / 2].Id + let last = this.Project.SourceFiles |> List.last |> fun f -> f.Id + + benchmark { + updateFile first updatePublicSurface + checkFile first expectSignatureChanged + checkFile last expectSignatureChanged + updateFile middle updatePublicSurface + checkFile last expectOk + addFileAbove middle (sourceFile "addedFile" [first]) + updateFile middle (addDependency "addedFile") + checkFile middle expectSignatureChanged + checkFile last expectOk + } + + [] + member this.Cleanup() = + benchmark.DeleteProjectDir() From 22b1c9bee775a9bfa3e37015d776561de14aeab2 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 19 May 2023 19:36:02 +0200 Subject: [PATCH 043/222] f --- src/Compiler/Service/TransparentCompiler.fs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 5f32e4325cf..bab6a5a7c7b 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -690,7 +690,6 @@ type internal TransparentCompiler |> fst |> Graph.subGraphFor (sourceFiles |> Array.last).Idx - let fileIndexes = graph.Values |> Seq.collect id |> Seq.append graph.Keys |> Seq.distinct |> Set @@ -835,7 +834,6 @@ type internal TransparentCompiler let! graph, dependencyFiles = ComputeDependencyGraph priorSnapshot (parsedInputs |> Seq.map p13) bootstrapInfo.TcConfig - // layers that can be processed in parallel let layers = Graph.leafSequence graph |> Seq.toList From 68df1f6c298ebf5ea3036f2761e3050b889604da Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 5 Jun 2023 15:44:45 +0200 Subject: [PATCH 044/222] in memory project references --- src/Compiler/Driver/GraphChecking/Graph.fs | 3 + src/Compiler/Driver/GraphChecking/Graph.fsi | 2 + src/Compiler/Service/BackgroundCompiler.fs | 6 + src/Compiler/Service/FSharpCheckerResults.fs | 1 + src/Compiler/Service/IncrementalBuild.fs | 1 + src/Compiler/Service/IncrementalBuild.fsi | 13 + src/Compiler/Service/TransparentCompiler.fs | 500 ++++++++++++------ .../FSharpChecker/TransparentCompiler.fs | 52 +- 8 files changed, 415 insertions(+), 163 deletions(-) diff --git a/src/Compiler/Driver/GraphChecking/Graph.fs b/src/Compiler/Driver/GraphChecking/Graph.fs index 9cbce0b4b9b..41e1ae4a47e 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fs +++ b/src/Compiler/Driver/GraphChecking/Graph.fs @@ -27,6 +27,9 @@ module internal Graph = |> Array.map (fun (KeyValue (k, v)) -> k, v) |> readOnlyDict + let nodes (graph: Graph<'Node>) : Set<'Node> = + graph.Values |> Seq.collect id |> Seq.append graph.Keys |> Seq.distinct |> Set + /// Find transitive dependencies of a single node. let transitiveDeps (node: 'Node) (graph: Graph<'Node>) = let visited = HashSet<'Node>() diff --git a/src/Compiler/Driver/GraphChecking/Graph.fsi b/src/Compiler/Driver/GraphChecking/Graph.fsi index 7d885f5c96a..f92a2a58468 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fsi +++ b/src/Compiler/Driver/GraphChecking/Graph.fsi @@ -10,6 +10,8 @@ module internal Graph = /// Build the graph. val make: nodeDeps: seq<'Node * 'Node array> -> Graph<'Node> when 'Node: equality val map<'T, 'U when 'U: equality> : f: ('T -> 'U) -> graph: Graph<'T> -> Graph<'U> + /// Get all nodes of the graph. + val nodes: graph: Graph<'Node> -> Set<'Node> /// Create a transitive closure of the graph in O(n^2) time (but parallelize it). /// The resulting graph contains edge A -> C iff the input graph contains a (directed) non-zero length path from A to C. val transitive<'Node when 'Node: equality> : graph: Graph<'Node> -> Graph<'Node> diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index baad181612a..4de5d8f7972 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -87,6 +87,9 @@ type internal IBackgroundCompiler = abstract member GetAssemblyData: options: FSharpProjectOptions * userOpName: string -> NodeCode + abstract member GetAssemblyData: + projectSnapshot: FSharpProjectSnapshot * userOpName: string -> NodeCode + /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) abstract member GetBackgroundCheckResultsForFileInProject: fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode @@ -1490,6 +1493,9 @@ type internal BackgroundCompiler member _.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = self.GetAssemblyData(options, userOpName) + member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = + self.GetAssemblyData(projectSnapshot.ToOptions(), userOpName) + member _.GetBackgroundCheckResultsForFileInProject ( fileName: string, diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 408e758427c..5e43544cfe5 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -59,6 +59,7 @@ open Internal.Utilities open Internal.Utilities.Collections open FSharp.Compiler.AbstractIL.ILBinaryReader open System.Threading.Tasks +open System.Runtime.CompilerServices type FSharpUnresolvedReferencesSet = FSharpUnresolvedReferencesSet of UnresolvedAssemblyReference list diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index 5403a37b86b..bee2e5df0ec 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -578,6 +578,7 @@ module Utilities = /// Constructs the build data (IRawFSharpAssemblyData) representing the assembly when used /// as a cross-assembly reference. Note the assembly has not been generated on disk, so this is /// a virtualized view of the assembly contents as computed by background checking. +[] type RawFSharpAssemblyDataBackedByLanguageService (tcConfig, tcGlobals, generatedCcu: CcuThunk, outfile, topAttrs, assemblyName, ilAssemRef) = let exportRemapping = MakeExportRemapping generatedCcu generatedCcu.Contents diff --git a/src/Compiler/Service/IncrementalBuild.fsi b/src/Compiler/Service/IncrementalBuild.fsi index 50c7201195c..d4fe402cce1 100644 --- a/src/Compiler/Service/IncrementalBuild.fsi +++ b/src/Compiler/Service/IncrementalBuild.fsi @@ -140,6 +140,19 @@ type internal PartialCheckResults = member TimeStamp: DateTime +[] +type internal RawFSharpAssemblyDataBackedByLanguageService = + new: + tcConfig: TcConfig * + tcGlobals: TcGlobals * + generatedCcu: CcuThunk * + outfile: string * + topAttrs: TopAttribs * + assemblyName: string * + ilAssemRef: FSharp.Compiler.AbstractIL.IL.ILAssemblyRef -> + RawFSharpAssemblyDataBackedByLanguageService + interface IRawFSharpAssemblyData + /// Manages an incremental build graph for the build of an F# project [] type internal IncrementalBuilder = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 237c32027df..97b264ad663 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -2,6 +2,7 @@ namespace FSharp.Compiler.CodeAnalysis.TransparentCompiler open System open System.Collections.Generic +open System.Runtime.CompilerServices open System.Diagnostics open System.IO @@ -37,6 +38,8 @@ open FSharp.Compiler.TypedTree open FSharp.Compiler.CheckDeclarations open FSharp.Compiler.EditorServices open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CreateILModule +open FSharp.Compiler.TypedTreeOps type internal FSharpFile = { @@ -49,6 +52,8 @@ type internal FSharpFile = /// Things we need to start parsing and checking files for a given project snapshot type internal BootstrapInfo = { + AssemblyName: string + OutFile: string TcConfig: TcConfig TcImports: TcImports TcGlobals: TcGlobals @@ -86,6 +91,19 @@ type internal TcIntermediate = sink: TcResultsSinkImpl } +[] +type internal DependencyGraphType = + /// A dependency graph for a single file - it will be missing files which this file does not depend on + | File + /// A dependency graph for a project - it will contain all files in the project + | Project + +[] +type internal Extensions = + [] + static member Key(fileSnapshots: FSharpFileSnapshot list) = + fileSnapshots |> List.map (fun f -> f.Key) + type internal TransparentCompiler ( legacyReferenceResolver, @@ -102,7 +120,7 @@ type internal TransparentCompiler getSource: (string -> Async) option, useChangeNotifications, useSyntaxTreeCache - ) = + ) as self = // Is having just one of these ok? let lexResourceManager = Lexhelp.LexResourceManager() @@ -115,6 +133,9 @@ type internal TransparentCompiler let ParseAndCheckFileInProjectCache = AsyncMemoize(triggerCacheEvent "ParseAndCheckFileInProject") + let ParseAndCheckFullProjectCache = + AsyncMemoize(triggerCacheEvent "ParseAndCheckFullProject") + let FrameworkImportsCache = AsyncMemoize(triggerCacheEvent "FrameworkImports") let BootstrapInfoStaticCache = AsyncMemoize(triggerCacheEvent "BootstrapInfoStatic") let BootstrapInfoCache = AsyncMemoize(triggerCacheEvent "BootstrapInfo") @@ -123,6 +144,8 @@ type internal TransparentCompiler let DependencyGraphCache = AsyncMemoize(triggerCacheEvent "DependencyGraph") + let AssemblyDataCache = AsyncMemoize(triggerCacheEvent "AssemblyData") + let SemanticClassificationCache = AsyncMemoize(triggerCacheEvent "SemanticClassification") @@ -156,44 +179,6 @@ type internal TransparentCompiler ) :> IBackgroundCompiler - let getProjectReferences (project: FSharpProjectSnapshot) userOpName = - [ - for r in project.ReferencedProjects do - - match r with - | FSharpReferencedProjectSnapshot.FSharpReference (nm, opts) -> - // Don't use cross-project references for FSharp.Core, since various bits of code - // require a concrete FSharp.Core to exist on-disk. The only solutions that have - // these cross-project references to FSharp.Core are VisualFSharp.sln and FSharp.sln. The ramification - // of this is that you need to build FSharp.Core to get intellisense in those projects. - - if - (try - Path.GetFileNameWithoutExtension(nm) - with _ -> - "") - <> GetFSharpCoreLibraryName() - then - { new IProjectReference with - member x.EvaluateRawContents() = - node { - Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) - - return! - backgroundCompiler.GetAssemblyData( - opts.ToOptions(), - userOpName + ".CheckReferencedProject(" + nm + ")" - ) - } - - member x.TryGetLogicalTimeStamp(cache) = - // TODO: - None - - member x.FileName = nm - } - ] - let ComputeFrameworkImports (tcConfig: TcConfig) frameworkDLLs nonFrameworkResolutions = let frameworkDLLsKey = frameworkDLLs @@ -322,6 +307,40 @@ type internal TransparentCompiler return tcImports, tcInfo } + let getProjectReferences (project: FSharpProjectSnapshot) userOpName = + [ + for r in project.ReferencedProjects do + + match r with + | FSharpReferencedProjectSnapshot.FSharpReference (nm, opts) -> + // Don't use cross-project references for FSharp.Core, since various bits of code + // require a concrete FSharp.Core to exist on-disk. The only solutions that have + // these cross-project references to FSharp.Core are VisualFSharp.sln and FSharp.sln. The ramification + // of this is that you need to build FSharp.Core to get intellisense in those projects. + + if + (try + Path.GetFileNameWithoutExtension(nm) + with _ -> + "") + <> GetFSharpCoreLibraryName() + then + { new IProjectReference with + member x.EvaluateRawContents() = + node { + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) + + return! self.GetAssemblyData(opts, userOpName + ".CheckReferencedProject(" + nm + ")") + } + + member x.TryGetLogicalTimeStamp(cache) = + // TODO: + None + + member x.FileName = nm + } + ] + /// Bootstrap info that does not depend on contents of the files let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) = @@ -457,7 +476,7 @@ type internal TransparentCompiler setupConfigFromLoadClosure () let tcConfig = TcConfig.Create(tcConfigB, validate = true) - let _outfile, _, assemblyName = tcConfigB.DecideNames sourceFiles + let outfile, _, assemblyName = tcConfigB.DecideNames sourceFiles // Resolve assemblies and create the framework TcImports. This caches a level of "system" references. No type providers are // included in these references. @@ -548,14 +567,30 @@ type internal TransparentCompiler importsInvalidatedByTypeProvider ) - return sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt, importsInvalidatedByTypeProvider + return + assemblyName, + outfile, + sourceFiles, + tcConfig, + tcImports, + tcGlobals, + initialTcInfo, + loadClosureOpt, + importsInvalidatedByTypeProvider } ) let computeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = node { - let! sourceFiles, tcConfig, tcImports, tcGlobals, initialTcInfo, loadClosureOpt, _importsInvalidatedByTypeProvider = - ComputeBootstrapInfoStatic projectSnapshot + let! assemblyName, + outFile, + sourceFiles, + tcConfig, + tcImports, + tcGlobals, + initialTcInfo, + loadClosureOpt, + _importsInvalidatedByTypeProvider = ComputeBootstrapInfoStatic projectSnapshot let fileSnapshots = Map [ for f in projectSnapshot.SourceFiles -> f.FileName, f ] @@ -582,6 +617,8 @@ type internal TransparentCompiler return Some { + AssemblyName = assemblyName + OutFile = outFile TcConfig = tcConfig TcImports = tcImports TcGlobals = tcGlobals @@ -667,71 +704,73 @@ type internal TransparentCompiler } ) - let ComputeDependencyGraph (priorSnapshot: FSharpProjectSnapshot) parsedInputs (tcConfig: TcConfig) = - let key = priorSnapshot.SourceFiles |> List.map (fun s -> s.Key) - - DependencyGraphCache.Get( - key, - node { + let computeDependencyGraph parsedInputs (tcConfig: TcConfig) (processGraph: Graph -> Graph) = + node { + let sourceFiles: FileInProject array = + parsedInputs + |> Seq.toArray + |> Array.mapi (fun idx (input: ParsedInput) -> + { + Idx = idx + FileName = input.FileName + ParsedInput = input + }) - let sourceFiles: FileInProject array = - parsedInputs - |> Seq.toArray - |> Array.mapi (fun idx (input: ParsedInput) -> - { - Idx = idx - FileName = input.FileName - ParsedInput = input - }) + use _ = + Activity.start "ComputeDependencyGraph" [| Activity.Tags.fileName, (sourceFiles |> Array.last).FileName |] - use _ = - Activity.start "ComputeDependencyGraph" [| Activity.Tags.fileName, (sourceFiles |> Array.last).FileName |] + let filePairs = FilePairMap(sourceFiles) - let filePairs = FilePairMap(sourceFiles) + // TODO: we will probably want to cache and re-use larger graphs if available + let graph = + DependencyResolution.mkGraph tcConfig.compilingFSharpCore filePairs sourceFiles + |> fst + |> processGraph - // TODO: we will probably want to cache and re-use larger graphs if available - let graph = - DependencyResolution.mkGraph tcConfig.compilingFSharpCore filePairs sourceFiles - |> fst - |> Graph.subGraphFor (sourceFiles |> Array.last).Idx + let nodeGraph = TransformDependencyGraph(graph, filePairs) - let fileIndexes = - graph.Values |> Seq.collect id |> Seq.append graph.Keys |> Seq.distinct |> Set + let fileNames = + parsedInputs + |> Seq.mapi (fun idx input -> idx, Path.GetFileName input.FileName) + |> Map.ofSeq - let nodeGraph = TransformDependencyGraph(graph, filePairs) + let debugGraph = + nodeGraph + |> Graph.map (function + | NodeToTypeCheck.PhysicalFile i -> i, fileNames[i] + | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF : {fileNames[i]}") + |> Graph.serialiseToMermaid - let fileNames = - parsedInputs - |> Seq.mapi (fun idx input -> idx, Path.GetFileName input.FileName) - |> Map.ofSeq + Trace.TraceInformation("\n" + debugGraph) - let debugGraph = - nodeGraph - |> Graph.map (function - | NodeToTypeCheck.PhysicalFile i -> i, fileNames[i] - | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF : {fileNames[i]}") - |> Graph.serialiseToMermaid + if Activity.Current <> null then + Activity.Current.AddTag("graph", debugGraph) |> ignore - Trace.TraceInformation("\n" + debugGraph) + return nodeGraph, graph + } - if Activity.Current <> null then - Activity.Current.AddTag("graph", debugGraph) |> ignore + let ComputeDependencyGraphForFile (priorSnapshot: FSharpProjectSnapshot) parsedInputs (tcConfig: TcConfig) = + let key = priorSnapshot.SourceFiles.Key(), DependencyGraphType.File + let lastFileIndex = (parsedInputs |> Array.length) - 1 + DependencyGraphCache.Get(key, computeDependencyGraph parsedInputs tcConfig (Graph.subGraphFor lastFileIndex)) - return nodeGraph, fileIndexes - } - ) + let ComputeDependencyGraphForProject (projectSnapshot: FSharpProjectSnapshot) parsedInputs (tcConfig: TcConfig) = + let key = projectSnapshot.SourceFiles.Key(), DependencyGraphType.Project + DependencyGraphCache.Get(key, computeDependencyGraph parsedInputs tcConfig id) let ComputeTcIntermediate (projectSnapshot: FSharpProjectSnapshot) - (dependencyFiles: Set) + (dependencyGraph: Graph) (fileIndex: FileIndex) (parsedInput: ParsedInput, parseErrors) bootstrapInfo (prevTcInfo: TcInfo) = + let dependencyFiles = dependencyGraph |> Graph.subGraphFor fileIndex |> Graph.nodes + TcIntermediateCache.Get( - projectSnapshot.UpTo(fileIndex).OnlyWith(dependencyFiles).Key, + projectSnapshot.OnlyWith(dependencyFiles).Key, node { let input = parsedInput let fileName = input.FileName @@ -797,24 +836,79 @@ type internal TransparentCompiler ) let mergeIntermediateResults = - Array.fold (fun (tcInfo: TcInfo) (tcIntermediate: TcIntermediate) -> - let (tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile), tcState = - tcInfo.tcState |> tcIntermediate.finisher.Invoke - - let tcEnvAtEndOfFile = - if keepAllBackgroundResolutions then - tcEnv - else - tcState.TcEnvFromImpls - - { tcInfo with - tcState = tcState - tcEnvAtEndOfFile = tcEnvAtEndOfFile - topAttribs = Some topAttribs - tcDiagnosticsRev = tcIntermediate.tcDiagnosticsRev @ tcInfo.tcDiagnosticsRev - tcDependencyFiles = tcIntermediate.tcDependencyFiles @ tcInfo.tcDependencyFiles - latestCcuSigForFile = Some ccuSigForFile - }) + Array.fold (fun (tcInfos: TcInfo list) (tcIntermediate: TcIntermediate) -> + match tcInfos with + | [] -> failwith "need initial tcInfo" + | tcInfo :: rest -> + let (tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile), tcState = + tcInfo.tcState |> tcIntermediate.finisher.Invoke + + let tcEnvAtEndOfFile = + if keepAllBackgroundResolutions then + tcEnv + else + tcState.TcEnvFromImpls + + { tcInfo with + tcState = tcState + tcEnvAtEndOfFile = tcEnvAtEndOfFile + topAttribs = Some topAttribs + tcDiagnosticsRev = tcIntermediate.tcDiagnosticsRev @ tcInfo.tcDiagnosticsRev + tcDependencyFiles = tcIntermediate.tcDependencyFiles @ tcInfo.tcDependencyFiles + latestCcuSigForFile = Some ccuSigForFile + } + :: tcInfo :: rest) + + let typeCheckLayers bootstrapInfo projectSnapshot (parsedInputs: array<_>) graph layers = + let rec processLayer (layers: Set list) tcInfos = + node { + match layers, tcInfos with + | [], _ -> return tcInfos + | _, [] -> return failwith "need initial tcInfo" + | layer :: rest, tcInfo :: _ -> + let! results = + layer + |> Seq.map (fun fileNode -> + + match fileNode with + | NodeToTypeCheck.PhysicalFile fileIndex -> + let parsedInput, parseErrors, _ = parsedInputs[fileIndex] + + ComputeTcIntermediate projectSnapshot graph fileIndex (parsedInput, parseErrors) bootstrapInfo tcInfo + | NodeToTypeCheck.ArtificialImplFile fileIndex -> + + let finisher tcState = + let parsedInput, _parseErrors, _ = parsedInputs[fileIndex] + let prefixPathOpt = None + // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. + AddSignatureResultToTcImplEnv + (bootstrapInfo.TcImports, + bootstrapInfo.TcGlobals, + prefixPathOpt, + TcResultsSink.NoSink, + tcState, + parsedInput) + tcState + + let tcIntermediate = + { + finisher = finisher + moduleNamesDict = tcInfo.moduleNamesDict + tcDiagnosticsRev = [] + tcDependencyFiles = [] + sink = Unchecked.defaultof<_> // TODO: something nicer + } + + node.Return(tcIntermediate)) + |> NodeCode.Parallel + + return! processLayer rest (mergeIntermediateResults tcInfos results) + } + + node { + let! tcInfo = processLayer layers [ bootstrapInfo.InitialTcInfo ] + return tcInfo, graph + } // Type check everything that is needed to check given file let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = @@ -837,7 +931,8 @@ type internal TransparentCompiler let! parsedInputs = files |> Seq.map (ComputeParseFile bootstrapInfo) |> NodeCode.Parallel - let! graph, dependencyFiles = ComputeDependencyGraph priorSnapshot (parsedInputs |> Seq.map p13) bootstrapInfo.TcConfig + let! graph, dependencyFiles = + ComputeDependencyGraphForFile priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig // layers that can be processed in parallel let layers = Graph.leafSequence graph |> Seq.toList @@ -845,58 +940,9 @@ type internal TransparentCompiler // remove the final layer, which should be the target file let layers = layers |> List.take (layers.Length - 1) - let rec processLayer (layers: Set list) tcInfo = - node { - match layers with - | [] -> return tcInfo - | layer :: rest -> - let! results = - layer - |> Seq.map (fun fileNode -> - - match fileNode with - | NodeToTypeCheck.PhysicalFile fileIndex -> - let parsedInput, parseErrors, _ = parsedInputs[fileIndex] - - ComputeTcIntermediate - projectSnapshot - dependencyFiles - fileIndex - (parsedInput, parseErrors) - bootstrapInfo - tcInfo - | NodeToTypeCheck.ArtificialImplFile fileIndex -> - - let finisher tcState = - let parsedInput, _parseErrors, _ = parsedInputs[fileIndex] - let prefixPathOpt = None - // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. - AddSignatureResultToTcImplEnv - (bootstrapInfo.TcImports, - bootstrapInfo.TcGlobals, - prefixPathOpt, - TcResultsSink.NoSink, - tcState, - parsedInput) - tcState - - let tcIntermediate = - { - finisher = finisher - moduleNamesDict = tcInfo.moduleNamesDict - tcDiagnosticsRev = [] - tcDependencyFiles = [] - sink = Unchecked.defaultof<_> - } - - node.Return(tcIntermediate)) - |> NodeCode.Parallel - - return! processLayer rest (mergeIntermediateResults tcInfo results) - } + let! tcInfos, graph = typeCheckLayers bootstrapInfo priorSnapshot parsedInputs dependencyFiles layers - let! tcInfo = processLayer layers bootstrapInfo.InitialTcInfo - return tcInfo, dependencyFiles + return tcInfos.Head, graph } ) @@ -981,24 +1027,46 @@ type internal TransparentCompiler } ) + let ComputeParseAndCheckFullProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = + ParseAndCheckFullProjectCache.Get( + projectSnapshot.Key, + node { + use _ = + Activity.start + "ComputeParseAndCheckFullProject" + [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] + + let! parsedInputs = + bootstrapInfo.SourceFiles + |> Seq.map (ComputeParseFile bootstrapInfo) + |> NodeCode.Parallel + + let! graph, dependencyFiles = + ComputeDependencyGraphForProject projectSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig + + // layers that can be processed in parallel + let layers = Graph.leafSequence graph |> Seq.toList + + let! tcInfos, _ = typeCheckLayers bootstrapInfo projectSnapshot parsedInputs dependencyFiles layers + return tcInfos |> List.rev + } + ) + let tryGetSink fileName (projectSnapshot: FSharpProjectSnapshot) = node { - use _ = - Activity.start "ComputeSemanticClassification" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - match! ComputeBootstrapInfo projectSnapshot with | None, _ -> return None | Some bootstrapInfo, _creationDiags -> let file = bootstrapInfo.GetFile fileName - let! tcInfo, dependencyFiles = ComputeTcPrior file bootstrapInfo projectSnapshot + let! tcInfo, graph = ComputeTcPrior file bootstrapInfo projectSnapshot let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file let fileIndex = projectSnapshot.IndexOf fileName let! { sink = sink } = - ComputeTcIntermediate projectSnapshot dependencyFiles fileIndex (parseTree, parseDiagnostics) bootstrapInfo tcInfo + ComputeTcIntermediate projectSnapshot graph fileIndex (parseTree, parseDiagnostics) bootstrapInfo tcInfo return Some(sink, bootstrapInfo) } @@ -1099,6 +1167,111 @@ type internal TransparentCompiler | Some itemKeyStore -> return itemKeyStore.FindAll symbol.Item } + member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, _userOpName) = + AssemblyDataCache.Get( + projectSnapshot.Key, + node { + + match! ComputeBootstrapInfo projectSnapshot with + | None, _ -> return ProjectAssemblyDataResult.Unavailable true + | Some bootstrapInfo, _creationDiags -> + + let! tcInfos = ComputeParseAndCheckFullProject bootstrapInfo projectSnapshot + + let assemblyName = bootstrapInfo.AssemblyName + let tcConfig = bootstrapInfo.TcConfig + let tcGlobals = bootstrapInfo.TcGlobals + + let results = + [ + for tcInfo in tcInfos -> + tcInfo.tcEnvAtEndOfFile, defaultArg tcInfo.topAttribs EmptyTopAttrs, None, tcInfo.latestCcuSigForFile + ] + + // Get the state at the end of the type-checking of the last file + let finalInfo = List.last tcInfos + + // Finish the checking + let (_tcEnvAtEndOfLastFile, topAttrs, _mimpls, _), tcState = + CheckMultipleInputsFinish(results, finalInfo.tcState) + + let tcState, _, ccuContents = CheckClosedInputSetFinish([], tcState) + + let generatedCcu = tcState.Ccu.CloneWithFinalizedContents(ccuContents) + + // Compute the identity of the generated assembly based on attributes, options etc. + // Some of this is duplicated from fsc.fs + let ilAssemRef = + let publicKey = + try + let signingInfo = ValidateKeySigningAttributes(tcConfig, tcGlobals, topAttrs) + + match GetStrongNameSigner signingInfo with + | None -> None + | Some s -> Some(PublicKey.KeyAsToken(s.PublicKey)) + with exn -> + errorRecoveryNoRange exn + None + + let locale = + TryFindFSharpStringAttribute + tcGlobals + (tcGlobals.FindSysAttrib "System.Reflection.AssemblyCultureAttribute") + topAttrs.assemblyAttrs + + let assemVerFromAttrib = + TryFindFSharpStringAttribute + tcGlobals + (tcGlobals.FindSysAttrib "System.Reflection.AssemblyVersionAttribute") + topAttrs.assemblyAttrs + |> Option.bind (fun v -> + try + Some(parseILVersion v) + with _ -> + None) + + let ver = + match assemVerFromAttrib with + | None -> tcConfig.version.GetVersionInfo(tcConfig.implicitIncludeDir) + | Some v -> v + + ILAssemblyRef.Create(assemblyName, None, publicKey, false, Some ver, locale) + + let assemblyDataResult = + try + // Assemblies containing type provider components can not successfully be used via cross-assembly references. + // We return 'None' for the assembly portion of the cross-assembly reference + let hasTypeProviderAssemblyAttrib = + topAttrs.assemblyAttrs + |> List.exists (fun (Attrib (tcref, _, _, _, _, _, _)) -> + let nm = tcref.CompiledRepresentationForNamedType.BasicQualifiedName + + nm = typeof + .FullName) + + if tcState.CreatesGeneratedProvidedTypes || hasTypeProviderAssemblyAttrib then + ProjectAssemblyDataResult.Unavailable true + else + ProjectAssemblyDataResult.Available( + RawFSharpAssemblyDataBackedByLanguageService( + tcConfig, + tcGlobals, + generatedCcu, + bootstrapInfo.OutFile, + topAttrs, + assemblyName, + ilAssemRef + ) + :> IRawFSharpAssemblyData + ) + with exn -> + errorRecoveryNoRange exn + ProjectAssemblyDataResult.Unavailable true + + return assemblyDataResult + } + ) + interface IBackgroundCompiler with member _.CacheEvent = cacheEvent.Publish @@ -1166,6 +1339,9 @@ type internal TransparentCompiler member _.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = backgroundCompiler.GetAssemblyData(options, userOpName) + member this.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = + this.GetAssemblyData(projectSnapshot, userOpName) + member _.GetBackgroundCheckResultsForFileInProject ( fileName: string, diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 842dcefaf5c..4c626c89221 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -4,6 +4,7 @@ open System.Collections.Concurrent open System.Diagnostics open FSharp.Compiler.CodeAnalysis open Internal.Utilities.Collections +open FSharp.Compiler.CodeAnalysis.TransparentCompiler open Xunit @@ -173,9 +174,14 @@ let ``Changes in a referenced project`` () = |> updateFile "First" (addDependency "Library") ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + + updateFile "First" updatePublicSurface + checkFile "Last" expectOk + updateFile "Library" updatePublicSurface saveFile "Library" checkFile "Last" expectSignatureChanged + } [] @@ -222,6 +228,7 @@ let ``Files that are not depended on don't invalidate cache`` () = let cacheEvents = ResizeArray() ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + updateFile "First" updatePublicSurface checkFile "Last" expectOk withChecker (fun checker -> checker.CacheEvent.Add cacheEvents.Add) updateFile "Second" updatePublicSurface @@ -240,7 +247,7 @@ let ``Files that are not depended on don't invalidate cache`` () = let graphConstructions = cacheEvents |> Seq.choose (function - | ("DependencyGraph", e, k) -> Some ((k :?> FSharpFileKey list) |> List.last |> fst |> Path.GetFileName, e) + | ("DependencyGraph", e, k) -> Some ((k :?> (FSharpFileKey list * DependencyGraphType)) |> fst |> List.last |> fst |> Path.GetFileName, e) | _ -> None) |> Seq.groupBy fst |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) @@ -249,3 +256,46 @@ let ``Files that are not depended on don't invalidate cache`` () = Assert.Equal([Started; Finished], graphConstructions["FileLast.fs"]) Assert.True intermediateTypeChecks.IsEmpty + +[] +let ``Files that are not depended on don't invalidate cache part 2`` () = + let project = SyntheticProject.Create( + sourceFile "A" [], + sourceFile "B" ["A"], + sourceFile "C" ["A"], + sourceFile "D" ["B"; "C"], + sourceFile "E" ["C"]) + + let cacheEvents = ResizeArray() + let allEvents = ResizeArray() + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + withChecker (fun checker -> checker.CacheEvent.Add allEvents.Add) + updateFile "A" updatePublicSurface + checkFile "D" expectOk + withChecker (fun checker -> checker.CacheEvent.Add cacheEvents.Add) + updateFile "B" updatePublicSurface + checkFile "E" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheEvents + |> Seq.choose (function + | ("TcIntermediate", e, k) -> Some ((k :?> FSharpProjectSnapshotKey).LastFile |> fst |> Path.GetFileName, e) + | _ -> None) + |> Seq.groupBy fst + |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) + |> Map + + let graphConstructions = + cacheEvents + |> Seq.choose (function + | ("DependencyGraph", e, k) -> Some ((k :?> (FSharpFileKey list * DependencyGraphType)) |> fst |> List.last |> fst |> Path.GetFileName, e) + | _ -> None) + |> Seq.groupBy fst + |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) + |> Map + + Assert.Equal([Started; Finished], graphConstructions["FileE.fs"]) + + Assert.True intermediateTypeChecks.IsEmpty \ No newline at end of file From faaf9fa11ce75a2b6321d45650badd8b7111e523 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 5 Jun 2023 16:58:30 +0200 Subject: [PATCH 045/222] try deflake test --- src/Compiler/Service/TransparentCompiler.fs | 4 ++-- .../FSharpChecker/TransparentCompiler.fs | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 97b264ad663..d30d38a6a2f 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -312,7 +312,7 @@ type internal TransparentCompiler for r in project.ReferencedProjects do match r with - | FSharpReferencedProjectSnapshot.FSharpReference (nm, opts) -> + | FSharpReferencedProjectSnapshot.FSharpReference (nm, projectSnapshot) -> // Don't use cross-project references for FSharp.Core, since various bits of code // require a concrete FSharp.Core to exist on-disk. The only solutions that have // these cross-project references to FSharp.Core are VisualFSharp.sln and FSharp.sln. The ramification @@ -330,7 +330,7 @@ type internal TransparentCompiler node { Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) - return! self.GetAssemblyData(opts, userOpName + ".CheckReferencedProject(" + nm + ")") + return! self.GetAssemblyData(projectSnapshot, userOpName + ".CheckReferencedProject(" + nm + ")") } member x.TryGetLogicalTimeStamp(cache) = diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 4c626c89221..4ded2fb2490 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -267,13 +267,15 @@ let ``Files that are not depended on don't invalidate cache part 2`` () = sourceFile "E" ["C"]) let cacheEvents = ResizeArray() - let allEvents = ResizeArray() ProjectWorkflowBuilder(project, useTransparentCompiler = true) { - withChecker (fun checker -> checker.CacheEvent.Add allEvents.Add) updateFile "A" updatePublicSurface checkFile "D" expectOk - withChecker (fun checker -> checker.CacheEvent.Add cacheEvents.Add) + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.CacheEvent.Add cacheEvents.Add + }) updateFile "B" updatePublicSurface checkFile "E" expectOk } |> ignore @@ -298,4 +300,4 @@ let ``Files that are not depended on don't invalidate cache part 2`` () = Assert.Equal([Started; Finished], graphConstructions["FileE.fs"]) - Assert.True intermediateTypeChecks.IsEmpty \ No newline at end of file + Assert.Equal([], intermediateTypeChecks |> Map.toList) \ No newline at end of file From d244d7337ad26b76e01846c9f38b31dab801dc84 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 5 Jun 2023 17:32:53 +0200 Subject: [PATCH 046/222] remove distinct --- src/Compiler/Driver/GraphChecking/Graph.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Driver/GraphChecking/Graph.fs b/src/Compiler/Driver/GraphChecking/Graph.fs index 41e1ae4a47e..84d96422811 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fs +++ b/src/Compiler/Driver/GraphChecking/Graph.fs @@ -28,7 +28,7 @@ module internal Graph = |> readOnlyDict let nodes (graph: Graph<'Node>) : Set<'Node> = - graph.Values |> Seq.collect id |> Seq.append graph.Keys |> Seq.distinct |> Set + graph.Values |> Seq.collect id |> Seq.append graph.Keys |> Set /// Find transitive dependencies of a single node. let transitiveDeps (node: 'Node) (graph: Graph<'Node>) = From 26c542670f2cbca547c740e27053a3a3f2226152 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 5 Jun 2023 17:47:48 +0200 Subject: [PATCH 047/222] try deflake test --- .../FSharpChecker/TransparentCompiler.fs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 4ded2fb2490..1dc0cea36f1 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -230,7 +230,11 @@ let ``Files that are not depended on don't invalidate cache`` () = ProjectWorkflowBuilder(project, useTransparentCompiler = true) { updateFile "First" updatePublicSurface checkFile "Last" expectOk - withChecker (fun checker -> checker.CacheEvent.Add cacheEvents.Add) + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.CacheEvent.Add cacheEvents.Add + }) updateFile "Second" updatePublicSurface checkFile "Last" expectOk } |> ignore @@ -255,7 +259,7 @@ let ``Files that are not depended on don't invalidate cache`` () = Assert.Equal([Started; Finished], graphConstructions["FileLast.fs"]) - Assert.True intermediateTypeChecks.IsEmpty + Assert.Equal([], intermediateTypeChecks |> Map.toList) [] let ``Files that are not depended on don't invalidate cache part 2`` () = From 9d4642283094917c6dfff37174033a9e820e27cd Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 8 Jun 2023 17:36:20 +0200 Subject: [PATCH 048/222] fix cache agent loop --- src/Compiler/Facilities/AsyncMemoize.fs | 110 ++++++++++-------- src/Compiler/Service/TransparentCompiler.fs | 33 +++--- .../CompilerService/AsyncMemoize.fs | 6 +- .../FSharp.Editor/Common/CancellableTasks.fs | 60 +++++++++- .../src/FSharp.Editor/FSharp.Editor.resx | 1 + .../LanguageService/LanguageService.fs | 2 +- .../FSharp.Editor/xlf/FSharp.Editor.cs.xlf | 1 + .../FSharp.Editor/xlf/FSharp.Editor.de.xlf | 2 + .../FSharp.Editor/xlf/FSharp.Editor.es.xlf | 1 + .../FSharp.Editor/xlf/FSharp.Editor.fr.xlf | 2 + .../FSharp.Editor/xlf/FSharp.Editor.it.xlf | 2 + .../FSharp.Editor/xlf/FSharp.Editor.ja.xlf | 1 + .../FSharp.Editor/xlf/FSharp.Editor.ko.xlf | 2 + .../FSharp.Editor/xlf/FSharp.Editor.pl.xlf | 2 + .../FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf | 1 + .../FSharp.Editor/xlf/FSharp.Editor.ru.xlf | 1 + .../FSharp.Editor/xlf/FSharp.Editor.tr.xlf | 2 + .../xlf/FSharp.Editor.zh-Hans.xlf | 1 + .../xlf/FSharp.Editor.zh-Hant.xlf | 2 + 19 files changed, 163 insertions(+), 69 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 581e6201193..2b56024fbe0 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -1,13 +1,15 @@ namespace Internal.Utilities.Collections -open FSharp.Compiler.BuildGraph -open System.Threading +open System open System.Collections.Generic +open System.Threading + +open FSharp.Compiler.BuildGraph type internal Action<'TKey, 'TValue> = | GetOrCompute of NodeCode<'TValue> * CancellationToken | CancelRequest - | JobCompleted + | JobCompleted of 'TValue type MemoizeRequest<'TKey, 'TValue> = 'TKey * Action<'TKey, 'TValue> * AsyncReplyChannel> @@ -20,12 +22,13 @@ type internal JobEventType = | Finished | Canceled -type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobEventType * 'TKey -> unit)) = +type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (string -> JobEventType * 'TKey -> unit), ?name: string) = + let name = defaultArg name "N/A" let tok = obj () let cache = - MruCache<_, 'TKey, Job<'TValue>>(keepStrongly = 10, areSame = (fun (x, y) -> x = y)) + MruCache<_, 'TKey, Job<'TValue>>(keepStrongly = 30, areSame = (fun (x, y) -> x = y)) let requestCounts = Dictionary<'TKey, int>() @@ -39,7 +42,8 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobE let sendAsync (inbox: MailboxProcessor<_>) key msg = inbox.PostAndAsyncReply(fun rc -> key, msg, rc) |> Async.Ignore |> Async.Start - let log event = logEvent |> Option.iter ((|>) event) + let log event = + logEvent |> Option.iter (fun x -> x name event) let agent = MailboxProcessor.Start(fun (inbox: MailboxProcessor>) -> @@ -48,66 +52,72 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (JobE async { while true do + try + let _name = name - let! key, action, replyChannel = inbox.Receive() - - match action, cache.TryGet(tok, key) with - | GetOrCompute _, Some (Completed job) -> replyChannel.Reply job - | GetOrCompute (_, ct), Some (Running (job, _)) -> + let! key, action, replyChannel = inbox.Receive() - incrRequestCount key - replyChannel.Reply job - ct.Register(fun _ -> post key CancelRequest) |> ignore + match action, cache.TryGet(tok, key) with + | GetOrCompute _, Some (Completed job) -> replyChannel.Reply job + | GetOrCompute (_, ct), Some (Running (job, _)) -> + incrRequestCount key + replyChannel.Reply job + ct.Register(fun _ -> post key CancelRequest) |> ignore - | GetOrCompute (computation, ct), None -> + | GetOrCompute (computation, ct), None -> - let cts = new CancellationTokenSource() + let cts = new CancellationTokenSource() - let startedComputation = - Async.StartAsTask( - Async.AwaitNodeCode( - node { - log (Started, key) - let! result = computation - post key JobCompleted - return result - } - ), - cancellationToken = cts.Token - ) + let startedComputation = + Async.StartAsTask( + Async.AwaitNodeCode( + node { + log (Started, key) + let! result = computation + post key (JobCompleted result) + return result + } + ), + cancellationToken = cts.Token + ) - let job = NodeCode.AwaitTask startedComputation + let job = NodeCode.AwaitTask startedComputation - cache.Set(tok, key, (Running(job, cts))) + cache.Set(tok, key, (Running(job, cts))) - incrRequestCount key + incrRequestCount key - ct.Register(fun _ -> post key CancelRequest) |> ignore + ct.Register(fun _ -> post key CancelRequest) |> ignore - replyChannel.Reply job + replyChannel.Reply job - | CancelRequest, Some (Running (_, cts)) -> - let requestCount = requestCounts.TryGetValue key |> snd + | CancelRequest, Some (Running (_, cts)) -> + let requestCount = requestCounts.TryGetValue key |> snd - if requestCount > 1 then - requestCounts.[key] <- requestCount - 1 + if requestCount > 1 then + requestCounts.[key] <- requestCount - 1 - else - cts.Cancel() - cache.RemoveAnySimilar(tok, key) - requestCounts.Remove key |> ignore - log (Canceled, key) + else + cts.Cancel() + cache.RemoveAnySimilar(tok, key) + requestCounts.Remove key |> ignore + log (Canceled, key) - | CancelRequest, None - | CancelRequest, Some (Completed _) -> () + | CancelRequest, None + | CancelRequest, Some (Completed _) -> () - | JobCompleted, Some (Running (job, _)) -> - // TODO: should we re-wrap the result? - cache.Set(tok, key, (Completed job)) - requestCounts.Remove key |> ignore - log (Finished, key) + | JobCompleted result, Some (Running _) + // Job could be evicted from cache while it's running + | JobCompleted result, None -> + cache.Set(tok, key, (Completed(node.Return result))) + requestCounts.Remove key |> ignore + log (Finished, key) - | JobCompleted, _ -> failwith "If this happens there's a bug" + | JobCompleted _, _ -> failwith "If this happens there's a bug" + with + | :? OperationCanceledException as e -> + System.Diagnostics.Trace.TraceError($"AsyncMemoize OperationCanceledException: {e.Message}") + | ex -> System.Diagnostics.Trace.TraceError($"AsyncMemoize Exception: %A{ex}") }) member _.Get(key, computation) = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index d30d38a6a2f..d783c63e359 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -128,28 +128,33 @@ type internal TransparentCompiler let cacheEvent = new Event() let triggerCacheEvent name (e, k) = cacheEvent.Trigger(name, e, k) - let ParseFileCache = AsyncMemoize(triggerCacheEvent "ParseFile") + let ParseFileCache = AsyncMemoize(triggerCacheEvent, "ParseFile") let ParseAndCheckFileInProjectCache = - AsyncMemoize(triggerCacheEvent "ParseAndCheckFileInProject") + AsyncMemoize(triggerCacheEvent, "ParseAndCheckFileInProject") let ParseAndCheckFullProjectCache = - AsyncMemoize(triggerCacheEvent "ParseAndCheckFullProject") + AsyncMemoize(triggerCacheEvent, "ParseAndCheckFullProject") - let FrameworkImportsCache = AsyncMemoize(triggerCacheEvent "FrameworkImports") - let BootstrapInfoStaticCache = AsyncMemoize(triggerCacheEvent "BootstrapInfoStatic") - let BootstrapInfoCache = AsyncMemoize(triggerCacheEvent "BootstrapInfo") - let TcPriorCache = AsyncMemoize(triggerCacheEvent "TcPrior") - let TcIntermediateCache = AsyncMemoize(triggerCacheEvent "TcIntermediate") + let FrameworkImportsCache = AsyncMemoize(triggerCacheEvent, "FrameworkImports") - let DependencyGraphCache = AsyncMemoize(triggerCacheEvent "DependencyGraph") + let BootstrapInfoStaticCache = + AsyncMemoize(triggerCacheEvent, "BootstrapInfoStatic") - let AssemblyDataCache = AsyncMemoize(triggerCacheEvent "AssemblyData") + let BootstrapInfoCache = AsyncMemoize(triggerCacheEvent, "BootstrapInfo") + + let TcPriorCache = AsyncMemoize(triggerCacheEvent, "TcPrior") + + let TcIntermediateCache = AsyncMemoize(triggerCacheEvent, "TcIntermediate") + + let DependencyGraphCache = AsyncMemoize(triggerCacheEvent, "DependencyGraph") + + let AssemblyDataCache = AsyncMemoize(triggerCacheEvent, "AssemblyData") let SemanticClassificationCache = - AsyncMemoize(triggerCacheEvent "SemanticClassification") + AsyncMemoize(triggerCacheEvent, "SemanticClassification") - let ItemKeyStoreCache = AsyncMemoize(triggerCacheEvent "ItemKeyStore") + let ItemKeyStoreCache = AsyncMemoize(triggerCacheEvent, "ItemKeyStore") // We currently share one global dependency provider for all scripts for the FSharpChecker. // For projects, one is used per project. @@ -1144,8 +1149,8 @@ type internal TransparentCompiler member _.ParseFile(fileName, projectSnapshot: FSharpProjectSnapshot, _userOpName) = node { - use _ = - Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + //use _ = + // Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] match! ComputeBootstrapInfo projectSnapshot with | None, creationDiags -> return emptyParseResult fileName creationDiags diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 80c137cc603..61fae5d2af3 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -18,7 +18,7 @@ let ``Basics``() = let eventLog = ResizeArray() - let memoize = AsyncMemoize(eventLog.Add) + let memoize = AsyncMemoize(fun _ -> eventLog.Add) let task = NodeCode.Parallel(seq { @@ -53,7 +53,7 @@ let ``We can cancel a job`` () = } let eventLog = ResizeArray() - let memoize = AsyncMemoize(eventLog.Add) + let memoize = AsyncMemoize(fun _ -> eventLog.Add) use cts1 = new CancellationTokenSource() use cts2 = new CancellationTokenSource() @@ -96,7 +96,7 @@ let ``Job keeps running even if first requestor cancels`` () = } let eventLog = ResizeArray() - let memoize = AsyncMemoize(eventLog.Add) + let memoize = AsyncMemoize(fun _ -> eventLog.Add) use cts1 = new CancellationTokenSource() use cts2 = new CancellationTokenSource() diff --git a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs index 11ef1ebe152..6ff78a04d09 100644 --- a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs +++ b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs @@ -37,6 +37,14 @@ module CancellableTasks = open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators open Microsoft.FSharp.Collections + [] + type VolatileBarrier() = + [] + let mutable isStopped = false + + member _.Proceed = not isStopped + member _.Stop() = isStopped <- true + /// A type that looks like an Awaiter type Awaiter<'Awaiter, 'TResult when 'Awaiter :> ICriticalNotifyCompletion @@ -677,6 +685,56 @@ module CancellableTasks = [] module HighPriority = + let inline startAsyncImmediateAsTask computation (cancellationToken: CancellationToken) = + // Protect against blocking the UI thread by switching to thread pool + let computation = + match SynchronizationContext.Current with + | null -> computation + | _ -> + async { + do! Async.SwitchToThreadPool() + return! computation + } + + // try not to yield if on bg thread already + let tcs = new TaskCompletionSource<_>(TaskCreationOptions.None) + let barrier = VolatileBarrier() + + let reg = + cancellationToken.Register(fun _ -> + if barrier.Proceed then + tcs.TrySetCanceled(cancellationToken) |> ignore) + + let task = tcs.Task + + let disposeReg () = + barrier.Stop() + + if not task.IsCanceled then + reg.Dispose() + + Async.StartWithContinuations( + computation, + continuation = + (fun result -> + disposeReg () + tcs.TrySetResult(result) |> ignore), + exceptionContinuation = + (fun exn -> + disposeReg () + + match exn with + | :? OperationCanceledException -> tcs.TrySetCanceled(cancellationToken) |> ignore + | exn -> tcs.TrySetException(exn) |> ignore), + cancellationContinuation = + (fun _oce -> + disposeReg () + tcs.TrySetCanceled(cancellationToken) |> ignore), + cancellationToken = cancellationToken + ) + + task + type Control.Async with /// Return an asynchronous computation that will wait for the given task to complete and return @@ -703,7 +761,7 @@ module CancellableTasks = /// Runs an asynchronous computation, starting on the current operating system thread. static member inline AsCancellableTask(computation: Async<'T>) : CancellableTask<_> = - fun ct -> Async.StartImmediateAsTask(computation, cancellationToken = ct) + fun ct -> startAsyncImmediateAsTask computation ct // High priority extensions type CancellableTaskBuilderBase with diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.resx b/vsintegration/src/FSharp.Editor/FSharp.Editor.resx index 7faf42e190f..3cc5d71012d 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.resx +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.resx @@ -226,6 +226,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 1725906522e..6b07a45f2ee 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -191,7 +191,7 @@ type internal FSharpWorkspaceServiceFactory [] useTransparentCompiler = useTransparentCompiler ) - if enableLiveBuffers then + if enableLiveBuffers && not useTransparentCompiler then workspace.WorkspaceChanged.Add(fun args -> if args.DocumentId <> null then backgroundTask { diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf index 24c8cc476ea..6bf09f95af7 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking Vodítka struktury bloků; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf index c8e43ea2004..f78764e08f3 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking Block Structure Guides; @@ -46,6 +47,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf index 461c87cf36f..bd6a8b08845 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking Guías de estructura de bloques; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf index 33c60f49f00..867bf8015e4 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking Block Structure Guides; @@ -46,6 +47,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf index 0ae400abfa6..20bed5ff971 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking Block Structure Guides; @@ -46,6 +47,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf index 08bc5e4924e..73ce2d36db8 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking ブロック構造ガイド; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf index 7ee219bbc77..988aef96a64 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking Block Structure Guides; @@ -46,6 +47,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf index 6c4a1c54f12..3ef2b3b2e3d 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking Block Structure Guides; @@ -46,6 +47,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf index 6e040346910..7f18691dec6 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking Guias de Estrutura de Bloco; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf index 3a6236f20ec..a6ee544872a 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking Руководства по блочной структуре; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf index f7900271f5c..79c8bd8056a 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking Block Structure Guides; @@ -46,6 +47,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf index fb08ab98d1e..f5706497fa9 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking 块结构指南; diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf index e3f2ede726d..2086f3d1c2f 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf @@ -36,6 +36,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking Block Structure Guides; @@ -46,6 +47,7 @@ Inline hints; Display inline type hints (preview); Display return type hints (preview); Display inline parameter name hints (preview); +Use Transparent Compiler (experimental); Live Buffers; Use live (unsaved) buffers for checking From 302c8cc929f3cb07d77bd307f38ae4d827a1a352 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 12 Jun 2023 11:51:59 +0200 Subject: [PATCH 049/222] wip --- src/Compiler/Service/BackgroundCompiler.fs | 5 + src/Compiler/Service/TransparentCompiler.fs | 290 +++++++++++------- src/Compiler/Service/service.fs | 8 +- src/Compiler/Service/service.fsi | 2 + .../Signatures/MissingDiagnostic.fs | 24 +- .../TypeChecks/TyparNameTests.fs | 2 +- tests/FSharp.Test.Utilities/Compiler.fs | 6 +- tests/FSharp.Test.Utilities/CompilerAssert.fs | 25 +- .../ProjectGeneration.fs | 6 +- 9 files changed, 244 insertions(+), 124 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 4de5d8f7972..b918fce8ffc 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -142,6 +142,8 @@ type internal IBackgroundCompiler = /// Parse and typecheck the whole project. abstract member ParseAndCheckProject: options: FSharpProjectOptions * userOpName: string -> NodeCode + abstract member ParseAndCheckProject: projectSnapshot: FSharpProjectSnapshot * userOpName: string -> NodeCode + abstract member ParseFile: fileName: string * sourceText: ISourceText * options: FSharpParsingOptions * cache: bool * flatErrors: bool * userOpName: string -> Async @@ -1595,6 +1597,9 @@ type internal BackgroundCompiler member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = self.ParseAndCheckProject(options, userOpName) + member _.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string): NodeCode = + self.ParseAndCheckProject(projectSnapshot.ToOptions(), userOpName) + member _.ParseFile ( fileName: string, diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index d783c63e359..aa9570c8f00 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -133,9 +133,12 @@ type internal TransparentCompiler let ParseAndCheckFileInProjectCache = AsyncMemoize(triggerCacheEvent, "ParseAndCheckFileInProject") - let ParseAndCheckFullProjectCache = + let ParseAndCheckAllFilesInProjectCache = AsyncMemoize(triggerCacheEvent, "ParseAndCheckFullProject") + let ParseAndCheckProjectCache = + AsyncMemoize(triggerCacheEvent, "ParseAndCheckProject") + let FrameworkImportsCache = AsyncMemoize(triggerCacheEvent, "FrameworkImports") let BootstrapInfoStaticCache = @@ -1032,8 +1035,8 @@ type internal TransparentCompiler } ) - let ComputeParseAndCheckFullProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = - ParseAndCheckFullProjectCache.Get( + let ComputeParseAndCheckAllFilesInProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = + ParseAndCheckAllFilesInProjectCache.Get( projectSnapshot.Key, node { use _ = @@ -1057,6 +1060,178 @@ type internal TransparentCompiler } ) + let ComputeGetAssemblyData (projectSnapshot: FSharpProjectSnapshot) = + AssemblyDataCache.Get( + projectSnapshot.Key, + node { + + match! ComputeBootstrapInfo projectSnapshot with + | None, _ -> return ProjectAssemblyDataResult.Unavailable true + | Some bootstrapInfo, _creationDiags -> + + let! tcInfos = ComputeParseAndCheckAllFilesInProject bootstrapInfo projectSnapshot + + let assemblyName = bootstrapInfo.AssemblyName + let tcConfig = bootstrapInfo.TcConfig + let tcGlobals = bootstrapInfo.TcGlobals + + let results = + [ + for tcInfo in tcInfos -> + tcInfo.tcEnvAtEndOfFile, defaultArg tcInfo.topAttribs EmptyTopAttrs, None, tcInfo.latestCcuSigForFile + ] + + // Get the state at the end of the type-checking of the last file + let finalInfo = List.last tcInfos + + // Finish the checking + let (_tcEnvAtEndOfLastFile, topAttrs, _mimpls, _), tcState = + CheckMultipleInputsFinish(results, finalInfo.tcState) + + let tcState, _, ccuContents = CheckClosedInputSetFinish([], tcState) + + let generatedCcu = tcState.Ccu.CloneWithFinalizedContents(ccuContents) + + // Compute the identity of the generated assembly based on attributes, options etc. + // Some of this is duplicated from fsc.fs + let ilAssemRef = + let publicKey = + try + let signingInfo = ValidateKeySigningAttributes(tcConfig, tcGlobals, topAttrs) + + match GetStrongNameSigner signingInfo with + | None -> None + | Some s -> Some(PublicKey.KeyAsToken(s.PublicKey)) + with exn -> + errorRecoveryNoRange exn + None + + let locale = + TryFindFSharpStringAttribute + tcGlobals + (tcGlobals.FindSysAttrib "System.Reflection.AssemblyCultureAttribute") + topAttrs.assemblyAttrs + + let assemVerFromAttrib = + TryFindFSharpStringAttribute + tcGlobals + (tcGlobals.FindSysAttrib "System.Reflection.AssemblyVersionAttribute") + topAttrs.assemblyAttrs + |> Option.bind (fun v -> + try + Some(parseILVersion v) + with _ -> + None) + + let ver = + match assemVerFromAttrib with + | None -> tcConfig.version.GetVersionInfo(tcConfig.implicitIncludeDir) + | Some v -> v + + ILAssemblyRef.Create(assemblyName, None, publicKey, false, Some ver, locale) + + let assemblyDataResult = + try + // Assemblies containing type provider components can not successfully be used via cross-assembly references. + // We return 'None' for the assembly portion of the cross-assembly reference + let hasTypeProviderAssemblyAttrib = + topAttrs.assemblyAttrs + |> List.exists (fun (Attrib (tcref, _, _, _, _, _, _)) -> + let nm = tcref.CompiledRepresentationForNamedType.BasicQualifiedName + + nm = typeof + .FullName) + + if tcState.CreatesGeneratedProvidedTypes || hasTypeProviderAssemblyAttrib then + ProjectAssemblyDataResult.Unavailable true + else + ProjectAssemblyDataResult.Available( + RawFSharpAssemblyDataBackedByLanguageService( + tcConfig, + tcGlobals, + generatedCcu, + bootstrapInfo.OutFile, + topAttrs, + assemblyName, + ilAssemRef + ) + :> IRawFSharpAssemblyData + ) + with exn -> + errorRecoveryNoRange exn + ProjectAssemblyDataResult.Unavailable true + + return assemblyDataResult + } + ) + + let ComputeParseAndCheckProject (projectSnapshot: FSharpProjectSnapshot) = + ParseAndCheckProjectCache.Get( + projectSnapshot.Key, + node { + + match! ComputeBootstrapInfo projectSnapshot with + | None, creationDiags -> + return FSharpCheckProjectResults(projectSnapshot.ProjectFileName, None, keepAssemblyContents, creationDiags, None) + | Some bootstrapInfo, creationDiags -> + + let! tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt = builder.GetFullCheckResultsAndImplementationsForProject() + + let diagnosticsOptions = tcProj.TcConfig.diagnosticsOptions + let fileName = DummyFileNameForRangesWithoutASpecificLocation + + // Although we do not use 'tcInfoExtras', computing it will make sure we get an extra info. + let! tcInfo, _tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() + + let topAttribs = tcInfo.topAttribs + let tcState = tcInfo.tcState + let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile + let tcDiagnostics = tcInfo.TcDiagnostics + let tcDependencyFiles = tcInfo.tcDependencyFiles + + let tcDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + diagnosticsOptions, + true, + fileName, + tcDiagnostics, + suggestNamesForErrors, + builder.TcConfig.flatErrors + ) + + let diagnostics = [| yield! creationDiags; yield! tcDiagnostics |] + + let getAssemblyData () = + match tcAssemblyDataOpt with + | ProjectAssemblyDataResult.Available data -> Some data + | _ -> None + + let details = + (tcProj.TcGlobals, + tcProj.TcImports, + tcState.Ccu, + tcState.CcuSig, + Choice1Of2 builder, + topAttribs, + getAssemblyData, + ilAssemRef, + tcEnvAtEnd.AccessRights, + tcAssemblyExprOpt, + Array.ofList tcDependencyFiles, + options) + + let results = + FSharpCheckProjectResults( + options.ProjectFileName, + Some tcProj.TcConfig, + keepAssemblyContents, + diagnostics, + Some details + ) + + return results + }) + let tryGetSink fileName (projectSnapshot: FSharpProjectSnapshot) = node { match! ComputeBootstrapInfo projectSnapshot with @@ -1173,109 +1348,7 @@ type internal TransparentCompiler } member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, _userOpName) = - AssemblyDataCache.Get( - projectSnapshot.Key, - node { - - match! ComputeBootstrapInfo projectSnapshot with - | None, _ -> return ProjectAssemblyDataResult.Unavailable true - | Some bootstrapInfo, _creationDiags -> - - let! tcInfos = ComputeParseAndCheckFullProject bootstrapInfo projectSnapshot - - let assemblyName = bootstrapInfo.AssemblyName - let tcConfig = bootstrapInfo.TcConfig - let tcGlobals = bootstrapInfo.TcGlobals - - let results = - [ - for tcInfo in tcInfos -> - tcInfo.tcEnvAtEndOfFile, defaultArg tcInfo.topAttribs EmptyTopAttrs, None, tcInfo.latestCcuSigForFile - ] - - // Get the state at the end of the type-checking of the last file - let finalInfo = List.last tcInfos - - // Finish the checking - let (_tcEnvAtEndOfLastFile, topAttrs, _mimpls, _), tcState = - CheckMultipleInputsFinish(results, finalInfo.tcState) - - let tcState, _, ccuContents = CheckClosedInputSetFinish([], tcState) - - let generatedCcu = tcState.Ccu.CloneWithFinalizedContents(ccuContents) - - // Compute the identity of the generated assembly based on attributes, options etc. - // Some of this is duplicated from fsc.fs - let ilAssemRef = - let publicKey = - try - let signingInfo = ValidateKeySigningAttributes(tcConfig, tcGlobals, topAttrs) - - match GetStrongNameSigner signingInfo with - | None -> None - | Some s -> Some(PublicKey.KeyAsToken(s.PublicKey)) - with exn -> - errorRecoveryNoRange exn - None - - let locale = - TryFindFSharpStringAttribute - tcGlobals - (tcGlobals.FindSysAttrib "System.Reflection.AssemblyCultureAttribute") - topAttrs.assemblyAttrs - - let assemVerFromAttrib = - TryFindFSharpStringAttribute - tcGlobals - (tcGlobals.FindSysAttrib "System.Reflection.AssemblyVersionAttribute") - topAttrs.assemblyAttrs - |> Option.bind (fun v -> - try - Some(parseILVersion v) - with _ -> - None) - - let ver = - match assemVerFromAttrib with - | None -> tcConfig.version.GetVersionInfo(tcConfig.implicitIncludeDir) - | Some v -> v - - ILAssemblyRef.Create(assemblyName, None, publicKey, false, Some ver, locale) - - let assemblyDataResult = - try - // Assemblies containing type provider components can not successfully be used via cross-assembly references. - // We return 'None' for the assembly portion of the cross-assembly reference - let hasTypeProviderAssemblyAttrib = - topAttrs.assemblyAttrs - |> List.exists (fun (Attrib (tcref, _, _, _, _, _, _)) -> - let nm = tcref.CompiledRepresentationForNamedType.BasicQualifiedName - - nm = typeof - .FullName) - - if tcState.CreatesGeneratedProvidedTypes || hasTypeProviderAssemblyAttrib then - ProjectAssemblyDataResult.Unavailable true - else - ProjectAssemblyDataResult.Available( - RawFSharpAssemblyDataBackedByLanguageService( - tcConfig, - tcGlobals, - generatedCcu, - bootstrapInfo.OutFile, - topAttrs, - assemblyName, - ilAssemRef - ) - :> IRawFSharpAssemblyData - ) - with exn -> - errorRecoveryNoRange exn - ProjectAssemblyDataResult.Unavailable true - - return assemblyDataResult - } - ) + ComputeGetAssemblyData projectSnapshot interface IBackgroundCompiler with @@ -1438,6 +1511,11 @@ type internal TransparentCompiler member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = backgroundCompiler.ParseAndCheckProject(options, userOpName) + member this.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string): NodeCode = + //this.ParseAndCheckProject(projectSnapshot, userOpName) + ignore (projectSnapshot, userOpName) + ComputeParseAndCheckProject projectSnapshot + member this.ParseFile(fileName, projectSnapshot, userOpName) = this.ParseFile(fileName, projectSnapshot, userOpName) diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index ffa38bee1c2..1cc87c71606 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -446,12 +446,18 @@ type FSharpChecker backgroundCompiler.ParseAndCheckFileInProject(fileName, projectSnapshot, userOpName) |> Async.AwaitNodeCode - member _.ParseAndCheckProject(options, ?userOpName: string) = + member _.ParseAndCheckProject(options: FSharpProjectOptions, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.ParseAndCheckProject(options, userOpName) |> Async.AwaitNodeCode + member _.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + + backgroundCompiler.ParseAndCheckProject(projectSnapshot, userOpName) + |> Async.AwaitNodeCode + member _.FindBackgroundReferencesInFile ( fileName: string, diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index 30ce574813f..e62d948c2fd 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -213,6 +213,8 @@ type public FSharpChecker = /// An optional string used for tracing compiler operations associated with this request. member ParseAndCheckProject: options: FSharpProjectOptions * ?userOpName: string -> Async + member ParseAndCheckProject: projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async + /// /// For a given script file, get the FSharpProjectOptions implied by the #load closure. /// All files are read from the FileSystem API, except the file being checked. diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/MissingDiagnostic.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/MissingDiagnostic.fs index 2d263fa728f..4eef2a7407b 100644 --- a/tests/FSharp.Compiler.ComponentTests/Signatures/MissingDiagnostic.fs +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/MissingDiagnostic.fs @@ -27,28 +27,34 @@ let ``Compile gives errors`` () = but here has type 'char' ") -[] -let ``Type check project with signature file doesn't get the diagnostic`` () = +[] +[] +[] +let ``Type check project with signature file doesn't get the diagnostic`` useTransparentCompiler = Fsi signature |> withAdditionalSourceFile (FsSource implementation) - |> typecheckProject false + |> typecheckProject false useTransparentCompiler |> fun projectResults -> projectResults.Diagnostics |> ignore Assert.False (projectResults.Diagnostics |> Array.isEmpty) -[] -let ``Type check project without signature file does get the diagnostic`` () = +[] +[] +[] +let ``Type check project without signature file does get the diagnostic`` useTransparentCompiler = Fs implementation - |> typecheckProject false + |> typecheckProject false useTransparentCompiler |> fun projectResults -> projectResults.Diagnostics |> ignore Assert.False (projectResults.Diagnostics |> Array.isEmpty) -[] -let ``Enabling enablePartialTypeChecking = true doesn't change the problem`` () = +[] +[] +[] +let ``Enabling enablePartialTypeChecking = true doesn't change the problem`` useTransparentCompiler = Fsi signature |> withAdditionalSourceFile (FsSource implementation) - |> typecheckProject true + |> typecheckProject true useTransparentCompiler |> fun projectResults -> projectResults.Diagnostics |> ignore Assert.False (projectResults.Diagnostics |> Array.isEmpty) \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs index 2761fb8cfeb..a34fff5b068 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs @@ -12,7 +12,7 @@ let private getGenericParametersNamesFor (additionalFile: SourceCodeFileKind) : string array = let typeCheckResult = - cUnit |> withAdditionalSourceFile additionalFile |> typecheckProject false + cUnit |> withAdditionalSourceFile additionalFile |> typecheckProject false false assert (Array.isEmpty typeCheckResult.Diagnostics) diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index caa56ba97e5..3de026d5319 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -27,6 +27,7 @@ open TestFramework open System.Runtime.CompilerServices open System.Runtime.InteropServices +open FSharp.Compiler.CodeAnalysis module rec Compiler = @@ -917,7 +918,7 @@ module rec Compiler = CompilerAssert.TypeCheck(options, fileName, source) | _ -> failwith "Typecheck only supports F#" - let typecheckProject enablePartialTypeChecking (cUnit: CompilationUnit) : FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults = + let typecheckProject enablePartialTypeChecking useTransparentCompiler (cUnit: CompilationUnit) : FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults = match cUnit with | FS fsSource -> let options = fsSource.Options |> Array.ofList @@ -935,7 +936,8 @@ module rec Compiler = |> async.Return let sourceFiles = Array.map fst sourceFiles - CompilerAssert.TypeCheckProject(options, sourceFiles, getSourceText, enablePartialTypeChecking) + + CompilerAssert.TypeCheckProject(options, sourceFiles, getSourceText, enablePartialTypeChecking, useTransparentCompiler) | _ -> failwith "Typecheck only supports F#" let run (result: CompilationResult) : CompilationResult = diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index f209beab7d0..ee36b9c0f9b 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -858,14 +858,31 @@ Updated automatically, please check diffs in your pull request, changes must be static member TypeCheckSingleError (source: string) (expectedSeverity: FSharpDiagnosticSeverity) (expectedErrorNumber: int) (expectedErrorRange: int * int * int * int) (expectedErrorMsg: string) = CompilerAssert.TypeCheckWithErrors source [| expectedSeverity, expectedErrorNumber, expectedErrorRange, expectedErrorMsg |] - static member TypeCheckProject(options: string array, sourceFiles: string array, getSourceText, enablePartialTypeChecking) : FSharpCheckProjectResults = - let checker = FSharpChecker.Create(documentSource = DocumentSource.Custom getSourceText, enablePartialTypeChecking = enablePartialTypeChecking) + static member TypeCheckProject(options: string array, sourceFiles: string array, getSourceText, enablePartialTypeChecking, useTransparentCompiler) : FSharpCheckProjectResults = + let checker = FSharpChecker.Create(documentSource = DocumentSource.Custom getSourceText, enablePartialTypeChecking = enablePartialTypeChecking, useTransparentCompiler = useTransparentCompiler) let defaultOptions = defaultProjectOptions TargetFramework.Current let projectOptions = { defaultOptions with OtherOptions = Array.append options defaultOptions.OtherOptions; SourceFiles = sourceFiles } - checker.ParseAndCheckProject(projectOptions) + if useTransparentCompiler then + let getFileSnapshot _ fileName = + async.Return + { + FileName = fileName + Version = "1" + GetSource = fun () -> task { + match! getSourceText fileName with + | Some source -> return source + | None -> return failwith $"couldn't get source for {fileName}" + } + } + + let snapshot = FSharpProjectSnapshot.FromOptions(projectOptions, getFileSnapshot) |> Async.RunSynchronously + + checker.ParseAndCheckProject(snapshot) + else + checker.ParseAndCheckProject(projectOptions) |> Async.RunImmediate - + static member CompileExeWithOptions(options, (source: SourceCodeFileKind)) = compile true options source (fun (errors, _, _) -> if errors.Length > 0 then diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 710d53b1b23..2705a86118e 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -1001,4 +1001,8 @@ type SyntheticProject with /// Saves project to disk and checks it with default options. Returns the FSharpChecker that was created member this.SaveAndCheck() = this.Workflow.Yield() |> Async.RunSynchronously |> ignore - this.Workflow.Checker \ No newline at end of file + this.Workflow.Checker + + +let replicateRealProject _projectFilePath = + () \ No newline at end of file From 41ce83f4368d9e57132ec08fc534044321d161ac Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 12 Jun 2023 16:33:51 +0200 Subject: [PATCH 050/222] ParseAndCheckProject --- src/Compiler/Service/BackgroundCompiler.fs | 2 +- src/Compiler/Service/FSharpCheckerResults.fs | 15 +- src/Compiler/Service/FSharpCheckerResults.fsi | 2 +- src/Compiler/Service/TransparentCompiler.fs | 219 +++++++++--------- src/Compiler/Service/service.fsi | 3 +- .../Signatures/MissingDiagnostic.fs | 12 +- ...ervice.SurfaceArea.netstandard20.debug.bsl | 1 + ...vice.SurfaceArea.netstandard20.release.bsl | 1 + 8 files changed, 135 insertions(+), 120 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index b918fce8ffc..3feb20dbebc 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -1597,7 +1597,7 @@ type internal BackgroundCompiler member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = self.ParseAndCheckProject(options, userOpName) - member _.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string): NodeCode = + member _.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = self.ParseAndCheckProject(projectSnapshot.ToOptions(), userOpName) member _.ParseFile diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 0eba5a62d9e..4c221e72fdf 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -3331,7 +3331,7 @@ type FSharpCheckProjectResults tcConfigOption: TcConfig option, keepAssemblyContents: bool, diagnostics: FSharpDiagnostic[], - details: (TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * Choice * TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * AccessorDomain * CheckedImplFile list option * string[] * FSharpProjectOptions) option + details: (TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * Choice * TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * AccessorDomain * CheckedImplFile list option * string[] * FSharpProjectOptions) option ) = let getDetails () = @@ -3362,6 +3362,7 @@ type FSharpCheckProjectResults FSharpAssemblySignature(tcGlobals, thisCcu, ccuSig, tcImports, topAttribs, ccuSig) + // TODO: Looks like we don't need this member _.TypedImplementationFiles = if not keepAssemblyContents then invalidOp @@ -3437,7 +3438,12 @@ type FSharpCheckProjectResults | Some (_, tcInfoExtras) -> tcInfoExtras.TcSymbolUses.GetUsesOfSymbol symbol.Item | _ -> [||] | _ -> [||]) - | Choice2Of2 tcSymbolUses -> tcSymbolUses.GetUsesOfSymbol symbol.Item + |> Array.toSeq + | Choice2Of2 tcSymbolUses -> + seq { + for symbolUses in tcSymbolUses do + yield! symbolUses.GetUsesOfSymbol symbol.Item + } results |> Seq.filter (fun symbolUse -> symbolUse.ItemOccurence <> ItemOccurence.RelatedText) @@ -3467,7 +3473,8 @@ type FSharpCheckProjectResults | Some (_, tcInfoExtras) -> tcInfoExtras.TcSymbolUses | _ -> TcSymbolUses.Empty | _ -> TcSymbolUses.Empty) - | Choice2Of2 tcSymbolUses -> [| tcSymbolUses |] + |> Array.toSeq + | Choice2Of2 tcSymbolUses -> tcSymbolUses [| for r in tcSymbolUses do @@ -3600,7 +3607,7 @@ type FsiInteractiveChecker(legacyReferenceResolver, tcConfig: TcConfig, tcGlobal tcImports, tcFileInfo.ThisCcu, tcFileInfo.CcuSigForFile, - Choice2Of2 tcFileInfo.ScopeSymbolUses, + Choice2Of2(Seq.singleton tcFileInfo.ScopeSymbolUses), None, (fun () -> None), mkSimpleAssemblyRef "stdin", diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 6a1a3ffb1a7..d3ee5fc4ea5 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -653,7 +653,7 @@ type public FSharpCheckProjectResults = tcConfigOption: TcConfig option * keepAssemblyContents: bool * diagnostics: FSharpDiagnostic[] * - details: (TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * Choice * TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * AccessorDomain * CheckedImplFile list option * string[] * FSharpProjectOptions) option -> + details: (TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * Choice * TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * AccessorDomain * CheckedImplFile list option * string[] * FSharpProjectOptions) option -> FSharpCheckProjectResults module internal ParseAndCheckFile = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index aa9570c8f00..65433cfcd6a 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -152,6 +152,8 @@ type internal TransparentCompiler let DependencyGraphCache = AsyncMemoize(triggerCacheEvent, "DependencyGraph") + let ProjectExtrasCache = AsyncMemoize(triggerCacheEvent, "ProjectExtras") + let AssemblyDataCache = AsyncMemoize(triggerCacheEvent, "AssemblyData") let SemanticClassificationCache = @@ -1060,107 +1062,114 @@ type internal TransparentCompiler } ) - let ComputeGetAssemblyData (projectSnapshot: FSharpProjectSnapshot) = - AssemblyDataCache.Get( + let ComputeProjectExtras (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = + ProjectExtrasCache.Get( projectSnapshot.Key, node { + let! tcInfos = ComputeParseAndCheckAllFilesInProject bootstrapInfo projectSnapshot - match! ComputeBootstrapInfo projectSnapshot with - | None, _ -> return ProjectAssemblyDataResult.Unavailable true - | Some bootstrapInfo, _creationDiags -> - - let! tcInfos = ComputeParseAndCheckAllFilesInProject bootstrapInfo projectSnapshot + let assemblyName = bootstrapInfo.AssemblyName + let tcConfig = bootstrapInfo.TcConfig + let tcGlobals = bootstrapInfo.TcGlobals - let assemblyName = bootstrapInfo.AssemblyName - let tcConfig = bootstrapInfo.TcConfig - let tcGlobals = bootstrapInfo.TcGlobals + let results = + [ + for tcInfo in tcInfos -> + tcInfo.tcEnvAtEndOfFile, defaultArg tcInfo.topAttribs EmptyTopAttrs, None, tcInfo.latestCcuSigForFile + ] - let results = - [ - for tcInfo in tcInfos -> - tcInfo.tcEnvAtEndOfFile, defaultArg tcInfo.topAttribs EmptyTopAttrs, None, tcInfo.latestCcuSigForFile - ] + // Get the state at the end of the type-checking of the last file + let finalInfo = List.last tcInfos - // Get the state at the end of the type-checking of the last file - let finalInfo = List.last tcInfos + // Finish the checking + let (_tcEnvAtEndOfLastFile, topAttrs, _mimpls, _), tcState = + CheckMultipleInputsFinish(results, finalInfo.tcState) - // Finish the checking - let (_tcEnvAtEndOfLastFile, topAttrs, _mimpls, _), tcState = - CheckMultipleInputsFinish(results, finalInfo.tcState) + let tcState, _, ccuContents = CheckClosedInputSetFinish([], tcState) - let tcState, _, ccuContents = CheckClosedInputSetFinish([], tcState) + let generatedCcu = tcState.Ccu.CloneWithFinalizedContents(ccuContents) - let generatedCcu = tcState.Ccu.CloneWithFinalizedContents(ccuContents) + // Compute the identity of the generated assembly based on attributes, options etc. + // Some of this is duplicated from fsc.fs + let ilAssemRef = + let publicKey = + try + let signingInfo = ValidateKeySigningAttributes(tcConfig, tcGlobals, topAttrs) - // Compute the identity of the generated assembly based on attributes, options etc. - // Some of this is duplicated from fsc.fs - let ilAssemRef = - let publicKey = + match GetStrongNameSigner signingInfo with + | None -> None + | Some s -> Some(PublicKey.KeyAsToken(s.PublicKey)) + with exn -> + errorRecoveryNoRange exn + None + + let locale = + TryFindFSharpStringAttribute + tcGlobals + (tcGlobals.FindSysAttrib "System.Reflection.AssemblyCultureAttribute") + topAttrs.assemblyAttrs + + let assemVerFromAttrib = + TryFindFSharpStringAttribute + tcGlobals + (tcGlobals.FindSysAttrib "System.Reflection.AssemblyVersionAttribute") + topAttrs.assemblyAttrs + |> Option.bind (fun v -> try - let signingInfo = ValidateKeySigningAttributes(tcConfig, tcGlobals, topAttrs) + Some(parseILVersion v) + with _ -> + None) - match GetStrongNameSigner signingInfo with - | None -> None - | Some s -> Some(PublicKey.KeyAsToken(s.PublicKey)) - with exn -> - errorRecoveryNoRange exn - None + let ver = + match assemVerFromAttrib with + | None -> tcConfig.version.GetVersionInfo(tcConfig.implicitIncludeDir) + | Some v -> v - let locale = - TryFindFSharpStringAttribute - tcGlobals - (tcGlobals.FindSysAttrib "System.Reflection.AssemblyCultureAttribute") - topAttrs.assemblyAttrs - - let assemVerFromAttrib = - TryFindFSharpStringAttribute - tcGlobals - (tcGlobals.FindSysAttrib "System.Reflection.AssemblyVersionAttribute") - topAttrs.assemblyAttrs - |> Option.bind (fun v -> - try - Some(parseILVersion v) - with _ -> - None) - - let ver = - match assemVerFromAttrib with - | None -> tcConfig.version.GetVersionInfo(tcConfig.implicitIncludeDir) - | Some v -> v - - ILAssemblyRef.Create(assemblyName, None, publicKey, false, Some ver, locale) - - let assemblyDataResult = - try - // Assemblies containing type provider components can not successfully be used via cross-assembly references. - // We return 'None' for the assembly portion of the cross-assembly reference - let hasTypeProviderAssemblyAttrib = - topAttrs.assemblyAttrs - |> List.exists (fun (Attrib (tcref, _, _, _, _, _, _)) -> - let nm = tcref.CompiledRepresentationForNamedType.BasicQualifiedName - - nm = typeof - .FullName) - - if tcState.CreatesGeneratedProvidedTypes || hasTypeProviderAssemblyAttrib then - ProjectAssemblyDataResult.Unavailable true - else - ProjectAssemblyDataResult.Available( - RawFSharpAssemblyDataBackedByLanguageService( - tcConfig, - tcGlobals, - generatedCcu, - bootstrapInfo.OutFile, - topAttrs, - assemblyName, - ilAssemRef - ) - :> IRawFSharpAssemblyData - ) - with exn -> - errorRecoveryNoRange exn + ILAssemblyRef.Create(assemblyName, None, publicKey, false, Some ver, locale) + + let assemblyDataResult = + try + // Assemblies containing type provider components can not successfully be used via cross-assembly references. + // We return 'None' for the assembly portion of the cross-assembly reference + let hasTypeProviderAssemblyAttrib = + topAttrs.assemblyAttrs + |> List.exists (fun (Attrib (tcref, _, _, _, _, _, _)) -> + let nm = tcref.CompiledRepresentationForNamedType.BasicQualifiedName + + nm = typeof + .FullName) + + if tcState.CreatesGeneratedProvidedTypes || hasTypeProviderAssemblyAttrib then ProjectAssemblyDataResult.Unavailable true + else + ProjectAssemblyDataResult.Available( + RawFSharpAssemblyDataBackedByLanguageService( + bootstrapInfo.TcConfig, + bootstrapInfo.TcGlobals, + generatedCcu, + bootstrapInfo.OutFile, + topAttrs, + bootstrapInfo.AssemblyName, + ilAssemRef + ) + :> IRawFSharpAssemblyData + ) + with exn -> + errorRecoveryNoRange exn + ProjectAssemblyDataResult.Unavailable true + return finalInfo, ilAssemRef, assemblyDataResult + } + ) + + let ComputeGetAssemblyData (projectSnapshot: FSharpProjectSnapshot) = + AssemblyDataCache.Get( + projectSnapshot.Key, + node { + match! ComputeBootstrapInfo projectSnapshot with + | None, _ -> return ProjectAssemblyDataResult.Unavailable true + | Some bootstrapInfo, _creationDiags -> + let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot return assemblyDataResult } ) @@ -1171,18 +1180,15 @@ type internal TransparentCompiler node { match! ComputeBootstrapInfo projectSnapshot with - | None, creationDiags -> + | None, creationDiags -> return FSharpCheckProjectResults(projectSnapshot.ProjectFileName, None, keepAssemblyContents, creationDiags, None) | Some bootstrapInfo, creationDiags -> - let! tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt = builder.GetFullCheckResultsAndImplementationsForProject() + let! tcInfo, ilAssemRef, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot - let diagnosticsOptions = tcProj.TcConfig.diagnosticsOptions + let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions let fileName = DummyFileNameForRangesWithoutASpecificLocation - // Although we do not use 'tcInfoExtras', computing it will make sure we get an extra info. - let! tcInfo, _tcInfoExtras = tcProj.GetOrComputeTcInfoWithExtras() - let topAttribs = tcInfo.topAttribs let tcState = tcInfo.tcState let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile @@ -1196,41 +1202,42 @@ type internal TransparentCompiler fileName, tcDiagnostics, suggestNamesForErrors, - builder.TcConfig.flatErrors + bootstrapInfo.TcConfig.flatErrors ) let diagnostics = [| yield! creationDiags; yield! tcDiagnostics |] let getAssemblyData () = - match tcAssemblyDataOpt with + match assemblyDataResult with | ProjectAssemblyDataResult.Available data -> Some data | _ -> None let details = - (tcProj.TcGlobals, - tcProj.TcImports, + (bootstrapInfo.TcGlobals, + bootstrapInfo.TcImports, tcState.Ccu, tcState.CcuSig, - Choice1Of2 builder, + Choice2Of2 Seq.empty, // TODO: SymbolUses, topAttribs, getAssemblyData, ilAssemRef, tcEnvAtEnd.AccessRights, - tcAssemblyExprOpt, + None, Array.ofList tcDependencyFiles, - options) + projectSnapshot.ToOptions()) let results = FSharpCheckProjectResults( - options.ProjectFileName, - Some tcProj.TcConfig, + projectSnapshot.ProjectFileName, + Some bootstrapInfo.TcConfig, keepAssemblyContents, diagnostics, Some details ) return results - }) + } + ) let tryGetSink fileName (projectSnapshot: FSharpProjectSnapshot) = node { @@ -1347,8 +1354,7 @@ type internal TransparentCompiler | Some itemKeyStore -> return itemKeyStore.FindAll symbol.Item } - member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, _userOpName) = - ComputeGetAssemblyData projectSnapshot + member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, _userOpName) = ComputeGetAssemblyData projectSnapshot interface IBackgroundCompiler with @@ -1511,9 +1517,8 @@ type internal TransparentCompiler member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = backgroundCompiler.ParseAndCheckProject(options, userOpName) - member this.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string): NodeCode = - //this.ParseAndCheckProject(projectSnapshot, userOpName) - ignore (projectSnapshot, userOpName) + member this.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = + ignore userOpName ComputeParseAndCheckProject projectSnapshot member this.ParseFile(fileName, projectSnapshot, userOpName) = diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index e62d948c2fd..b1f893dbba7 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -213,7 +213,8 @@ type public FSharpChecker = /// An optional string used for tracing compiler operations associated with this request. member ParseAndCheckProject: options: FSharpProjectOptions * ?userOpName: string -> Async - member ParseAndCheckProject: projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async + member ParseAndCheckProject: + projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async /// /// For a given script file, get the FSharpProjectOptions implied by the #load closure. diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/MissingDiagnostic.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/MissingDiagnostic.fs index 4eef2a7407b..d5fb46e2c9c 100644 --- a/tests/FSharp.Compiler.ComponentTests/Signatures/MissingDiagnostic.fs +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/MissingDiagnostic.fs @@ -28,8 +28,8 @@ but here has type 'char' ") [] -[] -[] +[] +[] let ``Type check project with signature file doesn't get the diagnostic`` useTransparentCompiler = Fsi signature |> withAdditionalSourceFile (FsSource implementation) @@ -39,8 +39,8 @@ let ``Type check project with signature file doesn't get the diagnostic`` useTra Assert.False (projectResults.Diagnostics |> Array.isEmpty) [] -[] -[] +[] +[] let ``Type check project without signature file does get the diagnostic`` useTransparentCompiler = Fs implementation |> typecheckProject false useTransparentCompiler @@ -49,8 +49,8 @@ let ``Type check project without signature file does get the diagnostic`` useTra Assert.False (projectResults.Diagnostics |> Array.isEmpty) [] -[] -[] +[] +[] let ``Enabling enablePartialTypeChecking = true doesn't change the problem`` useTransparentCompiler = Fsi signature |> withAdditionalSourceFile (FsSource implementation) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 95bc1d94324..e2883532ef7 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2058,6 +2058,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualCheckFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualParseFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer] CheckFileInProject(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] GetBackgroundParseResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpParsingOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 95bc1d94324..e2883532ef7 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2058,6 +2058,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualCheckFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualParseFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer] CheckFileInProject(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] GetBackgroundParseResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpParsingOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) From a7e1e964433cc90b439835a73394d15b5ea38eb8 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 14 Jun 2023 15:48:45 +0200 Subject: [PATCH 051/222] Fix dependency manager --- .../FSharp.DependencyManager.Utilities.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs index b083202fa72..1e8f6bfd2ae 100644 --- a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs +++ b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs @@ -197,7 +197,7 @@ module internal Utilities = psi.EnvironmentVariables.Remove("MSBuildSDKsPath") // Host can sometimes add this, and it can break things for varname, value in environment do - psi.EnvironmentVariables.Add(varname, value) + psi.EnvironmentVariables[varname] <- value psi.UseShellExecute <- false From 6f703c82ae824f2a48792cba347420d98e87f9fa Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Fri, 16 Jun 2023 12:30:01 +0200 Subject: [PATCH 052/222] Giraffe benchmark --- .../FSharpChecker/TransparentCompiler.fs | 6 +- .../ProjectGeneration.fs | 265 ++++++++++++++++-- .../BackgroundCompilerBenchmarks.fs | 93 ++++++ 3 files changed, 335 insertions(+), 29 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 1dc0cea36f1..d064308ebde 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -1,4 +1,4 @@ -module FSharp.Compiler.ComponentTests.FSharpChecker.TransparentCompiler +module FSharpChecker.TransparentCompiler open System.Collections.Concurrent open System.Diagnostics @@ -139,7 +139,7 @@ let ``Signature update`` () = let project = SyntheticProject.Create( { sourceFile "First" [] with - Source = "let f x = x" + Source = "let f (x: int) = x" SignatureFile = Custom "val f: x: int -> int" }, { sourceFile "Second" ["First"] with Source = "let a x = ModuleFirst.f x" }) @@ -304,4 +304,4 @@ let ``Files that are not depended on don't invalidate cache part 2`` () = Assert.Equal([Started; Finished], graphConstructions["FileE.fs"]) - Assert.Equal([], intermediateTypeChecks |> Map.toList) \ No newline at end of file + Assert.Equal([], intermediateTypeChecks |> Map.toList) diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 2705a86118e..f5351e383c2 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -16,14 +16,19 @@ module FSharp.Test.ProjectGeneration open System +open System.Collections.Concurrent +open System.Diagnostics open System.IO +open System.Text +open System.Text.RegularExpressions open System.Threading.Tasks +open System.Xml + open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Diagnostics open FSharp.Compiler.Text + open Xunit -open System.Collections.Concurrent -open System.Text open OpenTelemetry open OpenTelemetry.Resources @@ -36,6 +41,131 @@ let private projectRoot = "test-projects" let private defaultFunctionName = "f" +module ReferenceHelpers = + + type Runtime = + { Name: string + Version: string + Path: DirectoryInfo } + + module Seq = + let filterOut predicate = Seq.filter (predicate >> not) + + let filterOutAny predicates = + filterOut (fun x -> predicates |> Seq.exists ((|>) x)) + + let getNugetReferences nugetSourceOpt references = + seq { + for nugetSource in nugetSourceOpt |> Option.toList do + $"#i \"nuget:{nugetSource}\"" + + for name, versionOpt in references do + let version = versionOpt |> Option.map (sprintf ", %s") |> Option.defaultValue "" + $"#r \"nuget: %s{name}{version}\"" + } + |> String.concat "\n" + + let getFrameworkReference (name, versionOpt) = + + let getRuntimeList () = + // You can see which versions of the .NET runtime are currently installed with the following command. + let psi = + ProcessStartInfo("dotnet", "--list-runtimes", RedirectStandardOutput = true, UseShellExecute = false) + + let proc = Process.Start(psi) + proc.WaitForExit() + + let output = + seq { + while not proc.StandardOutput.EndOfStream do + proc.StandardOutput.ReadLine() + } + + /// Regex for output like: Microsoft.AspNetCore.App 5.0.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] + let listRuntimesRegex = Regex("([^\s]+) ([^\s]+) \[(.*?)\\]") + + output + |> Seq.map (fun x -> + let matches = listRuntimesRegex.Match(x) + let (version: string) = matches.Groups.[2].Value + + { Name = matches.Groups.[1].Value + Version = version + Path = DirectoryInfo(Path.Combine(matches.Groups[3].Value, version)) }) + + let createRuntimeLoadScript blockedDlls (r: Runtime) = + let dir = r.Path + + let isDLL (f: FileInfo) = f.Extension = ".dll" + + let tripleQuoted (s: string) = $"\"\"\"{s}\"\"\"" + + let packageSource (source: string) = $"#I {tripleQuoted source}" + + let reference (ref: string) = $"#r \"{ref}\"" + + let fileReferences = + dir.GetFiles() + |> Seq.filter isDLL + |> Seq.filterOutAny blockedDlls + |> Seq.map (fun f -> reference f.Name) + + seq { + packageSource dir.FullName + yield! fileReferences + } + |> String.concat "\n" + + let contains (x: string) (y: FileInfo) = y.Name.Contains x + + // List of DLLs that FSI can't load + let blockedDlls = + [ contains "aspnetcorev2_inprocess" + contains "api-ms-win" + contains "clrjit" + contains "clrgc" + contains "clretwrc" + contains "coreclr" + contains "hostpolicy" + contains "Microsoft.DiaSymReader.Native.amd64" + contains "mscordaccore_amd64_amd64_7" + contains "mscordaccore" + contains "msquic" + contains "mscordbi" + contains "mscorrc" + contains "System.IO.Compression.Native" ] + + let runTimeLoadScripts = + getRuntimeList () + |> Seq.map (fun runtime -> runtime.Name, (runtime, createRuntimeLoadScript blockedDlls runtime)) + |> Seq.groupBy fst + |> Seq.map (fun (name, runtimes) -> name, runtimes |> Seq.map snd |> Seq.toList) + |> Map + + runTimeLoadScripts + |> Map.tryFind name + |> Option.map ( + List.filter (fun (r, _) -> + match versionOpt with + | Some v -> r.Version = v + | None -> not (r.Version.Contains "preview")) + >> List.sortByDescending (fun (r, _) -> r.Version) + ) + |> Option.bind List.tryHead + |> Option.map snd + |> Option.defaultWith (fun () -> + failwith $"Couldn't find framework reference {name} {versionOpt}. Available Runtimes: \n" + + (runTimeLoadScripts + |> Map.toSeq + |> Seq.map snd + |> Seq.collect (List.map fst) + |> Seq.map (fun r -> $"{r.Name} {r.Version}") + |> String.concat "\n")) + + +open ReferenceHelpers + + type SignatureFile = | No | AutoGenerated @@ -92,7 +222,10 @@ type SyntheticProject = SourceFiles: SyntheticSourceFile list DependsOn: SyntheticProject list RecursiveNamespace: bool - OtherOptions: string list } + OtherOptions: string list + AutoAddModules: bool + NugetReferences: (string * string option) list + FrameworkReferences: (string * string option) list } static member Create(?name: string) = let name = defaultArg name $"TestProject_{Guid.NewGuid().ToString()[..7]}" @@ -103,7 +236,10 @@ type SyntheticProject = SourceFiles = [] DependsOn = [] RecursiveNamespace = false - OtherOptions = [] } + OtherOptions = [] + AutoAddModules = true + NugetReferences = [] + FrameworkReferences = [] } static member Create([] sourceFiles: SyntheticSourceFile[]) = { SyntheticProject.Create() with SourceFiles = sourceFiles |> List.ofArray } @@ -143,16 +279,25 @@ type SyntheticProject = [ p.Name f.Id if f.HasSignatureFile then - "s" ]) + "s" ]), + this.FrameworkReferences, + this.NugetReferences if not (OptionsCache.ContainsKey cacheKey) then OptionsCache[cacheKey] <- use _ = Activity.start "SyntheticProject.GetProjectOptions" [ "project", this.Name ] + let referenceScript = + seq { + yield! this.FrameworkReferences |> Seq.map getFrameworkReference + this.NugetReferences |> getNugetReferences (Some "https://api.nuget.org/v3/index.json") + } + |> String.concat "\n" + let baseOptions, _ = checker.GetProjectOptionsFromScript( - "file.fs", - SourceText.ofString "", + "file.fsx", + SourceText.ofString referenceScript, assumeDotNetFramework = false ) |> Async.RunSynchronously @@ -167,11 +312,13 @@ type SyntheticProject = this.ProjectDir ++ f.FileName |] OtherOptions = - [| yield! baseOptions.OtherOptions + Set [ + yield! baseOptions.OtherOptions "--optimize+" for p in this.DependsOn do $"-r:{p.OutputFilename}" - yield! this.OtherOptions |] + yield! this.OtherOptions ] + |> Set.toArray ReferencedProjects = [| for p in this.DependsOn do FSharpReferencedProject.FSharpReference(p.OutputFilename, p.GetProjectOptions checker) |] @@ -221,11 +368,12 @@ let private renderNamespaceModule (project: SyntheticProject) (f: SyntheticSourc let renderSourceFile (project: SyntheticProject) (f: SyntheticSourceFile) = seq { - renderNamespaceModule project f - if f.Source <> "" then + if project.AutoAddModules then + renderNamespaceModule project f f.Source else + renderNamespaceModule project f for p in project.DependsOn do $"open {p.Name}" @@ -272,6 +420,14 @@ let private renderFsProj (p: SyntheticProject) = """ + for fwr, v in p.FrameworkReferences do + let version = v |> Option.map (fun v -> $" Version=\"{v}\"") |> Option.defaultValue "" + $"" + + for nr, v in p.NugetReferences do + let version = v |> Option.map (fun v -> $" Version=\"{v}\"") |> Option.defaultValue "" + $"" + for f in p.SourceFiles do if f.HasSignatureFile then $"" @@ -415,10 +571,19 @@ module ProjectOperations = | FSharpCheckFileAnswer.Aborted -> failwith "Type checking was aborted" | FSharpCheckFileAnswer.Succeeded checkResults -> checkResults + let tryGetTypeCheckResult (parseResults: FSharpParseFileResults, checkResults: FSharpCheckFileAnswer) = + if not parseResults.ParseHadErrors then + match checkResults with + | FSharpCheckFileAnswer.Aborted -> None + | FSharpCheckFileAnswer.Succeeded checkResults -> Some checkResults + else None + let getSignature parseAndCheckResults = - match (getTypeCheckResult parseAndCheckResults).GenerateSignature() with - | Some s -> s.ToString() - | None -> "" + parseAndCheckResults + |> tryGetTypeCheckResult + |> Option.bind (fun r -> r.GenerateSignature()) + |> Option.map (fun s -> s.ToString()) + |> Option.defaultValue "" let expectOk parseAndCheckResults _ = let checkResult = getTypeCheckResult parseAndCheckResults @@ -442,14 +607,15 @@ module ProjectOperations = failwith $"Expected 1 warning with substring '{warningSubString}' but got %A{w}" let expectErrors parseAndCheckResults _ = - let checkResult = getTypeCheckResult parseAndCheckResults - - if - (checkResult.Diagnostics - |> Array.where (fun d -> d.Severity = FSharpDiagnosticSeverity.Error)) - .Length = 0 - then - failwith "Expected errors, but there were none" + let (parseResult: FSharpParseFileResults), _checkResult = parseAndCheckResults + if not parseResult.ParseHadErrors then + let checkResult = getTypeCheckResult parseAndCheckResults + if + (checkResult.Diagnostics + |> Array.where (fun d -> d.Severity = FSharpDiagnosticSeverity.Error)) + .Length = 0 + then + failwith "Expected errors, but there were none" let expectSignatureChanged result (oldSignature: string, newSignature: string) = expectOk result () @@ -745,6 +911,14 @@ type ProjectWorkflowBuilder return project }) + member this.UpdateFile(workflow: Async, chooseFile, processFile) = + async { + let! ctx = workflow + let file = ctx.Project.SourceFiles |> chooseFile + let fileId = file.Id + return! this.UpdateFile(async.Return ctx, fileId, processFile) + } + [] member this.RegenerateSignature(workflow: Async, fileId: string) = workflow @@ -752,7 +926,7 @@ type ProjectWorkflowBuilder async { use _ = Activity.start "ProjectWorkflowBuilder.RegenerateSignature" [ Activity.Tags.project, project.Name; "fileId", fileId ] - let project, file = project.FindInAllProjects fileId + let project, file = project.FindInAllProjects fileId let! result = checkFile fileId project checker let signature = getSignature result let signatureFileName = getSignatureFilePath project file @@ -1003,6 +1177,45 @@ type SyntheticProject with this.Workflow.Yield() |> Async.RunSynchronously |> ignore this.Workflow.Checker - -let replicateRealProject _projectFilePath = - () \ No newline at end of file + static member CreateFromRealProject(projectDir) = + + let projectFile = + projectDir + |> Directory.GetFiles + |> Seq.filter (fun f -> f.EndsWith ".fsproj") + |> Seq.toList + |> function + | [] -> failwith $"No .fsproj file found in {projectDir}" + | [ x ] -> x + | files -> failwith $"Multiple .fsproj files found in {projectDir}: {files}" + + let fsproj = XmlDocument() + do fsproj.Load projectFile + + let sourceFiles = + [| for node in fsproj.DocumentElement.SelectNodes("//Compile") -> + projectDir ++ node.Attributes["Include"].InnerText |] + + let parseReferences refType = + [ for node in fsproj.DocumentElement.SelectNodes($"//{refType}") do + node.Attributes["Include"].InnerText, + node.Attributes["Version"] |> Option.ofObj |> Option.map (fun x -> x.InnerText) ] + + let name = Path.GetFileNameWithoutExtension projectFile + + let nowarns = + [ for node in fsproj.DocumentElement.SelectNodes("//NoWarn") do + yield! node.InnerText.Split(';') ] + + { SyntheticProject.Create( + name, + [| for f in sourceFiles do + { sourceFile (Path.GetFileNameWithoutExtension f) [] with + Source = File.ReadAllText f } |] + ) with + AutoAddModules = false + NugetReferences = parseReferences "PackageReference" + FrameworkReferences = parseReferences "FrameworkReference" + OtherOptions = + [ for w in nowarns do + $"--nowarn:{w}" ] } diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs index b555ac27508..2cf37586fc6 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs @@ -333,3 +333,96 @@ type TransparentCompilerBenchmark() = [] member this.Cleanup() = benchmark.DeleteProjectDir() + + +[] +[] +[] +[] +type TransparentCompilerGiraffeBenchmark() = + + let mutable benchmark : ProjectWorkflowBuilder = Unchecked.defaultof<_> + + let rng = System.Random() + + let modify (sourceFile: SyntheticSourceFile) = + { sourceFile with Source = $"{sourceFile.Source}\n// {rng.NextDouble()}" } + + let break' (sourceFile: SyntheticSourceFile) = + { sourceFile with Source = $"/{sourceFile.Source}\n// {rng.NextDouble()}" } + + let fix (sourceFile: SyntheticSourceFile) = + { sourceFile with Source = sourceFile.Source.Substring(1) } + + [] + member val UseTransparentCompiler = true with get,set + + member this.Project = + let project = SyntheticProject.CreateFromRealProject (__SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ ".." ++ "Giraffe/src/Giraffe") + { project with OtherOptions = "--nowarn:FS3520"::project.OtherOptions } + + [] + member this.Setup() = + benchmark <- + ProjectWorkflowBuilder( + this.Project, + useGetSource = true, + useChangeNotifications = true, + useTransparentCompiler = this.UseTransparentCompiler, + runTimeout = 15_000).CreateBenchmarkBuilder() + + [] + member this.ChangeFirstCheckLast() = + + use _ = Activity.start "Benchmark" [ + "UseTransparentCompiler", this.UseTransparentCompiler.ToString() + ] + + benchmark { + updateFile this.Project.SourceFiles.Head.Id modify + checkFile (this.Project.SourceFiles |> List.last).Id expectOk + } + + [] + member this.ChangeSecondCheckLast() = + + use _ = Activity.start "Benchmark" [ + "UseTransparentCompiler", this.UseTransparentCompiler.ToString() + ] + + benchmark { + updateFile this.Project.SourceFiles[1].Id modify + checkFile (this.Project.SourceFiles |> List.last).Id expectOk + } + + [] + member this.SomeWorkflow() = + + use _ = Activity.start "Benchmark" [ + "UseTransparentCompiler", this.UseTransparentCompiler.ToString() + ] + + benchmark { + updateFile "Json" modify + checkFile "Json" expectOk + checkFile "ModelValidation" expectOk + updateFile "ModelValidation" modify + checkFile "ModelValidation" expectOk + updateFile "Xml" modify + checkFile "Xml" expectOk + updateFile "ModelValidation" modify + checkFile "ModelValidation" expectOk + + updateFile "Core" break' + checkFile "Core" expectErrors + checkFile "Routing" expectErrors + updateFile "Routing" modify + checkFile "Streaming" expectErrors + checkFile "EndpointRouting" expectErrors + + updateFile "Core" fix + checkFile "Core" expectOk + checkFile "Routing" expectOk + checkFile "Streaming" expectOk + checkFile "EndpointRouting" expectOk + } \ No newline at end of file From 4964043bb1dc8675b21f27fa7d39c12ca4cf76e6 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 20 Jun 2023 10:57:31 +0200 Subject: [PATCH 053/222] wip --- src/Compiler/Facilities/AsyncMemoize.fs | 6 ++- src/Compiler/Service/TransparentCompiler.fs | 2 +- .../BackgroundCompilerBenchmarks.fs | 2 +- .../LanguageService/WorkspaceExtensions.fs | 47 +++++++++++++++---- 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 2b56024fbe0..4f4a4299ce9 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -28,7 +28,9 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (stri let tok = obj () let cache = - MruCache<_, 'TKey, Job<'TValue>>(keepStrongly = 30, areSame = (fun (x, y) -> x = y)) + MruCache<_, 'TKey, Job<'TValue>>(keepStrongly = 30, keepMax = 200, + areSame = (fun (x, y) -> x = y), + requiredToKeep = function Running _ -> true | _ -> false) let requestCounts = Dictionary<'TKey, int>() @@ -113,7 +115,7 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (stri requestCounts.Remove key |> ignore log (Finished, key) - | JobCompleted _, _ -> failwith "If this happens there's a bug" + | JobCompleted _result, Some (_job) -> failwith "If this happens there's a bug" with | :? OperationCanceledException as e -> System.Diagnostics.Trace.TraceError($"AsyncMemoize OperationCanceledException: {e.Message}") diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 65433cfcd6a..15a9c53762b 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -504,7 +504,7 @@ type internal TransparentCompiler use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) - // TODO: might need to put something like this somewhere + // TODO: might need to put something like this somewhere, probably FSharpProjectSnapshot? //// Get the names and time stamps of all the non-framework referenced assemblies, which will act //// as inputs to one of the nodes in the build. //// diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs index 2cf37586fc6..08ce46d40d1 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs @@ -369,7 +369,7 @@ type TransparentCompilerGiraffeBenchmark() = useGetSource = true, useChangeNotifications = true, useTransparentCompiler = this.UseTransparentCompiler, - runTimeout = 15_000).CreateBenchmarkBuilder() + runTimeout = 15_000).CreateBenchmarkBuilder() [] member this.ChangeFirstCheckLast() = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 2c015d68e11..2adac731d73 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -9,6 +9,8 @@ open Microsoft.CodeAnalysis open FSharp.Compiler open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols +open System.Diagnostics +open Microsoft.VisualStudio.FSharp.Editor [] module private CheckerExtensions = @@ -25,20 +27,45 @@ module private CheckerExtensions = let getFileSnapshot (options: FSharpProjectOptions) path = async { - let project = projects[options.ProjectFileName] - let document = project[path] - let! version = document.GetTextVersionAsync() |> Async.AwaitTask - - let getSource () = - task { - let! sourceText = document.GetTextAsync() - return sourceText.ToFSharpSourceText() - } + let project = + projects.TryFind options.ProjectFileName + + if project.IsNone then + Trace.TraceError("Could not find project {0} in solution {1}", options.ProjectFileName, solution.FilePath) + + let documentOpt = + project + |> Option.bind (Map.tryFind path) + + let! version, getSource = + match documentOpt with + | Some document -> + async { + + let! version = document.GetTextVersionAsync() |> Async.AwaitTask + + let getSource () = + task { + let! sourceText = document.GetTextAsync() + return sourceText.ToFSharpSourceText() + } + return version.ToString(), getSource + + } + | None -> + Trace.TraceError("Could not find document {0} in project {1}", path, options.ProjectFileName) + + // Fall back to file system, although this is already suspicious + let version = System.IO.File.GetLastWriteTimeUtc(path) + let getSource () = + task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceText.ofString } + + async.Return (version.ToString(), getSource) return { FileName = path - Version = version.ToString() + Version = version GetSource = getSource } } From 3a3e91decab12f013962f2a70387d69fa6f31047 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 20 Jun 2023 11:56:00 +0200 Subject: [PATCH 054/222] f --- src/Compiler/Facilities/AsyncMemoize.fs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 4f4a4299ce9..2ab3f99f9f6 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -28,9 +28,15 @@ type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (stri let tok = obj () let cache = - MruCache<_, 'TKey, Job<'TValue>>(keepStrongly = 30, keepMax = 200, + MruCache<_, 'TKey, Job<'TValue>>( + keepStrongly = 30, + keepMax = 200, areSame = (fun (x, y) -> x = y), - requiredToKeep = function Running _ -> true | _ -> false) + requiredToKeep = + function + | Running _ -> true + | _ -> false + ) let requestCounts = Dictionary<'TKey, int>() From 037e053702102edb867e0fbe85bd8ad05d6634d5 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 20 Jun 2023 12:20:51 +0200 Subject: [PATCH 055/222] f --- .../LanguageService/WorkspaceExtensions.fs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 2adac731d73..d45d70b40c4 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -27,17 +27,14 @@ module private CheckerExtensions = let getFileSnapshot (options: FSharpProjectOptions) path = async { - let project = - projects.TryFind options.ProjectFileName + let project = projects.TryFind options.ProjectFileName if project.IsNone then Trace.TraceError("Could not find project {0} in solution {1}", options.ProjectFileName, solution.FilePath) - let documentOpt = - project - |> Option.bind (Map.tryFind path) + let documentOpt = project |> Option.bind (Map.tryFind path) - let! version, getSource = + let! version, getSource = match documentOpt with | Some document -> async { @@ -49,6 +46,7 @@ module private CheckerExtensions = let! sourceText = document.GetTextAsync() return sourceText.ToFSharpSourceText() } + return version.ToString(), getSource } @@ -57,10 +55,11 @@ module private CheckerExtensions = // Fall back to file system, although this is already suspicious let version = System.IO.File.GetLastWriteTimeUtc(path) + let getSource () = task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceText.ofString } - async.Return (version.ToString(), getSource) + async.Return(version.ToString(), getSource) return { From eba5dde40c8d8acea0ba5bd254271a06c47dc5f5 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 20 Jun 2023 18:15:47 +0200 Subject: [PATCH 056/222] wip --- .../FSharpChecker/CommonWorkflows.fs | 2 +- .../FSharpChecker/TransparentCompiler.fs | 155 +++++++++++++++++- .../ProjectGeneration.fs | 38 +++-- .../BackgroundCompilerBenchmarks.fs | 37 ++++- 4 files changed, 214 insertions(+), 18 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs index 36d37cfc209..551fa15eb8a 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs @@ -68,7 +68,7 @@ let ``Adding a file`` () = addFileAbove "Second" (sourceFile "New" []) updateFile "Second" (addDependency "New") saveAll - checkFile "Last" (expectSignatureContains "val f: x: 'a -> (ModuleNew.TNewV_1<'a> * ModuleFirst.TFirstV_1<'a> * ModuleSecond.TSecondV_1<'a>) * (ModuleFirst.TFirstV_1<'a> * ModuleThird.TThirdV_1<'a>) * TLastV_1<'a>") + checkFile "Last" (expectSignatureContains "val f: x: 'a -> (ModuleFirst.TFirstV_1<'a> * ModuleNew.TNewV_1<'a> * ModuleSecond.TSecondV_1<'a>) * (ModuleFirst.TFirstV_1<'a> * ModuleThird.TThirdV_1<'a>) * TLastV_1<'a>") } [] diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index d064308ebde..e5170392675 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -155,7 +155,7 @@ let ``Adding a file`` () = testWorkflow() { addFileAbove "Second" (sourceFile "New" []) updateFile "Second" (addDependency "New") - checkFile "Last" (expectSignatureContains "val f: x: 'a -> (ModuleNew.TNewV_1<'a> * ModuleFirst.TFirstV_1<'a> * ModuleSecond.TSecondV_1<'a>) * (ModuleFirst.TFirstV_1<'a> * ModuleThird.TThirdV_1<'a>) * TLastV_1<'a>") + checkFile "Last" (expectSignatureContains "val f: x: 'a -> (ModuleFirst.TFirstV_1<'a> * ModuleNew.TNewV_1<'a> * ModuleSecond.TSecondV_1<'a>) * (ModuleFirst.TFirstV_1<'a> * ModuleThird.TThirdV_1<'a>) * TLastV_1<'a>") } [] @@ -305,3 +305,156 @@ let ``Files that are not depended on don't invalidate cache part 2`` () = Assert.Equal([Started; Finished], graphConstructions["FileE.fs"]) Assert.Equal([], intermediateTypeChecks |> Map.toList) + + +type Operation = Update | Check | Add | Remove + +//[] +//[] +//[] +let Fuzzing signatureFiles = + let seed = System.Random().Next() + let rng = System.Random(int seed) + + let fileCount = 50 + let maxDepsPerFile = 3 + let threads = 4 + let operationCount = 50 + + let fileName i = sprintf $"F%03d{i}" + + let files = + [| for i in 1 .. fileCount do + let name = fileName i + let deps = [ + for _ in 1 .. maxDepsPerFile do + if i > 1 then + fileName <| rng.Next(1, i) ] + let signature = if signatureFiles then AutoGenerated else No + + { sourceFile name deps with SignatureFile = signature } + |] + + let project = SyntheticProject.Create(files) + + let builder = ProjectWorkflowBuilder(project, useTransparentCompiler = true, autoStart = false) + + let operationProbabilities = [ + Update, 20 + Check, 20 + // Add, 2 + // Remove, 1 + ] + + let operationPicker = [| + for op, prob in operationProbabilities do + for _ in 1 .. prob do + op + |] + + let getRandomOperation () = operationPicker[rng.Next(0, operationPicker.Length)] + + let getRandomFile (project: SyntheticProject) = project.SourceFiles[rng.Next(0, project.SourceFiles.Length)].Id + + let rec fuzzer n actx = async { + if n >= operationCount then + return! actx + else + let! ctx = actx + let project = ctx.Project + return! fuzzer (n + 1) <| + match getRandomOperation () with + | Update -> + let file = getRandomFile project + builder.UpdateFile(actx, file, updatePublicSurface) + | Check -> + let file = getRandomFile project + builder.CheckFile(actx, file, expectOk) // TODO: add timeout/cancelation + | Add -> + let file = getRandomFile project + let newFile = sourceFile (fileName <| project.SourceFiles.Length + 1) [] + builder.AddFileAbove(actx, file, newFile) + | Remove -> + let file = getRandomFile project + builder.RemoveFile(actx, file) + } + + let initialCtx = builder.Yield() + + let results = + async { + let! ctx = initialCtx // Save and initial check project + return! + [1..threads] + |> Seq.map (fun _ -> fuzzer 0 (async.Return ctx)) + |> Async.Parallel + } + |> Async.RunSynchronously + + ignore results + + +[] +let GiraffeTest () = + + let project = SyntheticProject.CreateFromRealProject (__SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ "Giraffe-signatures/src/Giraffe") + let project = { project with OtherOptions = "--nowarn:FS3520"::project.OtherOptions } + + let rng = System.Random() + + let addComment s = $"{s}\n// {rng.NextDouble().ToString()}" + let prependSlash s = $"/{s}\n// {rng.NextDouble()}" + + let modify (sourceFile: SyntheticSourceFile) = + { sourceFile with + Source = addComment sourceFile.Source + SignatureFile = + match sourceFile.SignatureFile with + | Custom signature -> Custom (addComment signature) + | x -> x + } + + let break' (sourceFile: SyntheticSourceFile) = + { sourceFile with + Source = prependSlash sourceFile.Source + SignatureFile = + match sourceFile.SignatureFile with + | Custom signature -> Custom (prependSlash signature) + | x -> x + } + + let fix (sourceFile: SyntheticSourceFile) = + { sourceFile with + Source = sourceFile.Source.Substring 1 + SignatureFile = + match sourceFile.SignatureFile with + | Custom signature -> Custom (signature.Substring 1) + | x -> x + } + + ProjectWorkflowBuilder(project, useTransparentCompiler = false) { + updateFile "Json" modify + checkFile "Json" expectOk + checkFile "ModelValidation" expectOk + updateFile "ModelValidation" modify + checkFile "ModelValidation" expectOk + updateFile "Xml" modify + checkFile "Xml" expectOk + updateFile "ModelValidation" modify + checkFile "ModelValidation" expectOk + + updateFile "Core" break' + saveAll + checkFile "Core" expectErrors + checkFile "Routing" expectErrors + updateFile "Routing" modify + checkFile "Streaming" expectErrors + checkFile "EndpointRouting" expectErrors + + updateFile "Core" fix + saveAll + checkFile "Core" expectOk + checkFile "Routing" expectOk + checkFile "Streaming" expectOk + checkFile "EndpointRouting" expectOk + } \ No newline at end of file diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index f5351e383c2..626e25d9752 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -374,14 +374,14 @@ let renderSourceFile (project: SyntheticProject) (f: SyntheticSourceFile) = f.Source else renderNamespaceModule project f - for p in project.DependsOn do + for p in project.DependsOn |> set do $"open {p.Name}" $"type {f.TypeName}<'a> = T{f.Id} of 'a" $"let {f.FunctionName} x =" - for dep in f.DependsOn do + for dep in f.DependsOn |> set do $" Module{dep}.{defaultFunctionName} x," $" T{f.Id} x" @@ -404,7 +404,11 @@ let renderSourceFile (project: SyntheticProject) (f: SyntheticSourceFile) = let renderCustomSignatureFile (project: SyntheticProject) (f: SyntheticSourceFile) = match f.SignatureFile with - | Custom signature -> $"{renderNamespaceModule project f}\n{signature}" + | Custom signature -> + if project.AutoAddModules then + $"{renderNamespaceModule project f}\n{signature}" + else + signature | _ -> failwith $"File {f.FileName} does not have a custom signature file." let private renderFsProj (p: SyntheticProject) = @@ -783,12 +787,14 @@ type ProjectWorkflowBuilder ?useChangeNotifications, ?useSyntaxTreeCache, ?useTransparentCompiler, - ?runTimeout + ?runTimeout, + ?autoStart ) = let useTransparentCompiler = defaultArg useTransparentCompiler false let useGetSource = not useTransparentCompiler && defaultArg useGetSource false let useChangeNotifications = not useTransparentCompiler && defaultArg useChangeNotifications false + let autoStart = defaultArg autoStart true let mutable latestProject = initialProject let mutable activity = None @@ -857,9 +863,9 @@ type ProjectWorkflowBuilder if Directory.Exists initialProject.ProjectDir then Directory.Delete(initialProject.ProjectDir, true) - member this.Run(workflow: Async) = + member this.Execute(workflow: Async) = try - Async.RunSynchronously(workflow, timeout = defaultArg runTimeout 60_000) + Async.RunSynchronously(workflow, timeout = defaultArg runTimeout 600_000) finally if initialContext.IsNone then this.DeleteProjectDir() @@ -868,6 +874,12 @@ type ProjectWorkflowBuilder x.ForceFlush() |> ignore x.Dispose()) + member this.Run(workflow: Async) = + if autoStart then + this.Execute(workflow) |> async.Return + else + workflow + [] member this.WithProject(workflow: Async, f) = workflow |> mapProjectAsync (fun project -> @@ -907,6 +919,8 @@ type ProjectWorkflowBuilder let project, file = project.FindInAllProjects fileId let filePath = project.ProjectDir ++ file.FileName do! checker.NotifyFileChanged(filePath, project.GetProjectOptions checker) + if (project.Find fileId).SignatureFile <> No then + do! checker.NotifyFileChanged($"{filePath}i", project.GetProjectOptions checker) return project }) @@ -1192,9 +1206,11 @@ type SyntheticProject with let fsproj = XmlDocument() do fsproj.Load projectFile - let sourceFiles = - [| for node in fsproj.DocumentElement.SelectNodes("//Compile") -> - projectDir ++ node.Attributes["Include"].InnerText |] + let signatureFiles, sourceFiles = + [ for node in fsproj.DocumentElement.SelectNodes("//Compile") -> + projectDir ++ node.Attributes["Include"].InnerText ] + |> List.partition (fun path -> path.EndsWith ".fsi") + let signatureFiles = set signatureFiles let parseReferences refType = [ for node in fsproj.DocumentElement.SelectNodes($"//{refType}") do @@ -1211,7 +1227,9 @@ type SyntheticProject with name, [| for f in sourceFiles do { sourceFile (Path.GetFileNameWithoutExtension f) [] with - Source = File.ReadAllText f } |] + Source = File.ReadAllText f + SignatureFile = if signatureFiles.Contains $"{f}i" then Custom (File.ReadAllText $"{f}i") else No + } |] ) with AutoAddModules = false NugetReferences = parseReferences "PackageReference" diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs index 08ce46d40d1..32fc2b0a8c9 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs @@ -346,19 +346,42 @@ type TransparentCompilerGiraffeBenchmark() = let rng = System.Random() let modify (sourceFile: SyntheticSourceFile) = - { sourceFile with Source = $"{sourceFile.Source}\n// {rng.NextDouble()}" } + { sourceFile with + Source = $"{sourceFile.Source}\n// {rng.NextDouble()}" + SignatureFile = + match sourceFile.SignatureFile with + | Custom signature -> Custom $"{signature}\n// {rng.NextDouble().ToString()}" + | x -> x + } let break' (sourceFile: SyntheticSourceFile) = - { sourceFile with Source = $"/{sourceFile.Source}\n// {rng.NextDouble()}" } + { sourceFile with + Source = $"/{sourceFile.Source}\n// {rng.NextDouble()}" + SignatureFile = + match sourceFile.SignatureFile with + | Custom signature -> Custom $"/{signature}\n// {rng.NextDouble().ToString()}" + | x -> x + } let fix (sourceFile: SyntheticSourceFile) = - { sourceFile with Source = sourceFile.Source.Substring(1) } + { sourceFile with + Source = sourceFile.Source.Substring 1 + SignatureFile = + match sourceFile.SignatureFile with + | Custom signature -> Custom (signature.Substring 1) + | x -> x + } [] member val UseTransparentCompiler = true with get,set + [] + member val SignatureFiles = true with get,set + member this.Project = - let project = SyntheticProject.CreateFromRealProject (__SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ ".." ++ "Giraffe/src/Giraffe") + let projectDir = if this.SignatureFiles then "Giraffe-signatures" else "Giraffe" + + let project = SyntheticProject.CreateFromRealProject (__SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ ".." ++ projectDir ++ "src/Giraffe") { project with OtherOptions = "--nowarn:FS3520"::project.OtherOptions } [] @@ -371,7 +394,7 @@ type TransparentCompilerGiraffeBenchmark() = useTransparentCompiler = this.UseTransparentCompiler, runTimeout = 15_000).CreateBenchmarkBuilder() - [] + //[] member this.ChangeFirstCheckLast() = use _ = Activity.start "Benchmark" [ @@ -383,7 +406,7 @@ type TransparentCompilerGiraffeBenchmark() = checkFile (this.Project.SourceFiles |> List.last).Id expectOk } - [] + //[] member this.ChangeSecondCheckLast() = use _ = Activity.start "Benchmark" [ @@ -414,6 +437,7 @@ type TransparentCompilerGiraffeBenchmark() = checkFile "ModelValidation" expectOk updateFile "Core" break' + saveAll checkFile "Core" expectErrors checkFile "Routing" expectErrors updateFile "Routing" modify @@ -421,6 +445,7 @@ type TransparentCompilerGiraffeBenchmark() = checkFile "EndpointRouting" expectErrors updateFile "Core" fix + saveAll checkFile "Core" expectOk checkFile "Routing" expectOk checkFile "Streaming" expectOk From dea9f0ebe12abc62f82d06cde1ff17b05c1ec644 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 20 Jun 2023 19:49:54 +0200 Subject: [PATCH 057/222] CancellableTask updates --- .../FSharp.Editor/Common/CancellableTasks.fs | 1 - .../LanguageService/SymbolHelpers.fs | 72 +++-- .../LanguageService/WorkspaceExtensions.fs | 62 ++-- .../Navigation/FindUsagesService.fs | 266 ++++++++++-------- 4 files changed, 233 insertions(+), 168 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs index 6ff78a04d09..7c55cfd2e25 100644 --- a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs +++ b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs @@ -895,7 +895,6 @@ module CancellableTasks = /// A CancellableTask with the item as the result. let inline singleton (item: 'item) : CancellableTask<'item> = fun _ -> Task.FromResult(item) - /// Allows chaining of CancellableTasks. /// The continuation. /// The value. diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index f84c2bec552..46fc7669b24 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -15,6 +15,7 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols open FSharp.Compiler.Text open Microsoft.VisualStudio.FSharp.Editor.Telemetry +open CancellableTasks module internal SymbolHelpers = /// Used for local code fixes in a document, e.g. to rename local parameters @@ -60,15 +61,9 @@ module internal SymbolHelpers = return symbolUses } - let getSymbolUsesInProjects - ( - symbol: FSharpSymbol, - projects: Project list, - onFound: Document -> range -> Async, - ct: CancellationToken - ) = + let getSymbolUsesInProjects (symbol: FSharpSymbol, projects: Project list, onFound: Document -> range -> CancellableTask) = match projects with - | [] -> Task.CompletedTask + | [] -> CancellableTask.singleton () | firstProject :: _ -> let isFastFindReferencesEnabled = firstProject.IsFastFindReferencesEnabled @@ -76,28 +71,46 @@ module internal SymbolHelpers = let props = [| nameof isFastFindReferencesEnabled, isFastFindReferencesEnabled :> obj |] - backgroundTask { + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () // TODO: this needs to be a single event with a duration TelemetryReporter.ReportSingleEvent(TelemetryEvents.GetSymbolUsesInProjectsStarted, props) - do! - projects - |> Seq.map (fun project -> - Task.Run(fun () -> project.FindFSharpReferencesAsync(symbol, onFound, "getSymbolUsesInProjects", ct))) - |> Task.WhenAll + let tasks = + [| + for project in projects do + yield + CancellableTask.startAsTask + cancellationToken + (project.FindFSharpReferencesAsync(symbol, onFound, "getSymbolUsesInProjects")) + |] + + do! Task.WhenAll(tasks) TelemetryReporter.ReportSingleEvent(TelemetryEvents.GetSymbolUsesInProjectsFinished, props) } - let findSymbolUses (symbolUse: FSharpSymbolUse) (currentDocument: Document) (checkFileResults: FSharpCheckFileResults) onFound = - async { + let findSymbolUses + (symbolUse: FSharpSymbolUse) + (currentDocument: Document) + (checkFileResults: FSharpCheckFileResults) + (onFound: Document -> range -> CancellableTask) + = + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + match symbolUse.GetSymbolScope currentDocument with | Some SymbolScope.CurrentDocument -> let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) - for symbolUse in symbolUses do - do! onFound currentDocument symbolUse.Range + let tasks = + [| + for symbolUse in symbolUses do + yield CancellableTask.startAsTask cancellationToken (onFound currentDocument symbolUse.Range) + |] + + do! Task.WhenAll(tasks) | Some SymbolScope.SignatureAndImplementation -> let otherFile = getOtherFile currentDocument.FilePath @@ -105,11 +118,11 @@ module internal SymbolHelpers = let! otherFileCheckResults = match currentDocument.Project.Solution.TryGetDocumentFromPath otherFile with | Some doc -> - async { + cancellableTask { let! _, checkFileResults = doc.GetFSharpParseAndCheckResultsAsync("findReferencedSymbolsAsync") return [ checkFileResults, doc ] } - | None -> async.Return [] + | None -> CancellableTask.singleton [] let symbolUses = (checkFileResults, currentDocument) :: otherFileCheckResults @@ -117,8 +130,13 @@ module internal SymbolHelpers = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) |> Seq.map (fun symbolUse -> (doc, symbolUse.Range))) - for document, range in symbolUses do - do! onFound document range + let tasks = + [| + for document, range in symbolUses do + yield CancellableTask.startAsTask cancellationToken (onFound document range) + |] + + do! Task.WhenAll tasks | scope -> let projectsToCheck = @@ -135,17 +153,15 @@ module internal SymbolHelpers = // In order to find all its usages we have to check all F# projects. | _ -> Seq.toList currentDocument.Project.Solution.Projects - let! ct = Async.CancellationToken - - do! - getSymbolUsesInProjects (symbolUse.Symbol, projectsToCheck, onFound, ct) - |> Async.AwaitTask + do! getSymbolUsesInProjects (symbolUse.Symbol, projectsToCheck, onFound) } let getSymbolUses (symbolUse: FSharpSymbolUse) (currentDocument: Document) (checkFileResults: FSharpCheckFileResults) = async { let symbolUses = ConcurrentBag() - let onFound = fun document range -> async { symbolUses.Add(document, range) } + + let onFound = + fun document range -> cancellableTask { symbolUses.Add(document, range) } do! findSymbolUses symbolUse currentDocument checkFileResults onFound diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index d45d70b40c4..e7db686d38c 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -2,15 +2,19 @@ module internal Microsoft.VisualStudio.FSharp.Editor.WorkspaceExtensions open System +open System.Diagnostics open System.Runtime.CompilerServices open System.Threading open System.Threading.Tasks + open Microsoft.CodeAnalysis +open Microsoft.VisualStudio.FSharp.Editor + open FSharp.Compiler open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols -open System.Diagnostics -open Microsoft.VisualStudio.FSharp.Editor + +open CancellableTasks [] module private CheckerExtensions = @@ -325,8 +329,9 @@ type Document with } /// Find F# references in the given F# document. - member this.FindFSharpReferencesAsync(symbol, onFound, userOpName) = - async { + member inline this.FindFSharpReferencesAsync(symbol, [] onFound, userOpName) = + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync(userOpName) let! symbolUses = @@ -345,8 +350,13 @@ type Document with fastCheck = this.Project.IsFastFindReferencesEnabled ) - for symbolUse in symbolUses do - do! onFound symbolUse + let tasks = + [| + for symbolUse in symbolUses do + yield CancellableTask.startAsTask cancellationToken (onFound symbolUse) + |] + + do! Task.WhenAll(tasks) } /// Try to find a F# lexer/token symbol of the given F# document and position. @@ -374,8 +384,8 @@ type Document with type Project with /// Find F# references in the given project. - member this.FindFSharpReferencesAsync(symbol: FSharpSymbol, onFound, userOpName, ct) : Task = - backgroundTask { + member inline this.FindFSharpReferencesAsync(symbol: FSharpSymbol, [] onFound, userOpName) : CancellableTask = + cancellableTask { let declarationLocation = symbol.SignatureLocation @@ -385,45 +395,53 @@ type Project with let declarationDocument = declarationLocation |> Option.bind this.Solution.TryGetDocumentFromFSharpRange + // Can we skip documents, which are above current, since they can't contain symbols from current one. let! canSkipDocuments = match declarationDocument with | Some document when this.IsFastFindReferencesEnabled && document.Project = this -> - backgroundTask { + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + let! _, _, _, options = document.GetFSharpCompilationOptionsAsync(userOpName) - |> RoslynHelpers.StartAsyncAsTask ct + |> RoslynHelpers.StartAsyncAsTask cancellationToken let signatureFile = if not (document.FilePath |> isSignatureFile) then - $"{document.FilePath}i" + document.FilePath + "i" else null return + options.SourceFiles |> Seq.takeWhile ((<>) document.FilePath) |> Seq.filter ((<>) signatureFile) |> Set } - | _ -> Task.FromResult Set.empty + | _ -> CancellableTask.singleton Set.empty let documents = this.Documents |> Seq.filter (fun document -> not (canSkipDocuments.Contains document.FilePath)) if this.IsFastFindReferencesEnabled then - do! - documents - |> Seq.map (fun doc -> - Task.Run(fun () -> - doc.FindFSharpReferencesAsync(symbol, (fun range -> onFound doc range), userOpName) - |> RoslynHelpers.StartAsyncUnitAsTask ct)) - |> Task.WhenAll + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + + let tasks = + [| + for doc in documents do + yield + cancellableTask { + return! doc.FindFSharpReferencesAsync(symbol, (fun range -> onFound doc range), userOpName) + } + |> CancellableTask.startAsTask cancellationToken + |] + + do! Task.WhenAll tasks else for doc in documents do - do! - doc.FindFSharpReferencesAsync(symbol, (fun range -> onFound doc range), userOpName) - |> RoslynHelpers.StartAsyncAsTask ct + do! doc.FindFSharpReferencesAsync(symbol, (fun range -> onFound doc range), userOpName) } member this.GetFSharpCompilationOptionsAsync(ct: CancellationToken) = diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index 5ab7ba417b0..c6d20f24c5f 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -4,150 +4,182 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Collections.Immutable open System.Composition +open System.Threading.Tasks open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.ExternalAccess.FSharp open Microsoft.CodeAnalysis.ExternalAccess.FSharp.FindUsages open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor.FindUsages -open FSharp.Compiler -open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.EditorServices open FSharp.Compiler.Text -open Microsoft.CodeAnalysis.Text - -[)>] -type internal FSharpFindUsagesService [] () = +open CancellableTasks + +module FSharpFindUsagesService = + + let onSymbolFound + allReferences + declarationRange + externalDefinitionItem + definitionItems + isExternal + (onReferenceFoundAsync: FSharpSourceReferenceItem -> Task) + (doc: Document) + (symbolUse: range) + = + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + let! sourceText = doc.GetTextAsync(cancellationToken) + + match declarationRange, RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse) with + | Some declRange, _ when Range.equals declRange symbolUse -> () + | _, None -> () + | _, Some textSpan -> + if allReferences then + let definitionItem = + if isExternal then + externalDefinitionItem + else + definitionItems + |> Array.tryFind (fun (_, projectId) -> doc.Project.Id = projectId) + |> Option.map (fun (definitionItem, _) -> definitionItem) + |> Option.defaultValue externalDefinitionItem + + let referenceItem = + FSharpSourceReferenceItem(definitionItem, FSharpDocumentSpan(doc, textSpan)) + // REVIEW: OnReferenceFoundAsync is throwing inside Roslyn, putting a try/with so find-all refs doesn't fail. + try + do! onReferenceFoundAsync referenceItem + with _ -> + () + } // File can be included in more than one project, hence single `range` may results with multiple `Document`s. let rangeToDocumentSpans (solution: Solution, range: range) = - async { - if range.Start = range.End then - return [] - else - let! spans = - solution.GetDocumentIdsWithFilePath(range.FileName) - |> Seq.map (fun documentId -> - async { - let doc = solution.GetDocument(documentId) - let! cancellationToken = Async.CancellationToken - let! sourceText = doc.GetTextAsync(cancellationToken) |> Async.AwaitTask - - match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) with - | Some span -> - let span = Tokenizer.fixupSpan (sourceText, span) - return Some(FSharpDocumentSpan(doc, span)) - | None -> return None - }) - |> Async.Parallel - - return spans |> Array.choose id |> Array.toList - } + if range.Start = range.End then + CancellableTask.singleton [||] + else + cancellableTask { + let documentIds = solution.GetDocumentIdsWithFilePath(range.FileName) + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + + let tasks = + [| + for documentId in documentIds do + let t = + cancellableTask { + let doc = solution.GetDocument(documentId) + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + let! sourceText = doc.GetTextAsync(cancellationToken) + + match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) with + | Some span -> + let span = Tokenizer.fixupSpan (sourceText, span) + return Some(FSharpDocumentSpan(doc, span)) + | None -> return None + } + + CancellableTask.start cancellationToken t + |] + + let! spans = Task.WhenAll tasks + + return spans |> Array.choose id + } let findReferencedSymbolsAsync ( document: Document, position: int, context: IFSharpFindUsagesContext, - allReferences: bool - ) : Async = - asyncMaybe { - let! sourceText = document.GetTextAsync(context.CancellationToken) |> Async.AwaitTask |> liftAsync + allReferences: bool, + userOp: string + ) : CancellableTask = + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () + let! sourceText = document.GetTextAsync(cancellationToken) let textLine = sourceText.Lines.GetLineFromPosition(position).ToString() let lineNumber = sourceText.Lines.GetLinePosition(position).Line + 1 let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, "findReferencedSymbolsAsync") - let! _, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpFindUsagesService)) - |> liftAsync - - let! symbolUse = - checkFileResults.GetSymbolUseAtLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland) - - let declaration = - checkFileResults.GetDeclarationLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, false) - - let tags = - FSharpGlyphTags.GetTags(Tokenizer.GetGlyphForSymbol(symbolUse.Symbol, symbol.Kind)) - - let declarationRange = - match declaration with - | FindDeclResult.DeclFound range -> Some range - | _ -> None - - let! declarationSpans = - async { - match declarationRange with - | Some range -> return! rangeToDocumentSpans (document.Project.Solution, range) - | None -> return! async.Return [] - } - |> liftAsync - - let isExternal = declarationSpans |> List.isEmpty - - let displayParts = - ImmutableArray.Create(Microsoft.CodeAnalysis.TaggedText(TextTags.Text, symbol.Ident.idText)) - - let originationParts = - ImmutableArray.Create(Microsoft.CodeAnalysis.TaggedText(TextTags.Assembly, symbolUse.Symbol.Assembly.SimpleName)) - - let externalDefinitionItem = - FSharpDefinitionItem.CreateNonNavigableItem(tags, displayParts, originationParts) - - let definitionItems = - declarationSpans - |> List.map (fun span -> FSharpDefinitionItem.Create(tags, displayParts, span), span.Document.Project.Id) - - for definitionItem, _ in definitionItems do - do! context.OnDefinitionFoundAsync(definitionItem) |> Async.AwaitTask |> liftAsync - - if isExternal then - do! - context.OnDefinitionFoundAsync(externalDefinitionItem) - |> Async.AwaitTask - |> liftAsync - - let onFound (doc: Document) (symbolUse: range) = - async { - let! sourceText = doc.GetTextAsync(context.CancellationToken) |> Async.AwaitTask - - match declarationRange, RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse) with - | Some declRange, _ when Range.equals declRange symbolUse -> () - | _, None -> () - | _, Some textSpan -> - if allReferences then - let definitionItem = - if isExternal then - externalDefinitionItem - else - definitionItems - |> List.tryFind (fun (_, projectId) -> doc.Project.Id = projectId) - |> Option.map (fun (definitionItem, _) -> definitionItem) - |> Option.defaultValue externalDefinitionItem - - let referenceItem = - FSharpSourceReferenceItem(definitionItem, FSharpDocumentSpan(doc, textSpan)) - // REVIEW: OnReferenceFoundAsync is throwing inside Roslyn, putting a try/with so find-all refs doesn't fail. - try - do! context.OnReferenceFoundAsync(referenceItem) |> Async.AwaitTask - with _ -> - () - } - - do! - SymbolHelpers.findSymbolUses symbolUse document checkFileResults onFound - |> liftAsync + match symbol with + | None -> () + | Some symbol -> + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOp) + + let symbolUse = + checkFileResults.GetSymbolUseAtLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland) + + let declaration = + checkFileResults.GetDeclarationLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, false) + + match symbolUse with + | None -> () + | Some symbolUse -> + + let tags = + FSharpGlyphTags.GetTags(Tokenizer.GetGlyphForSymbol(symbolUse.Symbol, symbol.Kind)) + + let declarationRange = + match declaration with + | FindDeclResult.DeclFound range -> Some range + | _ -> None + + let! declarationSpans = + match declarationRange with + | Some range -> cancellableTask { return! rangeToDocumentSpans (document.Project.Solution, range) } + | None -> CancellableTask.singleton [||] + + let isExternal = declarationSpans |> Array.isEmpty + + let displayParts = + ImmutableArray.Create(Microsoft.CodeAnalysis.TaggedText(TextTags.Text, symbol.Ident.idText)) + + let originationParts = + ImmutableArray.Create(Microsoft.CodeAnalysis.TaggedText(TextTags.Assembly, symbolUse.Symbol.Assembly.SimpleName)) + + let externalDefinitionItem = + FSharpDefinitionItem.CreateNonNavigableItem(tags, displayParts, originationParts) + + let definitionItems = + declarationSpans + |> Array.map (fun span -> FSharpDefinitionItem.Create(tags, displayParts, span), span.Document.Project.Id) + + let tasks = + [| + for definitionItem, _ in definitionItems do + yield context.OnDefinitionFoundAsync(definitionItem) + |] + + do! Task.WhenAll(tasks) + + if isExternal then + do! context.OnDefinitionFoundAsync(externalDefinitionItem) + + let onFound = + onSymbolFound + allReferences + declarationRange + externalDefinitionItem + definitionItems + isExternal + context.OnReferenceFoundAsync + + do! SymbolHelpers.findSymbolUses symbolUse document checkFileResults onFound } - |> Async.Ignore +open FSharpFindUsagesService + +[)>] +type internal FSharpFindUsagesService [] () = interface IFSharpFindUsagesService with member _.FindReferencesAsync(document, position, context) = - findReferencedSymbolsAsync (document, position, context, true) - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + findReferencedSymbolsAsync (document, position, context, true, nameof (FSharpFindUsagesService)) + |> CancellableTask.startAsTask context.CancellationToken member _.FindImplementationsAsync(document, position, context) = - findReferencedSymbolsAsync (document, position, context, false) - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + findReferencedSymbolsAsync (document, position, context, false, nameof (FSharpFindUsagesService)) + |> CancellableTask.startAsTask context.CancellationToken From 63c83cd89cd7cddb084ec5bb9386dd881a5c3bd2 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 21 Jun 2023 10:58:10 +0200 Subject: [PATCH 058/222] wip --- .../FSharpChecker/TransparentCompiler.fs | 68 +------------------ .../BackgroundCompilerBenchmarks.fs | 33 +++------ 2 files changed, 10 insertions(+), 91 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index e5170392675..216798d656b 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -391,70 +391,4 @@ let Fuzzing signatureFiles = } |> Async.RunSynchronously - ignore results - - -[] -let GiraffeTest () = - - let project = SyntheticProject.CreateFromRealProject (__SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ "Giraffe-signatures/src/Giraffe") - let project = { project with OtherOptions = "--nowarn:FS3520"::project.OtherOptions } - - let rng = System.Random() - - let addComment s = $"{s}\n// {rng.NextDouble().ToString()}" - let prependSlash s = $"/{s}\n// {rng.NextDouble()}" - - let modify (sourceFile: SyntheticSourceFile) = - { sourceFile with - Source = addComment sourceFile.Source - SignatureFile = - match sourceFile.SignatureFile with - | Custom signature -> Custom (addComment signature) - | x -> x - } - - let break' (sourceFile: SyntheticSourceFile) = - { sourceFile with - Source = prependSlash sourceFile.Source - SignatureFile = - match sourceFile.SignatureFile with - | Custom signature -> Custom (prependSlash signature) - | x -> x - } - - let fix (sourceFile: SyntheticSourceFile) = - { sourceFile with - Source = sourceFile.Source.Substring 1 - SignatureFile = - match sourceFile.SignatureFile with - | Custom signature -> Custom (signature.Substring 1) - | x -> x - } - - ProjectWorkflowBuilder(project, useTransparentCompiler = false) { - updateFile "Json" modify - checkFile "Json" expectOk - checkFile "ModelValidation" expectOk - updateFile "ModelValidation" modify - checkFile "ModelValidation" expectOk - updateFile "Xml" modify - checkFile "Xml" expectOk - updateFile "ModelValidation" modify - checkFile "ModelValidation" expectOk - - updateFile "Core" break' - saveAll - checkFile "Core" expectErrors - checkFile "Routing" expectErrors - updateFile "Routing" modify - checkFile "Streaming" expectErrors - checkFile "EndpointRouting" expectErrors - - updateFile "Core" fix - saveAll - checkFile "Core" expectOk - checkFile "Routing" expectOk - checkFile "Streaming" expectOk - checkFile "EndpointRouting" expectOk - } \ No newline at end of file + ignore results \ No newline at end of file diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs index 32fc2b0a8c9..0de205fa7c3 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs @@ -345,32 +345,17 @@ type TransparentCompilerGiraffeBenchmark() = let rng = System.Random() + let addComment s = $"{s}\n// {rng.NextDouble().ToString()}" + let prependSlash s = $"/{s}\n// {rng.NextDouble()}" + let modify (sourceFile: SyntheticSourceFile) = - { sourceFile with - Source = $"{sourceFile.Source}\n// {rng.NextDouble()}" - SignatureFile = - match sourceFile.SignatureFile with - | Custom signature -> Custom $"{signature}\n// {rng.NextDouble().ToString()}" - | x -> x - } + { sourceFile with Source = addComment sourceFile.Source } let break' (sourceFile: SyntheticSourceFile) = - { sourceFile with - Source = $"/{sourceFile.Source}\n// {rng.NextDouble()}" - SignatureFile = - match sourceFile.SignatureFile with - | Custom signature -> Custom $"/{signature}\n// {rng.NextDouble().ToString()}" - | x -> x - } + { sourceFile with Source = prependSlash sourceFile.Source } let fix (sourceFile: SyntheticSourceFile) = - { sourceFile with - Source = sourceFile.Source.Substring 1 - SignatureFile = - match sourceFile.SignatureFile with - | Custom signature -> Custom (signature.Substring 1) - | x -> x - } + { sourceFile with Source = sourceFile.Source.Substring 1 } [] member val UseTransparentCompiler = true with get,set @@ -439,10 +424,10 @@ type TransparentCompilerGiraffeBenchmark() = updateFile "Core" break' saveAll checkFile "Core" expectErrors - checkFile "Routing" expectErrors + checkFile "Routing" (if this.SignatureFiles then expectOk else expectErrors) updateFile "Routing" modify - checkFile "Streaming" expectErrors - checkFile "EndpointRouting" expectErrors + checkFile "Streaming" (if this.SignatureFiles then expectOk else expectErrors) + checkFile "EndpointRouting" (if this.SignatureFiles then expectOk else expectErrors) updateFile "Core" fix saveAll From 8f7e306a04e9d799d76b5644b770581b9cdf0a96 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 21 Jun 2023 12:19:49 +0200 Subject: [PATCH 059/222] wip --- .../CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs index 0de205fa7c3..aeeb39b752d 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs @@ -422,7 +422,6 @@ type TransparentCompilerGiraffeBenchmark() = checkFile "ModelValidation" expectOk updateFile "Core" break' - saveAll checkFile "Core" expectErrors checkFile "Routing" (if this.SignatureFiles then expectOk else expectErrors) updateFile "Routing" modify @@ -430,7 +429,6 @@ type TransparentCompilerGiraffeBenchmark() = checkFile "EndpointRouting" (if this.SignatureFiles then expectOk else expectErrors) updateFile "Core" fix - saveAll checkFile "Core" expectOk checkFile "Routing" expectOk checkFile "Streaming" expectOk From b35e9ec806007cf4f5318d6a5aea2e2d2e8fb729 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 21 Jun 2023 16:28:46 +0200 Subject: [PATCH 060/222] test --- .../CompilerService/AsyncMemoize.fs | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 61fae5d2af3..6a2ed957c2c 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -7,6 +7,8 @@ open FSharp.Test open FSharp.Compiler.BuildGraph open Internal.Utilities.Collections open System.Threading.Tasks +open System.Diagnostics +open System.Collections.Concurrent [] let ``Basics``() = @@ -119,3 +121,115 @@ let ``Job keeps running even if first requestor cancels`` () = Thread.Sleep 1 // Wait for event log to be updated Assert.Equal<(JobEventType * int) array>([| Started, key; Finished, key |], eventLog |> Seq.toArray ) + +type ExpectedException() = + inherit Exception() + +[] +let ``Stress test`` () = + let seed = System.Random().Next() + + let rng = System.Random seed + let threads = 100 + let iterations = 100 + let maxDuration = 100 + let maxTimeout = 100 + let exceptionProbability = 0.01 + let stepMs = 10 + let keyCount = 300 + let keys = [| 1 .. keyCount |] + + let intenseComputation durationMs result = + async { + if rng.NextDouble() < exceptionProbability then + raise (ExpectedException()) + let s = Stopwatch.StartNew() + let mutable number = 0 + while (int s.ElapsedMilliseconds) < durationMs do + number <- number + 1 % 12345 + return result + } + |> NodeCode.AwaitAsync + + let rec sleepyComputation durationMs result = + node { + if rng.NextDouble() < (exceptionProbability / (float durationMs / float stepMs)) then + raise (ExpectedException()) + if durationMs > 0 then + do! Async.Sleep (min stepMs durationMs) |> NodeCode.AwaitAsync + return! sleepyComputation (durationMs - stepMs) result + else + return result + } + + let rec mixedComputation durationMs result = + node { + if durationMs > 0 then + if rng.NextDouble() < 0.5 then + do! intenseComputation (min stepMs durationMs) () + else + do! sleepyComputation (min stepMs durationMs) () + return! mixedComputation (durationMs - stepMs) result + else + return result + } + + let computations = [| + intenseComputation + sleepyComputation + mixedComputation + |] + + let cacheEvents = ConcurrentBag() + + let cache = AsyncMemoize(fun _ x -> cacheEvents.Add x) + + let mutable started = 0 + let mutable canceled = 0 + let mutable failed = 0 + let mutable completed = 0 + + seq { + for _ in 1..threads do + let rec loop iteration = + async { + let computation = computations[rng.Next computations.Length] + let durationMs = rng.Next maxDuration + let timeoutMs = rng.Next maxTimeout + let key = keys[rng.Next keys.Length] + let result = key * 2 + let job = cache.Get(key, computation durationMs result) |> Async.AwaitNodeCode + let! runningJob = Async.StartChild(job, timeoutMs) + Interlocked.Increment &started |> ignore + try + let! actual = runningJob + Assert.Equal(result, actual) + Interlocked.Increment &completed |> ignore + with + | :? TaskCanceledException + | :? TimeoutException -> Interlocked.Increment &canceled |> ignore + | :? ExpectedException -> Interlocked.Increment &failed |> ignore + | :? AggregateException as ex when + ex.InnerExceptions |> Seq.exists (fun e -> e :? ExpectedException) -> + Interlocked.Increment &failed |> ignore + | e -> + failwith $"Seed {seed} failed on iteration {iteration}: %A{e}" + if iteration < iterations then + return! loop (iteration + 1) + return () + } + loop 1 + } + |> Async.Parallel + |> Async.RunSynchronously + |> ignore + + Assert.Equal (started, threads * iterations) + Assert.Equal (started, completed + canceled + failed) + + let events = + cacheEvents + |> Seq.countBy id + |> Seq.toArray + + ignore events From d7d9a7628e3a0dd80463ad6f4584a044bf7e0dbe Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 21 Jun 2023 18:05:43 +0200 Subject: [PATCH 061/222] test --- .../FSharpChecker/TransparentCompiler.fs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 216798d656b..1bcd01ccdf2 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -307,6 +307,36 @@ let ``Files that are not depended on don't invalidate cache part 2`` () = Assert.Equal([], intermediateTypeChecks |> Map.toList) +[] +[] +[] +let ``Multi-project`` signatureFiles = + + let sigFile = if signatureFiles then AutoGenerated else No + + let library = SyntheticProject.Create( + { sourceFile "LibA" [] with SignatureFile = sigFile } + //, + //{ sourceFile "LibB" ["LibA"] with SignatureFile = sigFile }, + //{ sourceFile "LibC" ["LibA"] with SignatureFile = sigFile }, + //{ sourceFile "LibD" ["LibB"; "LibC"] with SignatureFile = sigFile } + ) + + let project = + { SyntheticProject.Create( + sourceFile "A" ["LibA"] + //, + //sourceFile "B" ["A"; "LibB"], + //sourceFile "C" ["A"; "LibC"], + //sourceFile "D" ["A"; "LibD"] + ) + with DependsOn = [library] } + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + checkFile "A" expectOk + } + + type Operation = Update | Check | Add | Remove //[] From 5a7b68f7215b67ca95664615fd039c85ec0fcc7b Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 22 Jun 2023 19:11:54 +0200 Subject: [PATCH 062/222] wip --- .../FSharpChecker/TransparentCompiler.fs | 5 +++-- tests/FSharp.Test.Utilities/ProjectGeneration.fs | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 1bcd01ccdf2..3dd03477a8f 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -314,7 +314,7 @@ let ``Multi-project`` signatureFiles = let sigFile = if signatureFiles then AutoGenerated else No - let library = SyntheticProject.Create( + let library = SyntheticProject.Create("library", { sourceFile "LibA" [] with SignatureFile = sigFile } //, //{ sourceFile "LibB" ["LibA"] with SignatureFile = sigFile }, @@ -323,7 +323,7 @@ let ``Multi-project`` signatureFiles = ) let project = - { SyntheticProject.Create( + { SyntheticProject.Create("app", sourceFile "A" ["LibA"] //, //sourceFile "B" ["A"; "LibB"], @@ -333,6 +333,7 @@ let ``Multi-project`` signatureFiles = with DependsOn = [library] } ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + //updateFile "LibA" updatePublicSurface checkFile "A" expectOk } diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 626e25d9752..ce3661488fb 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -758,7 +758,10 @@ let SaveAndCheckProject project checker = do! saveProject project true checker - let! results = checker.ParseAndCheckProject(project.GetProjectOptions checker) + let options = project.GetProjectOptions checker + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot project) + + let! results = checker.ParseAndCheckProject(snapshot) if not (Array.isEmpty results.Diagnostics) then failwith $"Project {project.Name} failed initial check: \n%A{results.Diagnostics}" From e7f9930467bb7d206c3cc2412da41053f4d520a7 Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 23 Jun 2023 09:09:45 +0200 Subject: [PATCH 063/222] WIP --- src/Compiler/Checking/CheckDeclarations.fs | 3 + src/Compiler/FSharp.Compiler.Service.rsp | 499 ++++++++++++++++++ src/Compiler/SyntaxTree/PrettyNaming.fs | 1 - src/Compiler/SyntaxTree/SyntaxTree.fs | 2 +- src/Compiler/SyntaxTree/SyntaxTreeOps.fs | 108 ++-- src/Compiler/SyntaxTree/XmlDoc.fs | 1 - src/Compiler/SyntaxTree/XmlDoc.fsi | 1 - src/Compiler/Trimmed.rsp | 226 ++++++++ .../Graph/CompilationFromCmdlineArgsTests.fs | 11 +- 9 files changed, 790 insertions(+), 62 deletions(-) create mode 100644 src/Compiler/FSharp.Compiler.Service.rsp create mode 100644 src/Compiler/Trimmed.rsp diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index a37c39a7d43..a5a3dcd8dd1 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -4662,6 +4662,9 @@ let rec TcSignatureElementNonMutRec (cenv: cenv) parent typeNames endm (env: TcE | Some(p, _) -> TcOpenModuleOrNamespaceDecl cenv.tcSink g cenv.amap m.EndRange env ([p], m.EndRange) | None -> env, [] + if m.FileName.EndsWith("Continuation.fsi") then + () + // Publish the combined module type env.eModuleOrNamespaceTypeAccumulator.Value <- CombineCcuContentFragments [env.eModuleOrNamespaceTypeAccumulator.Value; modTyRoot] diff --git a/src/Compiler/FSharp.Compiler.Service.rsp b/src/Compiler/FSharp.Compiler.Service.rsp new file mode 100644 index 00000000000..f42fd83c989 --- /dev/null +++ b/src/Compiler/FSharp.Compiler.Service.rsp @@ -0,0 +1,499 @@ +-o:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSharp.Compiler.Service.dll +-g +--debug:embedded +--noframework +--define:TRACE +--define:COMPILER +--define:FSHARPCORE_USE_PACKAGE +--define:DEBUG +--define:NETSTANDARD +--define:FX_NO_WINFORMS +--define:Debug +--define:NETSTANDARD +--define:NETSTANDARD2_0 +--define:NETSTANDARD1_0_OR_GREATER +--define:NETSTANDARD1_1_OR_GREATER +--define:NETSTANDARD1_2_OR_GREATER +--define:NETSTANDARD1_3_OR_GREATER +--define:NETSTANDARD1_4_OR_GREATER +--define:NETSTANDARD1_5_OR_GREATER +--define:NETSTANDARD1_6_OR_GREATER +--define:NETSTANDARD2_0_OR_GREATER +--doc:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSharp.Compiler.Service.xml +--optimize- +--resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSComp.resources +--resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSIstrings.resources +--resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\UtilsStrings.resources +--resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSStrings.resources +-r:C:\Users\nojaf\.nuget\packages\fsharp.core\7.0.0\lib\netstandard2.0\FSharp.Core.dll +-r:C:\Users\nojaf\Projects\fsharp\artifacts\bin\FSharp.DependencyManager.Nuget\Debug\netstandard2.0\FSharp.DependencyManager.Nuget.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\Microsoft.Win32.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\mscorlib.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.AppContext.dll +-r:C:\Users\nojaf\.nuget\packages\system.buffers\4.5.1\ref\netstandard2.0\System.Buffers.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.Concurrent.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.dll +-r:C:\Users\nojaf\.nuget\packages\system.collections.immutable\6.0.0\lib\netstandard2.0\System.Collections.Immutable.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.NonGeneric.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.Specialized.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.Composition.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.EventBasedAsync.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.TypeConverter.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Console.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Core.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Data.Common.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Data.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Contracts.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Debug.dll +-r:C:\Users\nojaf\.nuget\packages\system.diagnostics.diagnosticsource\7.0.0\lib\netstandard2.0\System.Diagnostics.DiagnosticSource.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.FileVersionInfo.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Process.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.StackTrace.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.TextWriterTraceListener.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Tools.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.TraceSource.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Tracing.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Drawing.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Drawing.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Dynamic.Runtime.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.Calendars.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.Extensions.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.FileSystem.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.ZipFile.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.DriveInfo.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.Watcher.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.IsolatedStorage.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.MemoryMappedFiles.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Pipes.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.UnmanagedMemoryStream.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Expressions.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Parallel.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Queryable.dll +-r:C:\Users\nojaf\.nuget\packages\system.memory\4.5.5\lib\netstandard2.0\System.Memory.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Http.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.NameResolution.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.NetworkInformation.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Ping.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Requests.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Security.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Sockets.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebHeaderCollection.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebSockets.Client.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebSockets.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Numerics.dll +-r:C:\Users\nojaf\.nuget\packages\system.numerics.vectors\4.4.0\ref\netstandard2.0\System.Numerics.Vectors.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ObjectModel.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Reflection.dll +-r:C:\Users\nojaf\.nuget\packages\system.reflection.emit\4.7.0\ref\netstandard2.0\System.Reflection.Emit.dll +-r:C:\Users\nojaf\.nuget\packages\system.reflection.emit.ilgeneration\4.7.0\ref\netstandard2.0\System.Reflection.Emit.ILGeneration.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Reflection.Extensions.dll +-r:C:\Users\nojaf\.nuget\packages\system.reflection.metadata\6.0.1\lib\netstandard2.0\System.Reflection.Metadata.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Reflection.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.Reader.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.ResourceManager.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.Writer.dll +-r:C:\Users\nojaf\.nuget\packages\system.runtime.compilerservices.unsafe\6.0.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.CompilerServices.VisualC.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Extensions.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Handles.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.InteropServices.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.InteropServices.RuntimeInformation.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Numerics.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Formatters.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Json.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Xml.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Claims.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Algorithms.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Csp.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Encoding.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.X509Certificates.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Principal.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.SecureString.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ServiceModel.Web.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Text.Encoding.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Text.Encoding.Extensions.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Text.RegularExpressions.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Overlapped.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Tasks.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Tasks.Parallel.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Thread.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.ThreadPool.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Timer.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Transactions.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ValueTuple.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Web.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Windows.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.Linq.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.ReaderWriter.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.Serialization.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XDocument.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XmlDocument.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XmlSerializer.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XPath.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XPath.XDocument.dll +--target:library +--nowarn:44,57,75,1204,NU5125,IL2121 +--warn:3 +--warnaserror:3239,1182,0025 +--fullpaths +--flaterrors +--highentropyva+ +--targetprofile:netstandard +--nocopyfsharpcore +--deterministic+ +--simpleresolution +--nowarn:3384 +--times +--nowarn:75 +--extraoptimizationloops:1 +--warnon:1182 +--warnon:3218 +--warnon:3390 +--simpleresolution +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\buildproperties.fs +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSComp.fs +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSIstrings.fs +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\UtilsStrings.fs +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\.NETStandard,Version=v2.0.AssemblyAttributes.fs +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSharp.Compiler.Service.AssemblyInfo.fs +Utilities\Activity.fsi +Utilities\Activity.fs +Utilities\sformat.fsi +Utilities\sformat.fs +Utilities\sr.fsi +Utilities\sr.fs +Utilities\ResizeArray.fsi +Utilities\ResizeArray.fs +Utilities\HashMultiMap.fsi +Utilities\HashMultiMap.fs +Utilities\EditDistance.fsi +Utilities\EditDistance.fs +Utilities\TaggedCollections.fsi +Utilities\TaggedCollections.fs +Utilities\illib.fsi +Utilities\illib.fs +Utilities\FileSystem.fsi +Utilities\FileSystem.fs +Utilities\ildiag.fsi +Utilities\ildiag.fs +Utilities\zmap.fsi +Utilities\zmap.fs +Utilities\zset.fsi +Utilities\zset.fs +Utilities\XmlAdapters.fsi +Utilities\XmlAdapters.fs +Utilities\InternalCollections.fsi +Utilities\InternalCollections.fs +Utilities\QueueList.fsi +Utilities\QueueList.fs +Utilities\lib.fsi +Utilities\lib.fs +Utilities\ImmutableArray.fsi +Utilities\ImmutableArray.fs +Utilities\rational.fsi +Utilities\rational.fs +Utilities\PathMap.fsi +Utilities\PathMap.fs +Utilities\RidHelpers.fs +Utilities\range.fsi +Utilities\range.fs +Facilities\LanguageFeatures.fsi +Facilities\LanguageFeatures.fs +Facilities\DiagnosticOptions.fsi +Facilities\DiagnosticOptions.fs +Facilities\TextLayoutRender.fsi +Facilities\TextLayoutRender.fs +Facilities\DiagnosticsLogger.fsi +Facilities\DiagnosticsLogger.fs +Facilities\DiagnosticResolutionHints.fsi +Facilities\DiagnosticResolutionHints.fs +Facilities\prim-lexing.fsi +Facilities\prim-lexing.fs +Facilities\prim-parsing.fsi +Facilities\prim-parsing.fs +Facilities\ReferenceResolver.fsi +Facilities\ReferenceResolver.fs +Facilities\SimulatedMSBuildReferenceResolver.fsi +Facilities\SimulatedMSBuildReferenceResolver.fs +Facilities\CompilerLocation.fsi +Facilities\CompilerLocation.fs +Facilities\BuildGraph.fsi +Facilities\BuildGraph.fs +AbstractIL\il.fsi +AbstractIL\il.fs +AbstractIL\ilx.fsi +AbstractIL\ilx.fs +AbstractIL\ilascii.fsi +AbstractIL\ilascii.fs +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\ilpars.fsi +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\ilpars.fs +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\illex.fsi +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\illex.fs +AbstractIL\ilprint.fsi +AbstractIL\ilprint.fs +AbstractIL\ilmorph.fsi +AbstractIL\ilmorph.fs +AbstractIL\ilsign.fsi +AbstractIL\ilsign.fs +AbstractIL\ilnativeres.fsi +AbstractIL\ilnativeres.fs +AbstractIL\ilsupp.fsi +AbstractIL\ilsupp.fs +AbstractIL\ilbinary.fsi +AbstractIL\ilbinary.fs +AbstractIL\ilread.fsi +AbstractIL\ilread.fs +AbstractIL\ilwritepdb.fsi +AbstractIL\ilwritepdb.fs +AbstractIL\ilwrite.fsi +AbstractIL\ilwrite.fs +AbstractIL\ilreflect.fsi +AbstractIL\ilreflect.fs +SyntaxTree\PrettyNaming.fsi +SyntaxTree\PrettyNaming.fs +SyntaxTree\UnicodeLexing.fsi +SyntaxTree\UnicodeLexing.fs +SyntaxTree\XmlDoc.fsi +SyntaxTree\XmlDoc.fs +SyntaxTree\SyntaxTrivia.fsi +SyntaxTree\SyntaxTrivia.fs +SyntaxTree\SyntaxTree.fsi +SyntaxTree\SyntaxTree.fs +SyntaxTree\SyntaxTreeOps.fsi +SyntaxTree\SyntaxTreeOps.fs +SyntaxTree\ParseHelpers.fsi +SyntaxTree\ParseHelpers.fs +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\pppars.fsi +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\pppars.fs +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\pars.fsi +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\pars.fs +SyntaxTree\LexHelpers.fsi +SyntaxTree\LexHelpers.fs +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\pplex.fsi +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\pplex.fs +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\\lex.fsi +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\\lex.fs +SyntaxTree\LexFilter.fsi +SyntaxTree\LexFilter.fs +TypedTree\tainted.fsi +TypedTree\tainted.fs +TypedTree\TypeProviders.fsi +TypedTree\TypeProviders.fs +TypedTree\QuotationPickler.fsi +TypedTree\QuotationPickler.fs +TypedTree\CompilerGlobalState.fsi +TypedTree\CompilerGlobalState.fs +TypedTree\TypedTree.fsi +TypedTree\TypedTree.fs +TypedTree\TypedTreeBasics.fsi +TypedTree\TypedTreeBasics.fs +TypedTree\TcGlobals.fs +TypedTree\TypedTreeOps.fsi +TypedTree\TypedTreeOps.fs +TypedTree\TypedTreePickle.fsi +TypedTree\TypedTreePickle.fs +TypedTree\UpdatePrettyTyparNames.fsi +TypedTree\UpdatePrettyTyparNames.fs +Checking\import.fsi +Checking\import.fs +Checking\TypeHierarchy.fsi +Checking\TypeHierarchy.fs +Checking\infos.fsi +Checking\infos.fs +Checking\AccessibilityLogic.fsi +Checking\AccessibilityLogic.fs +Checking\AttributeChecking.fsi +Checking\AttributeChecking.fs +Checking\TypeRelations.fsi +Checking\TypeRelations.fs +Checking\InfoReader.fsi +Checking\InfoReader.fs +Checking\NicePrint.fsi +Checking\NicePrint.fs +Checking\AugmentWithHashCompare.fsi +Checking\AugmentWithHashCompare.fs +Checking\NameResolution.fsi +Checking\NameResolution.fs +Checking\SignatureConformance.fsi +Checking\SignatureConformance.fs +Checking\MethodOverrides.fsi +Checking\MethodOverrides.fs +Checking\MethodCalls.fsi +Checking\MethodCalls.fs +Checking\PatternMatchCompilation.fsi +Checking\PatternMatchCompilation.fs +Checking\ConstraintSolver.fsi +Checking\ConstraintSolver.fs +Checking\CheckFormatStrings.fsi +Checking\CheckFormatStrings.fs +Checking\FindUnsolved.fsi +Checking\FindUnsolved.fs +Checking\QuotationTranslator.fsi +Checking\QuotationTranslator.fs +Checking\PostInferenceChecks.fsi +Checking\PostInferenceChecks.fs +Checking\CheckBasics.fsi +Checking\CheckBasics.fs +Checking\CheckRecordSyntaxHelpers.fsi +Checking\CheckRecordSyntaxHelpers.fs +Checking\CheckExpressions.fsi +Checking\CheckExpressions.fs +Checking\CheckPatterns.fsi +Checking\CheckPatterns.fs +Checking\CheckComputationExpressions.fsi +Checking\CheckComputationExpressions.fs +Checking\CheckIncrementalClasses.fsi +Checking\CheckIncrementalClasses.fs +Checking\CheckDeclarations.fsi +Checking\CheckDeclarations.fs +Optimize\Optimizer.fsi +Optimize\Optimizer.fs +Optimize\DetupleArgs.fsi +Optimize\DetupleArgs.fs +Optimize\InnerLambdasToTopLevelFuncs.fsi +Optimize\InnerLambdasToTopLevelFuncs.fs +Optimize\LowerCalls.fsi +Optimize\LowerCalls.fs +Optimize\LowerSequences.fsi +Optimize\LowerSequences.fs +Optimize\LowerComputedCollections.fsi +Optimize\LowerComputedCollections.fs +Optimize\LowerStateMachines.fsi +Optimize\LowerStateMachines.fs +Optimize\LowerLocalMutables.fsi +Optimize\LowerLocalMutables.fs +CodeGen\IlxGenSupport.fsi +CodeGen\IlxGenSupport.fs +CodeGen\EraseClosures.fsi +CodeGen\EraseClosures.fs +CodeGen\EraseUnions.fsi +CodeGen\EraseUnions.fs +CodeGen\IlxGen.fsi +CodeGen\IlxGen.fs +Driver\FxResolver.fsi +Driver\FxResolver.fs +DependencyManager/AssemblyResolveHandler.fsi +DependencyManager/AssemblyResolveHandler.fs +DependencyManager/NativeDllResolveHandler.fsi +DependencyManager/NativeDllResolveHandler.fs +DependencyManager/DependencyProvider.fsi +DependencyManager/DependencyProvider.fs +Driver\CompilerConfig.fsi +Driver\CompilerConfig.fs +Driver\CompilerImports.fsi +Driver\CompilerImports.fs +Driver\CompilerDiagnostics.fsi +Driver\CompilerDiagnostics.fs +Driver\GraphChecking\Continuation.fsi +Driver\GraphChecking\Continuation.fs +Driver\GraphChecking\Types.fsi +Driver\GraphChecking\Types.fs +Driver\GraphChecking\Graph.fsi +Driver\GraphChecking\Graph.fs +Driver\GraphChecking\TrieMapping.fsi +Driver\GraphChecking\TrieMapping.fs +Driver\GraphChecking\FileContentMapping.fsi +Driver\GraphChecking\FileContentMapping.fs +Driver\GraphChecking\DependencyResolution.fsi +Driver\GraphChecking\DependencyResolution.fs +Driver\GraphChecking\GraphProcessing.fsi +Driver\GraphChecking\GraphProcessing.fs +Driver\GraphChecking\TypeCheckingGraphProcessing.fsi +Driver\GraphChecking\TypeCheckingGraphProcessing.fs +Driver\ParseAndCheckInputs.fsi +Driver\ParseAndCheckInputs.fs +Driver\ScriptClosure.fsi +Driver\ScriptClosure.fs +Driver\CompilerOptions.fsi +Driver\CompilerOptions.fs +Driver\OptimizeInputs.fsi +Driver\OptimizeInputs.fs +Driver\XmlDocFileWriter.fsi +Driver\XmlDocFileWriter.fs +Driver\BinaryResourceFormats.fsi +Driver\BinaryResourceFormats.fs +Driver\StaticLinking.fsi +Driver\StaticLinking.fs +Driver\CreateILModule.fsi +Driver\CreateILModule.fs +Driver\fsc.fsi +Driver\fsc.fs +Symbols\FSharpDiagnostic.fsi +Symbols\FSharpDiagnostic.fs +Symbols\SymbolHelpers.fsi +Symbols\SymbolHelpers.fs +Symbols\Symbols.fsi +Symbols\Symbols.fs +Symbols\Exprs.fsi +Symbols\Exprs.fs +Symbols\SymbolPatterns.fsi +Symbols\SymbolPatterns.fs +Service\SemanticClassification.fsi +Service\SemanticClassification.fs +Service\ItemKey.fsi +Service\ItemKey.fs +Service\SemanticClassificationKey.fsi +Service\SemanticClassificationKey.fs +Service\FSharpSource.fsi +Service\FSharpSource.fs +Service\IncrementalBuild.fsi +Service\IncrementalBuild.fs +Service\ServiceCompilerDiagnostics.fsi +Service\ServiceCompilerDiagnostics.fs +Service\ServiceConstants.fs +Service\ServiceDeclarationLists.fsi +Service\ServiceDeclarationLists.fs +Service\ServiceLexing.fsi +Service\ServiceLexing.fs +Service\ServiceParseTreeWalk.fsi +Service\ServiceParseTreeWalk.fs +Service\ServiceNavigation.fsi +Service\ServiceNavigation.fs +Service\ServiceParamInfoLocations.fsi +Service\ServiceParamInfoLocations.fs +Service\FSharpParseFileResults.fsi +Service\FSharpParseFileResults.fs +Service\ServiceParsedInputOps.fsi +Service\ServiceParsedInputOps.fs +Service\ServiceAssemblyContent.fsi +Service\ServiceAssemblyContent.fs +Service\ServiceXmlDocParser.fsi +Service\ServiceXmlDocParser.fs +Service\ExternalSymbol.fsi +Service\ExternalSymbol.fs +Service\QuickParse.fsi +Service\QuickParse.fs +Service\FSharpCheckerResults.fsi +Service\FSharpCheckerResults.fs +Service\service.fsi +Service\service.fs +Service\ServiceInterfaceStubGenerator.fsi +Service\ServiceInterfaceStubGenerator.fs +Service\ServiceStructure.fsi +Service\ServiceStructure.fs +Service\ServiceAnalysis.fsi +Service\ServiceAnalysis.fs +Interactive\FSharpInteractiveServer.fsi +Interactive\FSharpInteractiveServer.fs +Interactive\ControlledExecution.fs +Interactive\fsi.fsi +Interactive\fsi.fs +Legacy\LegacyHostedCompilerForTesting.fs diff --git a/src/Compiler/SyntaxTree/PrettyNaming.fs b/src/Compiler/SyntaxTree/PrettyNaming.fs index f78f742a03f..55721ae6222 100755 --- a/src/Compiler/SyntaxTree/PrettyNaming.fs +++ b/src/Compiler/SyntaxTree/PrettyNaming.fs @@ -10,7 +10,6 @@ open System.Collections.Concurrent open System.Globalization open System.Text -open FSharp.Compiler.AbstractIL open Internal.Utilities.Library open FSharp.Compiler.Text open FSharp.Compiler.Text.Layout diff --git a/src/Compiler/SyntaxTree/SyntaxTree.fs b/src/Compiler/SyntaxTree/SyntaxTree.fs index e9eb8bdfac3..230946c5f4f 100644 --- a/src/Compiler/SyntaxTree/SyntaxTree.fs +++ b/src/Compiler/SyntaxTree/SyntaxTree.fs @@ -4,7 +4,7 @@ namespace rec FSharp.Compiler.Syntax open System open System.Diagnostics -open Internal.Utilities.Library +// open Internal.Utilities.Library open FSharp.Compiler.Syntax open FSharp.Compiler.Text open FSharp.Compiler.Text.Range diff --git a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs index f25b7abb37f..c96d040c511 100644 --- a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs +++ b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs @@ -65,7 +65,7 @@ let mkSynCompGenSimplePatVar id = let (|SynSingleIdent|_|) x = match x with - | SynLongIdent ([ id ], _, _) -> Some id + | SynLongIdent (id = [ id ]) -> Some id | _ -> None /// Match a long identifier, including the case for single identifiers which gets a more optimized node in the syntax tree. @@ -105,13 +105,13 @@ let (|SynPipeRight|_|) input = let (|SynPipeRight2|_|) input = match input with - | SynBinOp (synId, SynExpr.Paren (SynExpr.Tuple (false, [ x1a; x1b ], _, _), _, _, _), x2) when synId.idText = "op_PipeRight2" -> + | SynBinOp (synId, SynExpr.Paren (expr = SynExpr.Tuple (isStruct = false; exprs = [ x1a; x1b ])), x2) when synId.idText = "op_PipeRight2" -> Some(x1a, x1b, x2) | _ -> None let (|SynPipeRight3|_|) input = match input with - | SynBinOp (synId, SynExpr.Paren (SynExpr.Tuple (false, [ x1a; x1b; x1c ], _, _), _, _, _), x2) when synId.idText = "op_PipeRight3" -> + | SynBinOp (synId, SynExpr.Paren (expr = SynExpr.Tuple (isStruct = false; exprs = [ x1a; x1b; x1c ])), x2) when synId.idText = "op_PipeRight3" -> Some(x1a, x1b, x1c, x2) | _ -> None @@ -138,7 +138,7 @@ let rec IsControlFlowExpression e = | SynExpr.LetOrUse _ | SynExpr.Sequential _ // Treat "ident { ... }" as a control flow expression - | SynExpr.App (_, _, SynExpr.Ident _, SynExpr.ComputationExpr _, _) + | SynExpr.App (funcExpr = SynExpr.Ident _; argExpr = SynExpr.ComputationExpr _) | SynExpr.IfThenElse _ | SynExpr.LetOrUseBang _ | SynExpr.Match _ @@ -147,7 +147,7 @@ let rec IsControlFlowExpression e = | SynExpr.For _ | SynExpr.ForEach _ | SynExpr.While _ -> true - | SynExpr.Typed (e, _, _) -> IsControlFlowExpression e + | SynExpr.Typed (expr = e) -> IsControlFlowExpression e | _ -> false // The syntactic criteria for when a debug point for a 'let' is extended to include @@ -257,7 +257,7 @@ let rec SimplePatOfPat (synArgNameGenerator: SynArgNameGenerator) p = let item = mkSynIdGetWithAlt m id altNameRefCell false, altNameRefCell, id, item | SynPat.Named (SynIdent (ident, _), _, _, _) - | SynPat.As (_, SynPat.Named (SynIdent (ident, _), _, _, _), _) -> + | SynPat.As (rhsPat = SynPat.Named (ident = SynIdent (ident, _))) -> // named pats should be referred to as their name in docs, tooltips, etc. let item = mkSynIdGet m ident.idText false, None, ident, item @@ -423,11 +423,11 @@ let mkSynQMarkSet m a b c = mkSynTrifix m qmarkSet a b c let mkSynDotParenGet mLhs mDot a b = match b with - | SynExpr.Tuple (false, [ _; _ ], _, _) -> + | SynExpr.Tuple (isStruct = false; exprs = [ _; _ ]) -> errorR (Deprecated(FSComp.SR.astDeprecatedIndexerNotation (), mLhs)) SynExpr.Const(SynConst.Unit, mLhs) - | SynExpr.Tuple (false, [ _; _; _ ], _, _) -> + | SynExpr.Tuple (isStruct = false; exprs = [ _; _; _ ]) -> errorR (Deprecated(FSComp.SR.astDeprecatedIndexerNotation (), mLhs)) SynExpr.Const(SynConst.Unit, mLhs) @@ -447,13 +447,13 @@ let mkSynAssign (l: SynExpr) (r: SynExpr) = match l with //| SynExpr.Paren (l2, m2) -> mkSynAssign m l2 r | LongOrSingleIdent (false, v, None, _) -> SynExpr.LongIdentSet(v, r, m) - | SynExpr.DotGet (e, _, v, _) -> SynExpr.DotSet(e, v, r, m) + | SynExpr.DotGet (expr = e; longDotId = v) -> SynExpr.DotSet(e, v, r, m) | SynExpr.DotIndexedGet (e1, e2, mDot, mLeft) -> SynExpr.DotIndexedSet(e1, e2, r, mLeft, mDot, m) | SynExpr.LibraryOnlyUnionCaseFieldGet (x, y, z, _) -> SynExpr.LibraryOnlyUnionCaseFieldSet(x, y, z, r, m) - | SynExpr.App (_, _, SynExpr.App (_, _, SingleIdent nm, a, _), b, _) when nm.idText = opNameQMark -> mkSynQMarkSet m a b r - | SynExpr.App (_, _, SynExpr.App (_, _, SingleIdent nm, a, _), b, _) when nm.idText = opNameParenGet -> mkSynDotParenSet m a b r - | SynExpr.App (_, _, SynExpr.LongIdent (false, v, None, _), x, _) -> SynExpr.NamedIndexedPropertySet(v, x, r, m) - | SynExpr.App (_, _, SynExpr.DotGet (e, _, v, _), x, _) -> SynExpr.DotNamedIndexedPropertySet(e, v, x, r, m) + | SynExpr.App (funcExpr = SynExpr.App (funcExpr = SingleIdent nm; argExpr = a); argExpr = b) when nm.idText = opNameQMark -> mkSynQMarkSet m a b r + | SynExpr.App (funcExpr = SynExpr.App (funcExpr = SingleIdent nm; argExpr = a); argExpr = b) when nm.idText = opNameParenGet -> mkSynDotParenSet m a b r + | SynExpr.App (funcExpr = SynExpr.LongIdent (false, v, None, _); argExpr = x) -> SynExpr.NamedIndexedPropertySet(v, x, r, m) + | SynExpr.App (funcExpr = SynExpr.DotGet (expr = e; longDotId = v); argExpr = x) -> SynExpr.DotNamedIndexedPropertySet(e, v, x, r, m) | l -> SynExpr.Set(l, r, m) let mkSynDot mDot m l (SynIdent (r, rTrivia)) = @@ -566,13 +566,13 @@ module SynInfo = let AritiesOfArgs (SynValInfo (args, _)) = List.map List.length args /// Get the argument attributes from the syntactic information for an argument. - let AttribsOfArgData (SynArgInfo (Attributes attribs, _, _)) = attribs + let AttribsOfArgData (SynArgInfo (attributes = Attributes attribs)) = attribs /// Infer the syntactic argument info for a single argument from a simple pattern. let rec InferSynArgInfoFromSimplePat attribs p = match p with - | SynSimplePat.Id (nm, _, isCompGen, _, isOpt, _) -> SynArgInfo(attribs, isOpt, (if isCompGen then None else Some nm)) - | SynSimplePat.Typed (a, _, _) -> InferSynArgInfoFromSimplePat attribs a + | SynSimplePat.Id (ident = nm; isCompilerGenerated = isCompGen; isOptional = isOpt) -> SynArgInfo(attribs, isOpt, (if isCompGen then None else Some nm)) + | SynSimplePat.Typed (pat = a) -> InferSynArgInfoFromSimplePat attribs a | SynSimplePat.Attrib (a, attribs2, _) -> InferSynArgInfoFromSimplePat (attribs @ attribs2) a /// Infer the syntactic argument info for one or more arguments one or more simple patterns. @@ -802,40 +802,40 @@ let rec synExprContainsError inpExpr = | SynExpr.Const _ | SynExpr.Dynamic _ -> false - | SynExpr.TypeTest (e, _, _) - | SynExpr.Upcast (e, _, _) - | SynExpr.AddressOf (_, e, _, _) - | SynExpr.ComputationExpr (_, e, _) - | SynExpr.ArrayOrListComputed (_, e, _) - | SynExpr.Typed (e, _, _) - | SynExpr.Do (e, _) - | SynExpr.Assert (e, _) - | SynExpr.DotGet (e, _, _, _) - | SynExpr.LongIdentSet (_, e, _) - | SynExpr.New (_, _, e, _) - | SynExpr.TypeApp (e, _, _, _, _, _, _) - | SynExpr.LibraryOnlyUnionCaseFieldGet (e, _, _, _) - | SynExpr.Downcast (e, _, _) - | SynExpr.InferredUpcast (e, _) - | SynExpr.InferredDowncast (e, _) - | SynExpr.Lazy (e, _) - | SynExpr.TraitCall (_, _, e, _) - | SynExpr.YieldOrReturn (_, e, _) - | SynExpr.YieldOrReturnFrom (_, e, _) - | SynExpr.DoBang (e, _) - | SynExpr.Fixed (e, _) - | SynExpr.DebugPoint (_, _, e) - | SynExpr.Paren (e, _, _, _) -> walkExpr e - - | SynExpr.NamedIndexedPropertySet (_, e1, e2, _) - | SynExpr.DotSet (e1, _, e2, _) - | SynExpr.Set (e1, e2, _) - | SynExpr.LibraryOnlyUnionCaseFieldSet (e1, _, _, e2, _) - | SynExpr.JoinIn (e1, _, e2, _) - | SynExpr.App (_, _, e1, e2, _) -> walkExpr e1 || walkExpr e2 - - | SynExpr.ArrayOrList (_, es, _) - | SynExpr.Tuple (_, es, _, _) -> walkExprs es + | SynExpr.TypeTest (expr = e) + | SynExpr.Upcast (expr = e) + | SynExpr.AddressOf (expr = e) + | SynExpr.ComputationExpr (expr = e) + | SynExpr.ArrayOrListComputed (expr = e) + | SynExpr.Typed (expr = e) + | SynExpr.Do (expr = e) + | SynExpr.Assert (expr = e) + | SynExpr.DotGet (expr = e) + | SynExpr.LongIdentSet (expr = e) + | SynExpr.New (expr = e) + | SynExpr.TypeApp (expr = e) + | SynExpr.LibraryOnlyUnionCaseFieldGet (expr = e) + | SynExpr.Downcast (expr = e) + | SynExpr.InferredUpcast (expr = e) + | SynExpr.InferredDowncast (expr = e) + | SynExpr.Lazy (expr = e) + | SynExpr.TraitCall (argExpr = e) + | SynExpr.YieldOrReturn (expr = e) + | SynExpr.YieldOrReturnFrom (expr = e) + | SynExpr.DoBang (expr = e) + | SynExpr.Fixed (expr = e) + | SynExpr.DebugPoint (innerExpr = e) + | SynExpr.Paren (expr = e) -> walkExpr e + + | SynExpr.NamedIndexedPropertySet (expr1 = e1; expr2 = e2) + | SynExpr.DotSet (targetExpr = e1; rhsExpr = e2) + | SynExpr.Set (targetExpr = e1; rhsExpr = e2) + | SynExpr.LibraryOnlyUnionCaseFieldSet (expr = e1; rhsExpr = e2) + | SynExpr.JoinIn (lhsExpr = e1; rhsExpr = e2) + | SynExpr.App (funcExpr = e1; argExpr = e2) -> walkExpr e1 || walkExpr e2 + + | SynExpr.ArrayOrList (exprs = es) + | SynExpr.Tuple (exprs = es) -> walkExprs es | SynExpr.AnonRecd (copyInfo = origExpr; recordFields = flds) -> (match origExpr with @@ -861,12 +861,12 @@ let rec synExprContainsError inpExpr = walkBinds bs || walkBinds binds - | SynExpr.ForEach (_, _, _, _, _, e1, e2, _) - | SynExpr.While (_, e1, e2, _) -> walkExpr e1 || walkExpr e2 + | SynExpr.ForEach (enumExpr = e1; bodyExpr = e2) + | SynExpr.While (whileExpr = e1; doExpr = e2) -> walkExpr e1 || walkExpr e2 | SynExpr.For (identBody = e1; toBody = e2; doBody = e3) -> walkExpr e1 || walkExpr e2 || walkExpr e3 - | SynExpr.MatchLambda (_, _, cl, _, _) -> walkMatchClauses cl + | SynExpr.MatchLambda (matchClauses = cl) -> walkMatchClauses cl | SynExpr.Lambda (body = e) -> walkExpr e @@ -880,7 +880,7 @@ let rec synExprContainsError inpExpr = | SynExpr.Sequential (_, _, e1, e2, _) -> walkExpr e1 || walkExpr e2 - | SynExpr.SequentialOrImplicitYield (_, e1, e2, _, _) -> walkExpr e1 || walkExpr e2 + | SynExpr.SequentialOrImplicitYield (expr1 = e1; expr2 = e2) -> walkExpr e1 || walkExpr e2 | SynExpr.IfThenElse (ifExpr = e1; thenExpr = e2; elseExpr = e3opt) -> walkExpr e1 || walkExpr e2 || walkExprOpt e3opt diff --git a/src/Compiler/SyntaxTree/XmlDoc.fs b/src/Compiler/SyntaxTree/XmlDoc.fs index e5aa1b70d00..ee92eae9a02 100644 --- a/src/Compiler/SyntaxTree/XmlDoc.fs +++ b/src/Compiler/SyntaxTree/XmlDoc.fs @@ -13,7 +13,6 @@ open FSharp.Compiler.DiagnosticsLogger open FSharp.Compiler.IO open FSharp.Compiler.Text open FSharp.Compiler.Text.Range -open FSharp.Compiler.AbstractIL.IL /// Represents collected XmlDoc lines [] diff --git a/src/Compiler/SyntaxTree/XmlDoc.fsi b/src/Compiler/SyntaxTree/XmlDoc.fsi index 939a1b238e5..33b168786cc 100644 --- a/src/Compiler/SyntaxTree/XmlDoc.fsi +++ b/src/Compiler/SyntaxTree/XmlDoc.fsi @@ -3,7 +3,6 @@ namespace FSharp.Compiler.Xml open FSharp.Compiler.Text -open FSharp.Compiler.AbstractIL.IL /// Represents collected XmlDoc lines [] diff --git a/src/Compiler/Trimmed.rsp b/src/Compiler/Trimmed.rsp new file mode 100644 index 00000000000..a981f6d5be2 --- /dev/null +++ b/src/Compiler/Trimmed.rsp @@ -0,0 +1,226 @@ +-o:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSharp.Compiler.Service.dll +-g +--nologo +--debug:embedded +--noframework +--define:TRACE +--define:COMPILER +--define:FSHARPCORE_USE_PACKAGE +--define:DEBUG +--define:NETSTANDARD +--define:FX_NO_WINFORMS +--define:Debug +--define:NETSTANDARD +--define:NETSTANDARD2_0 +--define:NETSTANDARD1_0_OR_GREATER +--define:NETSTANDARD1_1_OR_GREATER +--define:NETSTANDARD1_2_OR_GREATER +--define:NETSTANDARD1_3_OR_GREATER +--define:NETSTANDARD1_4_OR_GREATER +--define:NETSTANDARD1_5_OR_GREATER +--define:NETSTANDARD1_6_OR_GREATER +--define:NETSTANDARD2_0_OR_GREATER +--doc:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSharp.Compiler.Service.xml +--optimize- +--resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSComp.resources +--resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSIstrings.resources +--resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\UtilsStrings.resources +--resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSStrings.resources +-r:C:\Users\nojaf\.nuget\packages\fsharp.core\7.0.0\lib\netstandard2.0\FSharp.Core.dll +-r:C:\Users\nojaf\Projects\fsharp\artifacts\bin\FSharp.DependencyManager.Nuget\Debug\netstandard2.0\FSharp.DependencyManager.Nuget.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\Microsoft.Win32.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\mscorlib.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.AppContext.dll +-r:C:\Users\nojaf\.nuget\packages\system.buffers\4.5.1\ref\netstandard2.0\System.Buffers.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.Concurrent.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.dll +-r:C:\Users\nojaf\.nuget\packages\system.collections.immutable\6.0.0\lib\netstandard2.0\System.Collections.Immutable.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.NonGeneric.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.Specialized.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.Composition.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.EventBasedAsync.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.TypeConverter.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Console.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Core.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Data.Common.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Data.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Contracts.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Debug.dll +-r:C:\Users\nojaf\.nuget\packages\system.diagnostics.diagnosticsource\7.0.0\lib\netstandard2.0\System.Diagnostics.DiagnosticSource.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.FileVersionInfo.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Process.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.StackTrace.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.TextWriterTraceListener.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Tools.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.TraceSource.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Tracing.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Drawing.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Drawing.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Dynamic.Runtime.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.Calendars.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.Extensions.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.FileSystem.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.ZipFile.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.DriveInfo.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.Watcher.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.IsolatedStorage.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.MemoryMappedFiles.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Pipes.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.UnmanagedMemoryStream.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Expressions.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Parallel.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Queryable.dll +-r:C:\Users\nojaf\.nuget\packages\system.memory\4.5.5\lib\netstandard2.0\System.Memory.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Http.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.NameResolution.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.NetworkInformation.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Ping.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Requests.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Security.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Sockets.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebHeaderCollection.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebSockets.Client.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebSockets.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Numerics.dll +-r:C:\Users\nojaf\.nuget\packages\system.numerics.vectors\4.4.0\ref\netstandard2.0\System.Numerics.Vectors.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ObjectModel.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Reflection.dll +-r:C:\Users\nojaf\.nuget\packages\system.reflection.emit\4.7.0\ref\netstandard2.0\System.Reflection.Emit.dll +-r:C:\Users\nojaf\.nuget\packages\system.reflection.emit.ilgeneration\4.7.0\ref\netstandard2.0\System.Reflection.Emit.ILGeneration.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Reflection.Extensions.dll +-r:C:\Users\nojaf\.nuget\packages\system.reflection.metadata\6.0.1\lib\netstandard2.0\System.Reflection.Metadata.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Reflection.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.Reader.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.ResourceManager.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.Writer.dll +-r:C:\Users\nojaf\.nuget\packages\system.runtime.compilerservices.unsafe\6.0.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.CompilerServices.VisualC.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Extensions.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Handles.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.InteropServices.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.InteropServices.RuntimeInformation.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Numerics.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Formatters.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Json.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Xml.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Claims.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Algorithms.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Csp.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Encoding.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Primitives.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.X509Certificates.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Principal.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.SecureString.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ServiceModel.Web.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Text.Encoding.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Text.Encoding.Extensions.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Text.RegularExpressions.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Overlapped.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Tasks.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Tasks.Parallel.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Thread.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.ThreadPool.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Timer.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Transactions.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ValueTuple.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Web.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Windows.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.Linq.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.ReaderWriter.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.Serialization.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XDocument.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XmlDocument.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XmlSerializer.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XPath.dll +-r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XPath.XDocument.dll +--target:library +--nowarn:44,57,75,1204,NU5125,IL2121 +--warn:3 +--warnaserror:3239,1182,0025 +--fullpaths +--flaterrors +--highentropyva+ +--targetprofile:netstandard +--nocopyfsharpcore +--deterministic- +--simpleresolution +--nowarn:3384 +--nowarn:75 +--extraoptimizationloops:1 +--warnon:1182 +--warnon:3218 +--warnon:3390 +--simpleresolution +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSComp.fsi +C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSComp.fs +Utilities\Activity.fsi +Utilities\Activity.fs +Utilities\sformat.fsi +Utilities\sformat.fs +Utilities\TaggedCollections.fsi +Utilities\TaggedCollections.fs +Utilities\illib.fsi +Utilities\illib.fs +Utilities\FileSystem.fsi +Utilities\FileSystem.fs +Utilities\zmap.fsi +Utilities\zmap.fs +Utilities\zset.fsi +Utilities\zset.fs +Utilities\XmlAdapters.fsi +Utilities\XmlAdapters.fs +Utilities\InternalCollections.fsi +Utilities\InternalCollections.fs +Utilities\lib.fsi +Utilities\lib.fs +Utilities\range.fsi +Utilities\range.fs +Facilities\LanguageFeatures.fsi +Facilities\LanguageFeatures.fs +Facilities\DiagnosticOptions.fsi +Facilities\DiagnosticOptions.fs +Facilities\DiagnosticsLogger.fsi +Facilities\DiagnosticsLogger.fs +SyntaxTree\PrettyNaming.fsi +SyntaxTree\PrettyNaming.fs +SyntaxTree\XmlDoc.fsi +SyntaxTree\XmlDoc.fs +SyntaxTree\SyntaxTrivia.fsi +SyntaxTree\SyntaxTrivia.fs +SyntaxTree\SyntaxTree.fsi +SyntaxTree\SyntaxTree.fs +SyntaxTree\SyntaxTreeOps.fsi +SyntaxTree\SyntaxTreeOps.fs +Driver\GraphChecking\Continuation.fsi +Driver\GraphChecking\Continuation.fs +Driver\GraphChecking\Types.fsi +Driver\GraphChecking\Types.fs +Driver\GraphChecking\Graph.fsi +Driver\GraphChecking\Graph.fs +Driver\GraphChecking\TrieMapping.fsi +Driver\GraphChecking\TrieMapping.fs +Driver\GraphChecking\FileContentMapping.fsi +Driver\GraphChecking\FileContentMapping.fs +Driver\GraphChecking\DependencyResolution.fsi +Driver\GraphChecking\DependencyResolution.fs +Driver\GraphChecking\GraphProcessing.fsi +Driver\GraphChecking\GraphProcessing.fs +Driver\GraphChecking\TypeCheckingGraphProcessing.fsi +Driver\GraphChecking\TypeCheckingGraphProcessing.fs diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/CompilationFromCmdlineArgsTests.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/CompilationFromCmdlineArgsTests.fs index 4525d7286f2..63b18a33ab7 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/CompilationFromCmdlineArgsTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/CompilationFromCmdlineArgsTests.fs @@ -13,10 +13,12 @@ module CompilationFromCmdlineArgsTests = // The path needs to be absolute. let localProjects: string list = [ - @"C:\Projects\fantomas\src\Fantomas.Core\Fantomas.Core.args.txt" - @"C:\Projects\FsAutoComplete\src\FsAutoComplete\FsAutoComplete.args.txt" - @"C:\Projects\fsharp\src\Compiler\FSharp.Compiler.Service.args.txt" - @"C:\Projects\fsharp\tests\FSharp.Compiler.ComponentTests\FSharp.Compiler.ComponentTests.args.txt" + // @"C:\Projects\fantomas\src\Fantomas.Core\Fantomas.Core.args.txt" + // @"C:\Projects\FsAutoComplete\src\FsAutoComplete\FsAutoComplete.args.txt" + // @"C:\Projects\fsharp\src\Compiler\FSharp.Compiler.Service.args.txt" + // @"C:\Projects\fsharp\tests\FSharp.Compiler.ComponentTests\FSharp.Compiler.ComponentTests.args.txt" + @"C:\Users\nojaf\Projects\fsharp\src\Compiler\FSharp.Compiler.Service.rsp" + @"C:\Users\nojaf\Projects\fsharp\src\Compiler\Trimmed.rsp" ] let checker = FSharpChecker.Create() @@ -36,6 +38,7 @@ module CompilationFromCmdlineArgsTests = if not (Array.contains "--times" argsFromFile) then yield "--times" yield! methodOptions method + yield "--deterministic-" |] let diagnostics, exitCode = checker.Compile(args) |> Async.RunSynchronously From f0cc8ea6e7ca8d7fb4c161a21ffe6bdae53a551d Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 23 Jun 2023 18:30:25 +0200 Subject: [PATCH 064/222] reuse TcIntermediate for ParseAndCheckFile --- src/Compiler/Service/BackgroundCompiler.fs | 2 +- src/Compiler/Service/FSharpCheckerResults.fs | 4 +- src/Compiler/Service/FSharpCheckerResults.fsi | 2 +- src/Compiler/Service/TransparentCompiler.fs | 91 ++++++++++++++++++- .../FSharpChecker/TransparentCompiler.fs | 29 ++++-- 5 files changed, 116 insertions(+), 12 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 3feb20dbebc..586b19d3eaf 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -1030,7 +1030,7 @@ type internal BackgroundCompiler tcProj.TcConfig, tcProj.TcGlobals, options.IsIncompleteTypeCheckEnvironment, - builder, + Some builder, options, Array.ofList tcDependencyFiles, creationDiags, diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 15c866d4f5b..3f80fe59b64 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -3231,7 +3231,7 @@ type FSharpCheckFileResults tcConfig, tcGlobals, isIncompleteTypeCheckEnvironment: bool, - builder: IncrementalBuilder, + builder: IncrementalBuilder option, projectOptions, dependencyFiles, creationErrors: FSharpDiagnostic[], @@ -3272,7 +3272,7 @@ type FSharpCheckFileResults let errors = FSharpCheckFileResults.JoinErrors(isIncompleteTypeCheckEnvironment, creationErrors, parseErrors, tcErrors) - FSharpCheckFileResults(mainInputFileName, errors, Some tcFileInfo, dependencyFiles, Some builder, keepAssemblyContents) + FSharpCheckFileResults(mainInputFileName, errors, Some tcFileInfo, dependencyFiles, builder, keepAssemblyContents) static member CheckOneFile ( diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index d3ee5fc4ea5..deec695ce10 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -564,7 +564,7 @@ type public FSharpCheckFileResults = tcConfig: TcConfig * tcGlobals: TcGlobals * isIncompleteTypeCheckEnvironment: bool * - builder: IncrementalBuilder * + builder: IncrementalBuilder option * projectOptions: FSharpProjectOptions * dependencyFiles: string[] * creationErrors: FSharpDiagnostic[] * diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 15a9c53762b..465005790be 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -87,7 +87,7 @@ type internal TcIntermediate = tcDiagnosticsRev: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] list tcDependencyFiles: string list - + sink: TcResultsSinkImpl } @@ -989,7 +989,7 @@ type internal TransparentCompiler let parseTree = EmptyParsedInput(fileName, (false, false)) FSharpParseFileResults(diagnostics, parseTree, true, [||]) - let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = + let ComputeParseAndCheckFileInProject' (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = let key = fileName, projectSnapshot.Key ParseAndCheckFileInProjectCache.Get( @@ -1037,6 +1037,93 @@ type internal TransparentCompiler } ) + let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = + let key = fileName, projectSnapshot.Key + + ParseAndCheckFileInProjectCache.Get( + key, + node { + + use _ = + Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + + match! ComputeBootstrapInfo projectSnapshot with + | None, creationDiags -> return emptyParseResult fileName creationDiags, FSharpCheckFileAnswer.Aborted + + | Some bootstrapInfo, creationDiags -> + + let file = bootstrapInfo.GetFile fileName + + let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file + let! parseResults, _sourceText = getParseResult bootstrapInfo creationDiags fileName + + let! priorTcInfo, graph = ComputeTcPrior file bootstrapInfo projectSnapshot + + let fileIndex = projectSnapshot.IndexOf fileName + let! tcIntermediate = ComputeTcIntermediate projectSnapshot graph fileIndex (parseTree, parseDiagnostics) bootstrapInfo priorTcInfo + + let (tcEnv, _topAttribs, checkedImplFileOpt, ccuSigForFile), tcState = + priorTcInfo.tcState |> tcIntermediate.finisher.Invoke + + let sink = tcIntermediate.sink + + let tcResolutions = sink.GetResolutions() + let tcSymbolUses = sink.GetSymbolUses() + let tcOpenDeclarations = sink.GetOpenDeclarations() + + let tcDependencyFiles = [] // TODO add as a set to TcIntermediate + let tcDiagnostics = seq { + yield! priorTcInfo.TcDiagnostics + for x in tcIntermediate.tcDiagnosticsRev + do yield! x } + + let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions + + let tcDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + diagnosticsOptions, + false, + fileName, + tcDiagnostics, + suggestNamesForErrors, + bootstrapInfo.TcConfig.flatErrors + ) + + let tcDiagnostics = [| yield! creationDiags; yield! tcDiagnostics |] + + let loadClosure = None // TODO: script support + + let typedResults = + FSharpCheckFileResults.Make( + fileName, + projectSnapshot.ProjectFileName, + bootstrapInfo.TcConfig, + bootstrapInfo.TcGlobals, + projectSnapshot.IsIncompleteTypeCheckEnvironment, + None, + projectSnapshot.ToOptions(), + Array.ofList tcDependencyFiles, + creationDiags, + parseResults.Diagnostics, + tcDiagnostics, + keepAssemblyContents, + ccuSigForFile, + tcState.Ccu, + bootstrapInfo.TcImports, + tcEnv.AccessRights, + tcResolutions, + tcSymbolUses, + tcEnv.NameEnv, + loadClosure, + checkedImplFileOpt, + tcOpenDeclarations + ) + + return (parseResults, FSharpCheckFileAnswer.Succeeded typedResults) + } + ) + + let ComputeParseAndCheckAllFilesInProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = ParseAndCheckAllFilesInProjectCache.Get( projectSnapshot.Key, diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 3dd03477a8f..fbe69c42d0c 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -134,6 +134,21 @@ let ``Files depend on signature file if present`` () = checkFile "Second" expectNoChanges } +[] +let ``Project with signatures`` () = + + let project = SyntheticProject.Create( + { sourceFile "First" [] with + Source = "let f (x: int) = x" + SignatureFile = AutoGenerated }, + { sourceFile "Second" ["First"] with + Source = "let a x = ModuleFirst.f x" + SignatureFile = AutoGenerated }) + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + checkFile "Second" expectOk + } + [] let ``Signature update`` () = @@ -291,7 +306,7 @@ let ``Files that are not depended on don't invalidate cache part 2`` () = | _ -> None) |> Seq.groupBy fst |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) - |> Map + |> Seq.toList let graphConstructions = cacheEvents @@ -300,11 +315,10 @@ let ``Files that are not depended on don't invalidate cache part 2`` () = | _ -> None) |> Seq.groupBy fst |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) - |> Map - - Assert.Equal([Started; Finished], graphConstructions["FileE.fs"]) + |> Seq.toList - Assert.Equal([], intermediateTypeChecks |> Map.toList) + Assert.Equal(["FileE.fs", [Started; Finished]], graphConstructions) + Assert.Equal(["FileE.fs", [Started; Finished]], intermediateTypeChecks) [] @@ -315,7 +329,10 @@ let ``Multi-project`` signatureFiles = let sigFile = if signatureFiles then AutoGenerated else No let library = SyntheticProject.Create("library", - { sourceFile "LibA" [] with SignatureFile = sigFile } + { sourceFile "LibA" [] + with + Source = "let f (x: int) = x" + SignatureFile = sigFile } //, //{ sourceFile "LibB" ["LibA"] with SignatureFile = sigFile }, //{ sourceFile "LibC" ["LibA"] with SignatureFile = sigFile }, From 1d8aa139e2e200c5f54e91fe344e4b98323fe1c4 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 26 Jun 2023 11:50:53 +0200 Subject: [PATCH 065/222] Sort resultsToAdd when folding the firstState. --- .../TypeCheckingGraphProcessing.fs | 22 +- .../TypeCheckingGraphProcessing.fsi | 5 +- src/Compiler/Driver/GraphChecking/Types.fs | 2 +- src/Compiler/Driver/GraphChecking/Types.fsi | 2 +- src/Compiler/Driver/ParseAndCheckInputs.fs | 156 +++++++----- src/Compiler/Trimmed.rsp | 226 ------------------ 6 files changed, 114 insertions(+), 299 deletions(-) delete mode 100644 src/Compiler/Trimmed.rsp diff --git a/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fs b/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fs index 7f184a58880..52318982d2d 100644 --- a/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fs +++ b/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fs @@ -14,14 +14,16 @@ open System.Threading /// Direct dependencies of a node /// Transitive dependencies of a node /// A way to fold a single result into existing state +/// ... /// /// Similar to 'processFileGraph', this function is generic yet specific to the type-checking process. /// let combineResults (emptyState: 'State) - (deps: ProcessedNode<'Item, 'State * Finisher<'State, 'FinalFileResult>>[]) - (transitiveDeps: ProcessedNode<'Item, 'State * Finisher<'State, 'FinalFileResult>>[]) - (folder: 'State -> Finisher<'State, 'FinalFileResult> -> 'State) + (deps: ProcessedNode<'Item, 'State * Finisher<'ChosenItem, 'State, 'FinalFileResult>>[]) + (transitiveDeps: ProcessedNode<'Item, 'State * Finisher<'ChosenItem, 'State, 'FinalFileResult>>[]) + (sortResultsToAdd: 'Item -> 'Item -> int) + (folder: 'State -> Finisher<'ChosenItem, 'State, 'FinalFileResult> -> 'State) : 'State = match deps with | [||] -> emptyState @@ -49,6 +51,7 @@ let combineResults transitiveDeps |> Array.filter (fun dep -> itemsPresent.Contains dep.Info.Item = false) |> Array.distinctBy (fun dep -> dep.Info.Item) + |> Array.sortWith (fun a b -> sortResultsToAdd a.Info.Item b.Info.Item) |> Array.map (fun dep -> dep.Result |> snd) // Fold results not already included and produce the final state @@ -63,8 +66,9 @@ let combineResults /// let processTypeCheckingGraph<'Item, 'ChosenItem, 'State, 'FinalFileResult when 'Item: equality and 'Item: comparison> (graph: Graph<'Item>) - (work: 'Item -> 'State -> Finisher<'State, 'FinalFileResult>) - (folder: 'State -> Finisher<'State, 'FinalFileResult> -> 'FinalFileResult * 'State) + (work: 'Item -> 'State -> Finisher<'ChosenItem, 'State, 'FinalFileResult>) + (sortResultsToAdd: 'Item -> 'Item -> int) + (folder: 'State -> Finisher<'ChosenItem, 'State, 'FinalFileResult> -> 'FinalFileResult * 'State) // Decides whether a result for an item should be included in the final state, and how to map the item if it should. (finalStateChooser: 'Item -> 'ChosenItem option) (emptyState: 'State) @@ -72,9 +76,9 @@ let processTypeCheckingGraph<'Item, 'ChosenItem, 'State, 'FinalFileResult when ' : ('ChosenItem * 'FinalFileResult) list * 'State = let workWrapper - (getProcessedNode: 'Item -> ProcessedNode<'Item, 'State * Finisher<'State, 'FinalFileResult>>) + (getProcessedNode: 'Item -> ProcessedNode<'Item, 'State * Finisher<'ChosenItem, 'State, 'FinalFileResult>>) (node: NodeInfo<'Item>) - : 'State * Finisher<'State, 'FinalFileResult> = + : 'State * Finisher<'ChosenItem, 'State, 'FinalFileResult> = let folder x y = folder x y |> snd let deps = node.Deps |> Array.except [| node.Item |] |> Array.map getProcessedNode @@ -83,7 +87,9 @@ let processTypeCheckingGraph<'Item, 'ChosenItem, 'State, 'FinalFileResult when ' |> Array.except [| node.Item |] |> Array.map getProcessedNode - let inputState = combineResults emptyState deps transitiveDeps folder + let inputState = + combineResults emptyState deps transitiveDeps sortResultsToAdd folder + let singleRes = work node.Item inputState let state = folder inputState singleRes state, singleRes diff --git a/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fsi b/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fsi index 5db01dc3307..07f1d003ffb 100644 --- a/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fsi +++ b/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fsi @@ -9,8 +9,9 @@ open System.Threading /// val processTypeCheckingGraph<'Item, 'ChosenItem, 'State, 'FinalFileResult when 'Item: equality and 'Item: comparison> : graph: Graph<'Item> -> - work: ('Item -> 'State -> Finisher<'State, 'FinalFileResult>) -> - folder: ('State -> Finisher<'State, 'FinalFileResult> -> 'FinalFileResult * 'State) -> + work: ('Item -> 'State -> Finisher<'ChosenItem, 'State, 'FinalFileResult>) -> + sortResultsToAdd: ('Item -> 'Item -> int) -> + folder: ('State -> Finisher<'ChosenItem, 'State, 'FinalFileResult> -> 'FinalFileResult * 'State) -> finalStateChooser: ('Item -> 'ChosenItem option) -> emptyState: 'State -> ct: CancellationToken -> diff --git a/src/Compiler/Driver/GraphChecking/Types.fs b/src/Compiler/Driver/GraphChecking/Types.fs index c0e8e0f84b1..04359519b95 100644 --- a/src/Compiler/Driver/GraphChecking/Types.fs +++ b/src/Compiler/Driver/GraphChecking/Types.fs @@ -167,4 +167,4 @@ type internal FilePairMap(files: FileInProject array) = member x.IsSignature(index: FileIndex) = Map.containsKey index sigToImpl /// Callback that returns a previously calculated 'Result and updates 'State accordingly. -type internal Finisher<'State, 'Result> = delegate of 'State -> 'Result * 'State +type internal Finisher<'Node, 'State, 'Result> = Finisher of node: 'Node * finisher: ('State -> 'Result * 'State) diff --git a/src/Compiler/Driver/GraphChecking/Types.fsi b/src/Compiler/Driver/GraphChecking/Types.fsi index 7d0ba9bbdd5..67403d51d98 100644 --- a/src/Compiler/Driver/GraphChecking/Types.fsi +++ b/src/Compiler/Driver/GraphChecking/Types.fsi @@ -114,4 +114,4 @@ type internal FilePairMap = member IsSignature: index: FileIndex -> bool /// Callback that returns a previously calculated 'Result and updates 'State accordingly. -type internal Finisher<'State, 'Result> = delegate of 'State -> 'Result * 'State +type internal Finisher<'Node, 'State, 'Result> = Finisher of node: 'Node * finisher: ('State -> 'Result * 'State) diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index dfdbdf682d4..e85f4db1cc0 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1472,11 +1472,12 @@ type NodeToTypeCheck = /// Even though the actual implementation file was not type-checked. | ArtificialImplFile of signatureFileIndex: FileIndex -let folder (state: State) (finisher: Finisher) : FinalFileResult * State = finisher.Invoke(state) +let folder (state: State) (Finisher (finisher = finisher)) : FinalFileResult * State = finisher state /// Typecheck a single file (or interactive entry into F# Interactive) /// a callback functions that takes a `TcState` and will add the checked result to it. let CheckOneInputWithCallback + (idx: int) ((checkForErrors, tcConfig: TcConfig, tcImports: TcImports, @@ -1486,7 +1487,7 @@ let CheckOneInputWithCallback tcState: TcState, inp: ParsedInput, _skipImplIfSigExists: bool): (unit -> bool) * TcConfig * TcImports * TcGlobals * LongIdent option * TcResultsSink * TcState * ParsedInput * bool) - : Cancellable> = + : Cancellable> = cancellable { try CheckSimulateException tcConfig @@ -1535,25 +1536,33 @@ let CheckOneInputWithCallback TcOpenModuleOrNamespaceDecl tcSink tcGlobals amap m tcEnv (prefixPath, m) return - Finisher(fun tcState -> - let rootSigs = Zmap.add qualNameOfFile sigFileType tcState.tcsRootSigs + Finisher( + idx, + (fun tcState -> + if tcState.tcsRootSigs.ContainsKey qualNameOfFile then + printfn $"{qualNameOfFile} already part of root sigs?" + () - let tcSigEnv = - AddLocalRootModuleOrNamespace TcResultsSink.NoSink tcGlobals amap m tcState.tcsTcSigEnv sigFileType + let rootSigs = Zmap.add qualNameOfFile sigFileType tcState.tcsRootSigs - // Add the signature to the signature env (unless it had an explicit signature) - let ccuSigForFile = CombineCcuContentFragments [ sigFileType; tcState.tcsCcuSig ] + let tcSigEnv = + AddLocalRootModuleOrNamespace TcResultsSink.NoSink tcGlobals amap m tcState.tcsTcSigEnv sigFileType - let partialResult = tcEnv, EmptyTopAttrs, None, ccuSigForFile + // Add the signature to the signature env (unless it had an explicit signature) + let ccuSigForFile = CombineCcuContentFragments [ sigFileType; tcState.tcsCcuSig ] - let tcState = - { tcState with - tcsTcSigEnv = tcSigEnv - tcsRootSigs = rootSigs - tcsCreatesGeneratedProvidedTypes = tcState.tcsCreatesGeneratedProvidedTypes || createsGeneratedProvidedTypes - } + let partialResult = tcEnv, EmptyTopAttrs, None, ccuSigForFile - partialResult, tcState) + let tcState = + { tcState with + tcsTcSigEnv = tcSigEnv + tcsRootSigs = rootSigs + tcsCreatesGeneratedProvidedTypes = + tcState.tcsCreatesGeneratedProvidedTypes || createsGeneratedProvidedTypes + } + + partialResult, tcState) + ) | ParsedInput.ImplFile file -> let qualNameOfFile = file.QualifiedName @@ -1579,29 +1588,32 @@ let CheckOneInputWithCallback ) return - Finisher(fun tcState -> - // Check if we've already seen an implementation for this fragment - if Zset.contains qualNameOfFile tcState.tcsRootImpls then - errorR (Error(FSComp.SR.buildImplementationAlreadyGiven (qualNameOfFile.Text), m)) - - let ccuSigForFile, fsTcState = - AddCheckResultsToTcState - (tcGlobals, amap, false, prefixPathOpt, tcSink, tcState.tcsTcImplEnv, qualNameOfFile, implFile.Signature) - tcState - - let partialResult = tcEnvAtEnd, topAttrs, Some implFile, ccuSigForFile - - let tcState = - { fsTcState with - tcsCreatesGeneratedProvidedTypes = - fsTcState.tcsCreatesGeneratedProvidedTypes || createsGeneratedProvidedTypes - } - - partialResult, tcState) + Finisher( + idx, + (fun tcState -> + // Check if we've already seen an implementation for this fragment + if Zset.contains qualNameOfFile tcState.tcsRootImpls then + errorR (Error(FSComp.SR.buildImplementationAlreadyGiven (qualNameOfFile.Text), m)) + + let ccuSigForFile, fsTcState = + AddCheckResultsToTcState + (tcGlobals, amap, false, prefixPathOpt, tcSink, tcState.tcsTcImplEnv, qualNameOfFile, implFile.Signature) + tcState + + let partialResult = tcEnvAtEnd, topAttrs, Some implFile, ccuSigForFile + + let tcState = + { fsTcState with + tcsCreatesGeneratedProvidedTypes = + fsTcState.tcsCreatesGeneratedProvidedTypes || createsGeneratedProvidedTypes + } + + partialResult, tcState) + ) with e -> errorRecovery e range0 - return Finisher(fun tcState -> (tcState.TcEnvFromSignatures, EmptyTopAttrs, None, tcState.tcsCcuSig), tcState) + return Finisher(idx, (fun tcState -> (tcState.TcEnvFromSignatures, EmptyTopAttrs, None, tcState.tcsCcuSig), tcState)) } let AddSignatureResultToTcImplEnv (tcImports: TcImports, tcGlobals, prefixPathOpt, tcSink, tcState, input: ParsedInput) = @@ -1711,38 +1723,52 @@ let CheckMultipleInputsUsingGraphMode // somewhere in the files processed prior to each one, or in the processing of this particular file. let priorErrors = checkForErrors () - let processArtificialImplFile (input: ParsedInput) ((currentTcState, _currentPriorErrors): State) : Finisher = - Finisher(fun (state: State) -> - let tcState, currentPriorErrors = state - - let f = - // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. - AddSignatureResultToTcImplEnv(tcImports, tcGlobals, prefixPathOpt, TcResultsSink.NoSink, currentTcState, input) - - // The `partialResult` will be excluded at the end of `GraphProcessing.processGraph`. - // The important thing is that `nextTcState` will populated the necessary information to TcEnvFromImpls. - let partialResult, nextTcState = f tcState - partialResult, (nextTcState, currentPriorErrors)) + let processArtificialImplFile + (idx: int) + (input: ParsedInput) + ((currentTcState, _currentPriorErrors): State) + : Finisher = + Finisher( + idx, + (fun (state: State) -> + let tcState, currentPriorErrors = state + + let f = + // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. + AddSignatureResultToTcImplEnv(tcImports, tcGlobals, prefixPathOpt, TcResultsSink.NoSink, currentTcState, input) + + // The `partialResult` will be excluded at the end of `GraphProcessing.processGraph`. + // The important thing is that `nextTcState` will populated the necessary information to TcEnvFromImpls. + let partialResult, nextTcState = f tcState + partialResult, (nextTcState, currentPriorErrors)) + ) let processFile + (idx: int) ((input, logger): ParsedInput * DiagnosticsLogger) ((currentTcState, _currentPriorErrors): State) - : Finisher = + : Finisher = use _ = UseDiagnosticsLogger logger let checkForErrors2 () = priorErrors || (logger.ErrorCount > 0) let tcSink = TcResultsSink.NoSink - let finisher = - CheckOneInputWithCallback(checkForErrors2, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcSink, currentTcState, input, false) + let (Finisher (finisher = finisher)) = + CheckOneInputWithCallback + idx + (checkForErrors2, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcSink, currentTcState, input, false) |> Cancellable.runWithoutCancellation - Finisher(fun (state: State) -> - let tcState, priorErrors = state - let (partialResult: PartialResult, tcState) = finisher.Invoke(tcState) - let hasErrors = logger.ErrorCount > 0 - let priorOrCurrentErrors = priorErrors || hasErrors - let state: State = tcState, priorOrCurrentErrors - partialResult, state) + Finisher( + idx, + (fun (state: State) -> + printfn "Finisher for %s" input.FileName + let tcState, priorErrors = state + let (partialResult: PartialResult, tcState) = finisher tcState + let hasErrors = logger.ErrorCount > 0 + let priorOrCurrentErrors = priorErrors || hasErrors + let state: State = tcState, priorOrCurrentErrors + partialResult, state) + ) UseMultipleDiagnosticLoggers (inputs, diagnosticsLogger, Some eagerFormat) (fun inputsWithLoggers -> // Equip loggers to locally filter w.r.t. scope pragmas in each input @@ -1753,14 +1779,14 @@ let CheckMultipleInputsUsingGraphMode let logger = DiagnosticsLoggerForInput(tcConfig, input, oldLogger) input, logger) - let processFile (node: NodeToTypeCheck) (state: State) : Finisher = + let processFile (node: NodeToTypeCheck) (state: State) : Finisher = match node with | NodeToTypeCheck.ArtificialImplFile idx -> let parsedInput, _ = inputsWithLoggers[idx] - processArtificialImplFile parsedInput state + processArtificialImplFile idx parsedInput state | NodeToTypeCheck.PhysicalFile idx -> let parsedInput, logger = inputsWithLoggers[idx] - processFile (parsedInput, logger) state + processFile idx (parsedInput, logger) state let state: State = tcState, priorErrors @@ -1769,10 +1795,18 @@ let CheckMultipleInputsUsingGraphMode | NodeToTypeCheck.ArtificialImplFile _ -> None | NodeToTypeCheck.PhysicalFile file -> Some file + let sortFn (a: NodeToTypeCheck) (b: NodeToTypeCheck) : int = + match a, b with + | NodeToTypeCheck.PhysicalFile aIdx, NodeToTypeCheck.PhysicalFile bIdx + | NodeToTypeCheck.ArtificialImplFile aIdx, NodeToTypeCheck.ArtificialImplFile bIdx -> aIdx.CompareTo bIdx + | NodeToTypeCheck.PhysicalFile _, NodeToTypeCheck.ArtificialImplFile _ -> -1 + | NodeToTypeCheck.ArtificialImplFile _, NodeToTypeCheck.PhysicalFile _ -> 1 + let partialResults, (tcState, _) = TypeCheckingGraphProcessing.processTypeCheckingGraph nodeGraph processFile + sortFn folder finalStateItemChooser state diff --git a/src/Compiler/Trimmed.rsp b/src/Compiler/Trimmed.rsp deleted file mode 100644 index a981f6d5be2..00000000000 --- a/src/Compiler/Trimmed.rsp +++ /dev/null @@ -1,226 +0,0 @@ --o:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSharp.Compiler.Service.dll --g ---nologo ---debug:embedded ---noframework ---define:TRACE ---define:COMPILER ---define:FSHARPCORE_USE_PACKAGE ---define:DEBUG ---define:NETSTANDARD ---define:FX_NO_WINFORMS ---define:Debug ---define:NETSTANDARD ---define:NETSTANDARD2_0 ---define:NETSTANDARD1_0_OR_GREATER ---define:NETSTANDARD1_1_OR_GREATER ---define:NETSTANDARD1_2_OR_GREATER ---define:NETSTANDARD1_3_OR_GREATER ---define:NETSTANDARD1_4_OR_GREATER ---define:NETSTANDARD1_5_OR_GREATER ---define:NETSTANDARD1_6_OR_GREATER ---define:NETSTANDARD2_0_OR_GREATER ---doc:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSharp.Compiler.Service.xml ---optimize- ---resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSComp.resources ---resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSIstrings.resources ---resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\UtilsStrings.resources ---resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSStrings.resources --r:C:\Users\nojaf\.nuget\packages\fsharp.core\7.0.0\lib\netstandard2.0\FSharp.Core.dll --r:C:\Users\nojaf\Projects\fsharp\artifacts\bin\FSharp.DependencyManager.Nuget\Debug\netstandard2.0\FSharp.DependencyManager.Nuget.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\Microsoft.Win32.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\mscorlib.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.AppContext.dll --r:C:\Users\nojaf\.nuget\packages\system.buffers\4.5.1\ref\netstandard2.0\System.Buffers.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.Concurrent.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.dll --r:C:\Users\nojaf\.nuget\packages\system.collections.immutable\6.0.0\lib\netstandard2.0\System.Collections.Immutable.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.NonGeneric.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.Specialized.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.Composition.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.EventBasedAsync.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.TypeConverter.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Console.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Core.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Data.Common.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Data.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Contracts.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Debug.dll --r:C:\Users\nojaf\.nuget\packages\system.diagnostics.diagnosticsource\7.0.0\lib\netstandard2.0\System.Diagnostics.DiagnosticSource.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.FileVersionInfo.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Process.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.StackTrace.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.TextWriterTraceListener.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Tools.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.TraceSource.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Tracing.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Drawing.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Drawing.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Dynamic.Runtime.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.Calendars.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.Extensions.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.FileSystem.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.ZipFile.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.DriveInfo.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.Watcher.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.IsolatedStorage.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.MemoryMappedFiles.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Pipes.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.UnmanagedMemoryStream.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Expressions.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Parallel.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Queryable.dll --r:C:\Users\nojaf\.nuget\packages\system.memory\4.5.5\lib\netstandard2.0\System.Memory.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Http.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.NameResolution.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.NetworkInformation.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Ping.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Requests.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Security.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Sockets.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebHeaderCollection.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebSockets.Client.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebSockets.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Numerics.dll --r:C:\Users\nojaf\.nuget\packages\system.numerics.vectors\4.4.0\ref\netstandard2.0\System.Numerics.Vectors.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ObjectModel.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Reflection.dll --r:C:\Users\nojaf\.nuget\packages\system.reflection.emit\4.7.0\ref\netstandard2.0\System.Reflection.Emit.dll --r:C:\Users\nojaf\.nuget\packages\system.reflection.emit.ilgeneration\4.7.0\ref\netstandard2.0\System.Reflection.Emit.ILGeneration.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Reflection.Extensions.dll --r:C:\Users\nojaf\.nuget\packages\system.reflection.metadata\6.0.1\lib\netstandard2.0\System.Reflection.Metadata.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Reflection.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.Reader.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.ResourceManager.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.Writer.dll --r:C:\Users\nojaf\.nuget\packages\system.runtime.compilerservices.unsafe\6.0.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.CompilerServices.VisualC.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Extensions.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Handles.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.InteropServices.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.InteropServices.RuntimeInformation.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Numerics.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Formatters.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Json.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Xml.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Claims.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Algorithms.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Csp.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Encoding.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.X509Certificates.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Principal.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.SecureString.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ServiceModel.Web.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Text.Encoding.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Text.Encoding.Extensions.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Text.RegularExpressions.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Overlapped.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Tasks.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Tasks.Parallel.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Thread.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.ThreadPool.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Timer.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Transactions.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ValueTuple.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Web.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Windows.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.Linq.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.ReaderWriter.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.Serialization.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XDocument.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XmlDocument.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XmlSerializer.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XPath.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XPath.XDocument.dll ---target:library ---nowarn:44,57,75,1204,NU5125,IL2121 ---warn:3 ---warnaserror:3239,1182,0025 ---fullpaths ---flaterrors ---highentropyva+ ---targetprofile:netstandard ---nocopyfsharpcore ---deterministic- ---simpleresolution ---nowarn:3384 ---nowarn:75 ---extraoptimizationloops:1 ---warnon:1182 ---warnon:3218 ---warnon:3390 ---simpleresolution -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSComp.fsi -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSComp.fs -Utilities\Activity.fsi -Utilities\Activity.fs -Utilities\sformat.fsi -Utilities\sformat.fs -Utilities\TaggedCollections.fsi -Utilities\TaggedCollections.fs -Utilities\illib.fsi -Utilities\illib.fs -Utilities\FileSystem.fsi -Utilities\FileSystem.fs -Utilities\zmap.fsi -Utilities\zmap.fs -Utilities\zset.fsi -Utilities\zset.fs -Utilities\XmlAdapters.fsi -Utilities\XmlAdapters.fs -Utilities\InternalCollections.fsi -Utilities\InternalCollections.fs -Utilities\lib.fsi -Utilities\lib.fs -Utilities\range.fsi -Utilities\range.fs -Facilities\LanguageFeatures.fsi -Facilities\LanguageFeatures.fs -Facilities\DiagnosticOptions.fsi -Facilities\DiagnosticOptions.fs -Facilities\DiagnosticsLogger.fsi -Facilities\DiagnosticsLogger.fs -SyntaxTree\PrettyNaming.fsi -SyntaxTree\PrettyNaming.fs -SyntaxTree\XmlDoc.fsi -SyntaxTree\XmlDoc.fs -SyntaxTree\SyntaxTrivia.fsi -SyntaxTree\SyntaxTrivia.fs -SyntaxTree\SyntaxTree.fsi -SyntaxTree\SyntaxTree.fs -SyntaxTree\SyntaxTreeOps.fsi -SyntaxTree\SyntaxTreeOps.fs -Driver\GraphChecking\Continuation.fsi -Driver\GraphChecking\Continuation.fs -Driver\GraphChecking\Types.fsi -Driver\GraphChecking\Types.fs -Driver\GraphChecking\Graph.fsi -Driver\GraphChecking\Graph.fs -Driver\GraphChecking\TrieMapping.fsi -Driver\GraphChecking\TrieMapping.fs -Driver\GraphChecking\FileContentMapping.fsi -Driver\GraphChecking\FileContentMapping.fs -Driver\GraphChecking\DependencyResolution.fsi -Driver\GraphChecking\DependencyResolution.fs -Driver\GraphChecking\GraphProcessing.fsi -Driver\GraphChecking\GraphProcessing.fs -Driver\GraphChecking\TypeCheckingGraphProcessing.fsi -Driver\GraphChecking\TypeCheckingGraphProcessing.fs From 0203625a498cf30f81c4fff2a59ec703378c4460 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 26 Jun 2023 11:55:32 +0200 Subject: [PATCH 066/222] Undo temporary changes. --- src/Compiler/Checking/CheckDeclarations.fs | 3 - src/Compiler/FSharp.Compiler.Service.rsp | 499 ------------------ src/Compiler/SyntaxTree/PrettyNaming.fs | 1 + src/Compiler/SyntaxTree/SyntaxTree.fs | 2 +- src/Compiler/SyntaxTree/SyntaxTreeOps.fs | 108 ++-- src/Compiler/SyntaxTree/XmlDoc.fs | 1 + src/Compiler/SyntaxTree/XmlDoc.fsi | 1 + .../Graph/CompilationFromCmdlineArgsTests.fs | 11 +- 8 files changed, 62 insertions(+), 564 deletions(-) delete mode 100644 src/Compiler/FSharp.Compiler.Service.rsp diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index a5a3dcd8dd1..7704b14266b 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -4661,9 +4661,6 @@ let rec TcSignatureElementNonMutRec (cenv: cenv) parent typeNames endm (env: TcE match TryStripPrefixPath g enclosingNamespacePath with | Some(p, _) -> TcOpenModuleOrNamespaceDecl cenv.tcSink g cenv.amap m.EndRange env ([p], m.EndRange) | None -> env, [] - - if m.FileName.EndsWith("Continuation.fsi") then - () // Publish the combined module type env.eModuleOrNamespaceTypeAccumulator.Value <- diff --git a/src/Compiler/FSharp.Compiler.Service.rsp b/src/Compiler/FSharp.Compiler.Service.rsp deleted file mode 100644 index f42fd83c989..00000000000 --- a/src/Compiler/FSharp.Compiler.Service.rsp +++ /dev/null @@ -1,499 +0,0 @@ --o:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSharp.Compiler.Service.dll --g ---debug:embedded ---noframework ---define:TRACE ---define:COMPILER ---define:FSHARPCORE_USE_PACKAGE ---define:DEBUG ---define:NETSTANDARD ---define:FX_NO_WINFORMS ---define:Debug ---define:NETSTANDARD ---define:NETSTANDARD2_0 ---define:NETSTANDARD1_0_OR_GREATER ---define:NETSTANDARD1_1_OR_GREATER ---define:NETSTANDARD1_2_OR_GREATER ---define:NETSTANDARD1_3_OR_GREATER ---define:NETSTANDARD1_4_OR_GREATER ---define:NETSTANDARD1_5_OR_GREATER ---define:NETSTANDARD1_6_OR_GREATER ---define:NETSTANDARD2_0_OR_GREATER ---doc:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSharp.Compiler.Service.xml ---optimize- ---resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSComp.resources ---resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSIstrings.resources ---resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\UtilsStrings.resources ---resource:C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSStrings.resources --r:C:\Users\nojaf\.nuget\packages\fsharp.core\7.0.0\lib\netstandard2.0\FSharp.Core.dll --r:C:\Users\nojaf\Projects\fsharp\artifacts\bin\FSharp.DependencyManager.Nuget\Debug\netstandard2.0\FSharp.DependencyManager.Nuget.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\Microsoft.Win32.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\mscorlib.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.AppContext.dll --r:C:\Users\nojaf\.nuget\packages\system.buffers\4.5.1\ref\netstandard2.0\System.Buffers.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.Concurrent.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.dll --r:C:\Users\nojaf\.nuget\packages\system.collections.immutable\6.0.0\lib\netstandard2.0\System.Collections.Immutable.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.NonGeneric.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.Specialized.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.Composition.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.EventBasedAsync.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.TypeConverter.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Console.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Core.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Data.Common.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Data.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Contracts.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Debug.dll --r:C:\Users\nojaf\.nuget\packages\system.diagnostics.diagnosticsource\7.0.0\lib\netstandard2.0\System.Diagnostics.DiagnosticSource.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.FileVersionInfo.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Process.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.StackTrace.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.TextWriterTraceListener.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Tools.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.TraceSource.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Tracing.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Drawing.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Drawing.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Dynamic.Runtime.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.Calendars.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.Extensions.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.FileSystem.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.ZipFile.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.DriveInfo.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.Watcher.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.IsolatedStorage.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.MemoryMappedFiles.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Pipes.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.UnmanagedMemoryStream.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Expressions.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Parallel.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Queryable.dll --r:C:\Users\nojaf\.nuget\packages\system.memory\4.5.5\lib\netstandard2.0\System.Memory.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Http.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.NameResolution.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.NetworkInformation.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Ping.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Requests.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Security.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Sockets.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebHeaderCollection.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebSockets.Client.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebSockets.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Numerics.dll --r:C:\Users\nojaf\.nuget\packages\system.numerics.vectors\4.4.0\ref\netstandard2.0\System.Numerics.Vectors.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ObjectModel.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Reflection.dll --r:C:\Users\nojaf\.nuget\packages\system.reflection.emit\4.7.0\ref\netstandard2.0\System.Reflection.Emit.dll --r:C:\Users\nojaf\.nuget\packages\system.reflection.emit.ilgeneration\4.7.0\ref\netstandard2.0\System.Reflection.Emit.ILGeneration.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Reflection.Extensions.dll --r:C:\Users\nojaf\.nuget\packages\system.reflection.metadata\6.0.1\lib\netstandard2.0\System.Reflection.Metadata.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Reflection.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.Reader.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.ResourceManager.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.Writer.dll --r:C:\Users\nojaf\.nuget\packages\system.runtime.compilerservices.unsafe\6.0.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.CompilerServices.VisualC.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Extensions.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Handles.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.InteropServices.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.InteropServices.RuntimeInformation.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Numerics.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Formatters.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Json.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.Serialization.Xml.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Claims.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Algorithms.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Csp.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Encoding.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Primitives.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.X509Certificates.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Principal.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.SecureString.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ServiceModel.Web.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Text.Encoding.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Text.Encoding.Extensions.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Text.RegularExpressions.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Overlapped.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Tasks.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Tasks.Parallel.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Thread.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.ThreadPool.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Timer.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Transactions.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ValueTuple.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Web.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Windows.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.Linq.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.ReaderWriter.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.Serialization.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XDocument.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XmlDocument.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XmlSerializer.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XPath.dll --r:C:\Users\nojaf\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XPath.XDocument.dll ---target:library ---nowarn:44,57,75,1204,NU5125,IL2121 ---warn:3 ---warnaserror:3239,1182,0025 ---fullpaths ---flaterrors ---highentropyva+ ---targetprofile:netstandard ---nocopyfsharpcore ---deterministic+ ---simpleresolution ---nowarn:3384 ---times ---nowarn:75 ---extraoptimizationloops:1 ---warnon:1182 ---warnon:3218 ---warnon:3390 ---simpleresolution -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\buildproperties.fs -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSComp.fs -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSIstrings.fs -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\UtilsStrings.fs -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\.NETStandard,Version=v2.0.AssemblyAttributes.fs -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service/Debug/netstandard2.0\FSharp.Compiler.Service.AssemblyInfo.fs -Utilities\Activity.fsi -Utilities\Activity.fs -Utilities\sformat.fsi -Utilities\sformat.fs -Utilities\sr.fsi -Utilities\sr.fs -Utilities\ResizeArray.fsi -Utilities\ResizeArray.fs -Utilities\HashMultiMap.fsi -Utilities\HashMultiMap.fs -Utilities\EditDistance.fsi -Utilities\EditDistance.fs -Utilities\TaggedCollections.fsi -Utilities\TaggedCollections.fs -Utilities\illib.fsi -Utilities\illib.fs -Utilities\FileSystem.fsi -Utilities\FileSystem.fs -Utilities\ildiag.fsi -Utilities\ildiag.fs -Utilities\zmap.fsi -Utilities\zmap.fs -Utilities\zset.fsi -Utilities\zset.fs -Utilities\XmlAdapters.fsi -Utilities\XmlAdapters.fs -Utilities\InternalCollections.fsi -Utilities\InternalCollections.fs -Utilities\QueueList.fsi -Utilities\QueueList.fs -Utilities\lib.fsi -Utilities\lib.fs -Utilities\ImmutableArray.fsi -Utilities\ImmutableArray.fs -Utilities\rational.fsi -Utilities\rational.fs -Utilities\PathMap.fsi -Utilities\PathMap.fs -Utilities\RidHelpers.fs -Utilities\range.fsi -Utilities\range.fs -Facilities\LanguageFeatures.fsi -Facilities\LanguageFeatures.fs -Facilities\DiagnosticOptions.fsi -Facilities\DiagnosticOptions.fs -Facilities\TextLayoutRender.fsi -Facilities\TextLayoutRender.fs -Facilities\DiagnosticsLogger.fsi -Facilities\DiagnosticsLogger.fs -Facilities\DiagnosticResolutionHints.fsi -Facilities\DiagnosticResolutionHints.fs -Facilities\prim-lexing.fsi -Facilities\prim-lexing.fs -Facilities\prim-parsing.fsi -Facilities\prim-parsing.fs -Facilities\ReferenceResolver.fsi -Facilities\ReferenceResolver.fs -Facilities\SimulatedMSBuildReferenceResolver.fsi -Facilities\SimulatedMSBuildReferenceResolver.fs -Facilities\CompilerLocation.fsi -Facilities\CompilerLocation.fs -Facilities\BuildGraph.fsi -Facilities\BuildGraph.fs -AbstractIL\il.fsi -AbstractIL\il.fs -AbstractIL\ilx.fsi -AbstractIL\ilx.fs -AbstractIL\ilascii.fsi -AbstractIL\ilascii.fs -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\ilpars.fsi -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\ilpars.fs -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\illex.fsi -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\illex.fs -AbstractIL\ilprint.fsi -AbstractIL\ilprint.fs -AbstractIL\ilmorph.fsi -AbstractIL\ilmorph.fs -AbstractIL\ilsign.fsi -AbstractIL\ilsign.fs -AbstractIL\ilnativeres.fsi -AbstractIL\ilnativeres.fs -AbstractIL\ilsupp.fsi -AbstractIL\ilsupp.fs -AbstractIL\ilbinary.fsi -AbstractIL\ilbinary.fs -AbstractIL\ilread.fsi -AbstractIL\ilread.fs -AbstractIL\ilwritepdb.fsi -AbstractIL\ilwritepdb.fs -AbstractIL\ilwrite.fsi -AbstractIL\ilwrite.fs -AbstractIL\ilreflect.fsi -AbstractIL\ilreflect.fs -SyntaxTree\PrettyNaming.fsi -SyntaxTree\PrettyNaming.fs -SyntaxTree\UnicodeLexing.fsi -SyntaxTree\UnicodeLexing.fs -SyntaxTree\XmlDoc.fsi -SyntaxTree\XmlDoc.fs -SyntaxTree\SyntaxTrivia.fsi -SyntaxTree\SyntaxTrivia.fs -SyntaxTree\SyntaxTree.fsi -SyntaxTree\SyntaxTree.fs -SyntaxTree\SyntaxTreeOps.fsi -SyntaxTree\SyntaxTreeOps.fs -SyntaxTree\ParseHelpers.fsi -SyntaxTree\ParseHelpers.fs -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\pppars.fsi -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\pppars.fs -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\pars.fsi -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\pars.fs -SyntaxTree\LexHelpers.fsi -SyntaxTree\LexHelpers.fs -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\pplex.fsi -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\pplex.fs -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\\lex.fsi -C:\Users\nojaf\Projects\fsharp\artifacts/obj/FSharp.Compiler.Service//netstandard2.0\\lex.fs -SyntaxTree\LexFilter.fsi -SyntaxTree\LexFilter.fs -TypedTree\tainted.fsi -TypedTree\tainted.fs -TypedTree\TypeProviders.fsi -TypedTree\TypeProviders.fs -TypedTree\QuotationPickler.fsi -TypedTree\QuotationPickler.fs -TypedTree\CompilerGlobalState.fsi -TypedTree\CompilerGlobalState.fs -TypedTree\TypedTree.fsi -TypedTree\TypedTree.fs -TypedTree\TypedTreeBasics.fsi -TypedTree\TypedTreeBasics.fs -TypedTree\TcGlobals.fs -TypedTree\TypedTreeOps.fsi -TypedTree\TypedTreeOps.fs -TypedTree\TypedTreePickle.fsi -TypedTree\TypedTreePickle.fs -TypedTree\UpdatePrettyTyparNames.fsi -TypedTree\UpdatePrettyTyparNames.fs -Checking\import.fsi -Checking\import.fs -Checking\TypeHierarchy.fsi -Checking\TypeHierarchy.fs -Checking\infos.fsi -Checking\infos.fs -Checking\AccessibilityLogic.fsi -Checking\AccessibilityLogic.fs -Checking\AttributeChecking.fsi -Checking\AttributeChecking.fs -Checking\TypeRelations.fsi -Checking\TypeRelations.fs -Checking\InfoReader.fsi -Checking\InfoReader.fs -Checking\NicePrint.fsi -Checking\NicePrint.fs -Checking\AugmentWithHashCompare.fsi -Checking\AugmentWithHashCompare.fs -Checking\NameResolution.fsi -Checking\NameResolution.fs -Checking\SignatureConformance.fsi -Checking\SignatureConformance.fs -Checking\MethodOverrides.fsi -Checking\MethodOverrides.fs -Checking\MethodCalls.fsi -Checking\MethodCalls.fs -Checking\PatternMatchCompilation.fsi -Checking\PatternMatchCompilation.fs -Checking\ConstraintSolver.fsi -Checking\ConstraintSolver.fs -Checking\CheckFormatStrings.fsi -Checking\CheckFormatStrings.fs -Checking\FindUnsolved.fsi -Checking\FindUnsolved.fs -Checking\QuotationTranslator.fsi -Checking\QuotationTranslator.fs -Checking\PostInferenceChecks.fsi -Checking\PostInferenceChecks.fs -Checking\CheckBasics.fsi -Checking\CheckBasics.fs -Checking\CheckRecordSyntaxHelpers.fsi -Checking\CheckRecordSyntaxHelpers.fs -Checking\CheckExpressions.fsi -Checking\CheckExpressions.fs -Checking\CheckPatterns.fsi -Checking\CheckPatterns.fs -Checking\CheckComputationExpressions.fsi -Checking\CheckComputationExpressions.fs -Checking\CheckIncrementalClasses.fsi -Checking\CheckIncrementalClasses.fs -Checking\CheckDeclarations.fsi -Checking\CheckDeclarations.fs -Optimize\Optimizer.fsi -Optimize\Optimizer.fs -Optimize\DetupleArgs.fsi -Optimize\DetupleArgs.fs -Optimize\InnerLambdasToTopLevelFuncs.fsi -Optimize\InnerLambdasToTopLevelFuncs.fs -Optimize\LowerCalls.fsi -Optimize\LowerCalls.fs -Optimize\LowerSequences.fsi -Optimize\LowerSequences.fs -Optimize\LowerComputedCollections.fsi -Optimize\LowerComputedCollections.fs -Optimize\LowerStateMachines.fsi -Optimize\LowerStateMachines.fs -Optimize\LowerLocalMutables.fsi -Optimize\LowerLocalMutables.fs -CodeGen\IlxGenSupport.fsi -CodeGen\IlxGenSupport.fs -CodeGen\EraseClosures.fsi -CodeGen\EraseClosures.fs -CodeGen\EraseUnions.fsi -CodeGen\EraseUnions.fs -CodeGen\IlxGen.fsi -CodeGen\IlxGen.fs -Driver\FxResolver.fsi -Driver\FxResolver.fs -DependencyManager/AssemblyResolveHandler.fsi -DependencyManager/AssemblyResolveHandler.fs -DependencyManager/NativeDllResolveHandler.fsi -DependencyManager/NativeDllResolveHandler.fs -DependencyManager/DependencyProvider.fsi -DependencyManager/DependencyProvider.fs -Driver\CompilerConfig.fsi -Driver\CompilerConfig.fs -Driver\CompilerImports.fsi -Driver\CompilerImports.fs -Driver\CompilerDiagnostics.fsi -Driver\CompilerDiagnostics.fs -Driver\GraphChecking\Continuation.fsi -Driver\GraphChecking\Continuation.fs -Driver\GraphChecking\Types.fsi -Driver\GraphChecking\Types.fs -Driver\GraphChecking\Graph.fsi -Driver\GraphChecking\Graph.fs -Driver\GraphChecking\TrieMapping.fsi -Driver\GraphChecking\TrieMapping.fs -Driver\GraphChecking\FileContentMapping.fsi -Driver\GraphChecking\FileContentMapping.fs -Driver\GraphChecking\DependencyResolution.fsi -Driver\GraphChecking\DependencyResolution.fs -Driver\GraphChecking\GraphProcessing.fsi -Driver\GraphChecking\GraphProcessing.fs -Driver\GraphChecking\TypeCheckingGraphProcessing.fsi -Driver\GraphChecking\TypeCheckingGraphProcessing.fs -Driver\ParseAndCheckInputs.fsi -Driver\ParseAndCheckInputs.fs -Driver\ScriptClosure.fsi -Driver\ScriptClosure.fs -Driver\CompilerOptions.fsi -Driver\CompilerOptions.fs -Driver\OptimizeInputs.fsi -Driver\OptimizeInputs.fs -Driver\XmlDocFileWriter.fsi -Driver\XmlDocFileWriter.fs -Driver\BinaryResourceFormats.fsi -Driver\BinaryResourceFormats.fs -Driver\StaticLinking.fsi -Driver\StaticLinking.fs -Driver\CreateILModule.fsi -Driver\CreateILModule.fs -Driver\fsc.fsi -Driver\fsc.fs -Symbols\FSharpDiagnostic.fsi -Symbols\FSharpDiagnostic.fs -Symbols\SymbolHelpers.fsi -Symbols\SymbolHelpers.fs -Symbols\Symbols.fsi -Symbols\Symbols.fs -Symbols\Exprs.fsi -Symbols\Exprs.fs -Symbols\SymbolPatterns.fsi -Symbols\SymbolPatterns.fs -Service\SemanticClassification.fsi -Service\SemanticClassification.fs -Service\ItemKey.fsi -Service\ItemKey.fs -Service\SemanticClassificationKey.fsi -Service\SemanticClassificationKey.fs -Service\FSharpSource.fsi -Service\FSharpSource.fs -Service\IncrementalBuild.fsi -Service\IncrementalBuild.fs -Service\ServiceCompilerDiagnostics.fsi -Service\ServiceCompilerDiagnostics.fs -Service\ServiceConstants.fs -Service\ServiceDeclarationLists.fsi -Service\ServiceDeclarationLists.fs -Service\ServiceLexing.fsi -Service\ServiceLexing.fs -Service\ServiceParseTreeWalk.fsi -Service\ServiceParseTreeWalk.fs -Service\ServiceNavigation.fsi -Service\ServiceNavigation.fs -Service\ServiceParamInfoLocations.fsi -Service\ServiceParamInfoLocations.fs -Service\FSharpParseFileResults.fsi -Service\FSharpParseFileResults.fs -Service\ServiceParsedInputOps.fsi -Service\ServiceParsedInputOps.fs -Service\ServiceAssemblyContent.fsi -Service\ServiceAssemblyContent.fs -Service\ServiceXmlDocParser.fsi -Service\ServiceXmlDocParser.fs -Service\ExternalSymbol.fsi -Service\ExternalSymbol.fs -Service\QuickParse.fsi -Service\QuickParse.fs -Service\FSharpCheckerResults.fsi -Service\FSharpCheckerResults.fs -Service\service.fsi -Service\service.fs -Service\ServiceInterfaceStubGenerator.fsi -Service\ServiceInterfaceStubGenerator.fs -Service\ServiceStructure.fsi -Service\ServiceStructure.fs -Service\ServiceAnalysis.fsi -Service\ServiceAnalysis.fs -Interactive\FSharpInteractiveServer.fsi -Interactive\FSharpInteractiveServer.fs -Interactive\ControlledExecution.fs -Interactive\fsi.fsi -Interactive\fsi.fs -Legacy\LegacyHostedCompilerForTesting.fs diff --git a/src/Compiler/SyntaxTree/PrettyNaming.fs b/src/Compiler/SyntaxTree/PrettyNaming.fs index 55721ae6222..f78f742a03f 100755 --- a/src/Compiler/SyntaxTree/PrettyNaming.fs +++ b/src/Compiler/SyntaxTree/PrettyNaming.fs @@ -10,6 +10,7 @@ open System.Collections.Concurrent open System.Globalization open System.Text +open FSharp.Compiler.AbstractIL open Internal.Utilities.Library open FSharp.Compiler.Text open FSharp.Compiler.Text.Layout diff --git a/src/Compiler/SyntaxTree/SyntaxTree.fs b/src/Compiler/SyntaxTree/SyntaxTree.fs index 230946c5f4f..e9eb8bdfac3 100644 --- a/src/Compiler/SyntaxTree/SyntaxTree.fs +++ b/src/Compiler/SyntaxTree/SyntaxTree.fs @@ -4,7 +4,7 @@ namespace rec FSharp.Compiler.Syntax open System open System.Diagnostics -// open Internal.Utilities.Library +open Internal.Utilities.Library open FSharp.Compiler.Syntax open FSharp.Compiler.Text open FSharp.Compiler.Text.Range diff --git a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs index c96d040c511..f25b7abb37f 100644 --- a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs +++ b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs @@ -65,7 +65,7 @@ let mkSynCompGenSimplePatVar id = let (|SynSingleIdent|_|) x = match x with - | SynLongIdent (id = [ id ]) -> Some id + | SynLongIdent ([ id ], _, _) -> Some id | _ -> None /// Match a long identifier, including the case for single identifiers which gets a more optimized node in the syntax tree. @@ -105,13 +105,13 @@ let (|SynPipeRight|_|) input = let (|SynPipeRight2|_|) input = match input with - | SynBinOp (synId, SynExpr.Paren (expr = SynExpr.Tuple (isStruct = false; exprs = [ x1a; x1b ])), x2) when synId.idText = "op_PipeRight2" -> + | SynBinOp (synId, SynExpr.Paren (SynExpr.Tuple (false, [ x1a; x1b ], _, _), _, _, _), x2) when synId.idText = "op_PipeRight2" -> Some(x1a, x1b, x2) | _ -> None let (|SynPipeRight3|_|) input = match input with - | SynBinOp (synId, SynExpr.Paren (expr = SynExpr.Tuple (isStruct = false; exprs = [ x1a; x1b; x1c ])), x2) when synId.idText = "op_PipeRight3" -> + | SynBinOp (synId, SynExpr.Paren (SynExpr.Tuple (false, [ x1a; x1b; x1c ], _, _), _, _, _), x2) when synId.idText = "op_PipeRight3" -> Some(x1a, x1b, x1c, x2) | _ -> None @@ -138,7 +138,7 @@ let rec IsControlFlowExpression e = | SynExpr.LetOrUse _ | SynExpr.Sequential _ // Treat "ident { ... }" as a control flow expression - | SynExpr.App (funcExpr = SynExpr.Ident _; argExpr = SynExpr.ComputationExpr _) + | SynExpr.App (_, _, SynExpr.Ident _, SynExpr.ComputationExpr _, _) | SynExpr.IfThenElse _ | SynExpr.LetOrUseBang _ | SynExpr.Match _ @@ -147,7 +147,7 @@ let rec IsControlFlowExpression e = | SynExpr.For _ | SynExpr.ForEach _ | SynExpr.While _ -> true - | SynExpr.Typed (expr = e) -> IsControlFlowExpression e + | SynExpr.Typed (e, _, _) -> IsControlFlowExpression e | _ -> false // The syntactic criteria for when a debug point for a 'let' is extended to include @@ -257,7 +257,7 @@ let rec SimplePatOfPat (synArgNameGenerator: SynArgNameGenerator) p = let item = mkSynIdGetWithAlt m id altNameRefCell false, altNameRefCell, id, item | SynPat.Named (SynIdent (ident, _), _, _, _) - | SynPat.As (rhsPat = SynPat.Named (ident = SynIdent (ident, _))) -> + | SynPat.As (_, SynPat.Named (SynIdent (ident, _), _, _, _), _) -> // named pats should be referred to as their name in docs, tooltips, etc. let item = mkSynIdGet m ident.idText false, None, ident, item @@ -423,11 +423,11 @@ let mkSynQMarkSet m a b c = mkSynTrifix m qmarkSet a b c let mkSynDotParenGet mLhs mDot a b = match b with - | SynExpr.Tuple (isStruct = false; exprs = [ _; _ ]) -> + | SynExpr.Tuple (false, [ _; _ ], _, _) -> errorR (Deprecated(FSComp.SR.astDeprecatedIndexerNotation (), mLhs)) SynExpr.Const(SynConst.Unit, mLhs) - | SynExpr.Tuple (isStruct = false; exprs = [ _; _; _ ]) -> + | SynExpr.Tuple (false, [ _; _; _ ], _, _) -> errorR (Deprecated(FSComp.SR.astDeprecatedIndexerNotation (), mLhs)) SynExpr.Const(SynConst.Unit, mLhs) @@ -447,13 +447,13 @@ let mkSynAssign (l: SynExpr) (r: SynExpr) = match l with //| SynExpr.Paren (l2, m2) -> mkSynAssign m l2 r | LongOrSingleIdent (false, v, None, _) -> SynExpr.LongIdentSet(v, r, m) - | SynExpr.DotGet (expr = e; longDotId = v) -> SynExpr.DotSet(e, v, r, m) + | SynExpr.DotGet (e, _, v, _) -> SynExpr.DotSet(e, v, r, m) | SynExpr.DotIndexedGet (e1, e2, mDot, mLeft) -> SynExpr.DotIndexedSet(e1, e2, r, mLeft, mDot, m) | SynExpr.LibraryOnlyUnionCaseFieldGet (x, y, z, _) -> SynExpr.LibraryOnlyUnionCaseFieldSet(x, y, z, r, m) - | SynExpr.App (funcExpr = SynExpr.App (funcExpr = SingleIdent nm; argExpr = a); argExpr = b) when nm.idText = opNameQMark -> mkSynQMarkSet m a b r - | SynExpr.App (funcExpr = SynExpr.App (funcExpr = SingleIdent nm; argExpr = a); argExpr = b) when nm.idText = opNameParenGet -> mkSynDotParenSet m a b r - | SynExpr.App (funcExpr = SynExpr.LongIdent (false, v, None, _); argExpr = x) -> SynExpr.NamedIndexedPropertySet(v, x, r, m) - | SynExpr.App (funcExpr = SynExpr.DotGet (expr = e; longDotId = v); argExpr = x) -> SynExpr.DotNamedIndexedPropertySet(e, v, x, r, m) + | SynExpr.App (_, _, SynExpr.App (_, _, SingleIdent nm, a, _), b, _) when nm.idText = opNameQMark -> mkSynQMarkSet m a b r + | SynExpr.App (_, _, SynExpr.App (_, _, SingleIdent nm, a, _), b, _) when nm.idText = opNameParenGet -> mkSynDotParenSet m a b r + | SynExpr.App (_, _, SynExpr.LongIdent (false, v, None, _), x, _) -> SynExpr.NamedIndexedPropertySet(v, x, r, m) + | SynExpr.App (_, _, SynExpr.DotGet (e, _, v, _), x, _) -> SynExpr.DotNamedIndexedPropertySet(e, v, x, r, m) | l -> SynExpr.Set(l, r, m) let mkSynDot mDot m l (SynIdent (r, rTrivia)) = @@ -566,13 +566,13 @@ module SynInfo = let AritiesOfArgs (SynValInfo (args, _)) = List.map List.length args /// Get the argument attributes from the syntactic information for an argument. - let AttribsOfArgData (SynArgInfo (attributes = Attributes attribs)) = attribs + let AttribsOfArgData (SynArgInfo (Attributes attribs, _, _)) = attribs /// Infer the syntactic argument info for a single argument from a simple pattern. let rec InferSynArgInfoFromSimplePat attribs p = match p with - | SynSimplePat.Id (ident = nm; isCompilerGenerated = isCompGen; isOptional = isOpt) -> SynArgInfo(attribs, isOpt, (if isCompGen then None else Some nm)) - | SynSimplePat.Typed (pat = a) -> InferSynArgInfoFromSimplePat attribs a + | SynSimplePat.Id (nm, _, isCompGen, _, isOpt, _) -> SynArgInfo(attribs, isOpt, (if isCompGen then None else Some nm)) + | SynSimplePat.Typed (a, _, _) -> InferSynArgInfoFromSimplePat attribs a | SynSimplePat.Attrib (a, attribs2, _) -> InferSynArgInfoFromSimplePat (attribs @ attribs2) a /// Infer the syntactic argument info for one or more arguments one or more simple patterns. @@ -802,40 +802,40 @@ let rec synExprContainsError inpExpr = | SynExpr.Const _ | SynExpr.Dynamic _ -> false - | SynExpr.TypeTest (expr = e) - | SynExpr.Upcast (expr = e) - | SynExpr.AddressOf (expr = e) - | SynExpr.ComputationExpr (expr = e) - | SynExpr.ArrayOrListComputed (expr = e) - | SynExpr.Typed (expr = e) - | SynExpr.Do (expr = e) - | SynExpr.Assert (expr = e) - | SynExpr.DotGet (expr = e) - | SynExpr.LongIdentSet (expr = e) - | SynExpr.New (expr = e) - | SynExpr.TypeApp (expr = e) - | SynExpr.LibraryOnlyUnionCaseFieldGet (expr = e) - | SynExpr.Downcast (expr = e) - | SynExpr.InferredUpcast (expr = e) - | SynExpr.InferredDowncast (expr = e) - | SynExpr.Lazy (expr = e) - | SynExpr.TraitCall (argExpr = e) - | SynExpr.YieldOrReturn (expr = e) - | SynExpr.YieldOrReturnFrom (expr = e) - | SynExpr.DoBang (expr = e) - | SynExpr.Fixed (expr = e) - | SynExpr.DebugPoint (innerExpr = e) - | SynExpr.Paren (expr = e) -> walkExpr e - - | SynExpr.NamedIndexedPropertySet (expr1 = e1; expr2 = e2) - | SynExpr.DotSet (targetExpr = e1; rhsExpr = e2) - | SynExpr.Set (targetExpr = e1; rhsExpr = e2) - | SynExpr.LibraryOnlyUnionCaseFieldSet (expr = e1; rhsExpr = e2) - | SynExpr.JoinIn (lhsExpr = e1; rhsExpr = e2) - | SynExpr.App (funcExpr = e1; argExpr = e2) -> walkExpr e1 || walkExpr e2 - - | SynExpr.ArrayOrList (exprs = es) - | SynExpr.Tuple (exprs = es) -> walkExprs es + | SynExpr.TypeTest (e, _, _) + | SynExpr.Upcast (e, _, _) + | SynExpr.AddressOf (_, e, _, _) + | SynExpr.ComputationExpr (_, e, _) + | SynExpr.ArrayOrListComputed (_, e, _) + | SynExpr.Typed (e, _, _) + | SynExpr.Do (e, _) + | SynExpr.Assert (e, _) + | SynExpr.DotGet (e, _, _, _) + | SynExpr.LongIdentSet (_, e, _) + | SynExpr.New (_, _, e, _) + | SynExpr.TypeApp (e, _, _, _, _, _, _) + | SynExpr.LibraryOnlyUnionCaseFieldGet (e, _, _, _) + | SynExpr.Downcast (e, _, _) + | SynExpr.InferredUpcast (e, _) + | SynExpr.InferredDowncast (e, _) + | SynExpr.Lazy (e, _) + | SynExpr.TraitCall (_, _, e, _) + | SynExpr.YieldOrReturn (_, e, _) + | SynExpr.YieldOrReturnFrom (_, e, _) + | SynExpr.DoBang (e, _) + | SynExpr.Fixed (e, _) + | SynExpr.DebugPoint (_, _, e) + | SynExpr.Paren (e, _, _, _) -> walkExpr e + + | SynExpr.NamedIndexedPropertySet (_, e1, e2, _) + | SynExpr.DotSet (e1, _, e2, _) + | SynExpr.Set (e1, e2, _) + | SynExpr.LibraryOnlyUnionCaseFieldSet (e1, _, _, e2, _) + | SynExpr.JoinIn (e1, _, e2, _) + | SynExpr.App (_, _, e1, e2, _) -> walkExpr e1 || walkExpr e2 + + | SynExpr.ArrayOrList (_, es, _) + | SynExpr.Tuple (_, es, _, _) -> walkExprs es | SynExpr.AnonRecd (copyInfo = origExpr; recordFields = flds) -> (match origExpr with @@ -861,12 +861,12 @@ let rec synExprContainsError inpExpr = walkBinds bs || walkBinds binds - | SynExpr.ForEach (enumExpr = e1; bodyExpr = e2) - | SynExpr.While (whileExpr = e1; doExpr = e2) -> walkExpr e1 || walkExpr e2 + | SynExpr.ForEach (_, _, _, _, _, e1, e2, _) + | SynExpr.While (_, e1, e2, _) -> walkExpr e1 || walkExpr e2 | SynExpr.For (identBody = e1; toBody = e2; doBody = e3) -> walkExpr e1 || walkExpr e2 || walkExpr e3 - | SynExpr.MatchLambda (matchClauses = cl) -> walkMatchClauses cl + | SynExpr.MatchLambda (_, _, cl, _, _) -> walkMatchClauses cl | SynExpr.Lambda (body = e) -> walkExpr e @@ -880,7 +880,7 @@ let rec synExprContainsError inpExpr = | SynExpr.Sequential (_, _, e1, e2, _) -> walkExpr e1 || walkExpr e2 - | SynExpr.SequentialOrImplicitYield (expr1 = e1; expr2 = e2) -> walkExpr e1 || walkExpr e2 + | SynExpr.SequentialOrImplicitYield (_, e1, e2, _, _) -> walkExpr e1 || walkExpr e2 | SynExpr.IfThenElse (ifExpr = e1; thenExpr = e2; elseExpr = e3opt) -> walkExpr e1 || walkExpr e2 || walkExprOpt e3opt diff --git a/src/Compiler/SyntaxTree/XmlDoc.fs b/src/Compiler/SyntaxTree/XmlDoc.fs index ee92eae9a02..e5aa1b70d00 100644 --- a/src/Compiler/SyntaxTree/XmlDoc.fs +++ b/src/Compiler/SyntaxTree/XmlDoc.fs @@ -13,6 +13,7 @@ open FSharp.Compiler.DiagnosticsLogger open FSharp.Compiler.IO open FSharp.Compiler.Text open FSharp.Compiler.Text.Range +open FSharp.Compiler.AbstractIL.IL /// Represents collected XmlDoc lines [] diff --git a/src/Compiler/SyntaxTree/XmlDoc.fsi b/src/Compiler/SyntaxTree/XmlDoc.fsi index 33b168786cc..939a1b238e5 100644 --- a/src/Compiler/SyntaxTree/XmlDoc.fsi +++ b/src/Compiler/SyntaxTree/XmlDoc.fsi @@ -3,6 +3,7 @@ namespace FSharp.Compiler.Xml open FSharp.Compiler.Text +open FSharp.Compiler.AbstractIL.IL /// Represents collected XmlDoc lines [] diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/CompilationFromCmdlineArgsTests.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/CompilationFromCmdlineArgsTests.fs index 63b18a33ab7..4525d7286f2 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/CompilationFromCmdlineArgsTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/CompilationFromCmdlineArgsTests.fs @@ -13,12 +13,10 @@ module CompilationFromCmdlineArgsTests = // The path needs to be absolute. let localProjects: string list = [ - // @"C:\Projects\fantomas\src\Fantomas.Core\Fantomas.Core.args.txt" - // @"C:\Projects\FsAutoComplete\src\FsAutoComplete\FsAutoComplete.args.txt" - // @"C:\Projects\fsharp\src\Compiler\FSharp.Compiler.Service.args.txt" - // @"C:\Projects\fsharp\tests\FSharp.Compiler.ComponentTests\FSharp.Compiler.ComponentTests.args.txt" - @"C:\Users\nojaf\Projects\fsharp\src\Compiler\FSharp.Compiler.Service.rsp" - @"C:\Users\nojaf\Projects\fsharp\src\Compiler\Trimmed.rsp" + @"C:\Projects\fantomas\src\Fantomas.Core\Fantomas.Core.args.txt" + @"C:\Projects\FsAutoComplete\src\FsAutoComplete\FsAutoComplete.args.txt" + @"C:\Projects\fsharp\src\Compiler\FSharp.Compiler.Service.args.txt" + @"C:\Projects\fsharp\tests\FSharp.Compiler.ComponentTests\FSharp.Compiler.ComponentTests.args.txt" ] let checker = FSharpChecker.Create() @@ -38,7 +36,6 @@ module CompilationFromCmdlineArgsTests = if not (Array.contains "--times" argsFromFile) then yield "--times" yield! methodOptions method - yield "--deterministic-" |] let diagnostics, exitCode = checker.Compile(args) |> Async.RunSynchronously From 12fdbdd777bbf2b29bac024ccb5afe0377360c8e Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 26 Jun 2023 13:24:08 +0200 Subject: [PATCH 067/222] Use NodeToTypeCheck instead of idx. --- .../TypeCheckingGraphProcessing.fs | 14 ++++----- .../TypeCheckingGraphProcessing.fsi | 4 +-- src/Compiler/Driver/ParseAndCheckInputs.fs | 30 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fs b/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fs index 52318982d2d..fb3db7b8666 100644 --- a/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fs +++ b/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fs @@ -20,10 +20,10 @@ open System.Threading /// let combineResults (emptyState: 'State) - (deps: ProcessedNode<'Item, 'State * Finisher<'ChosenItem, 'State, 'FinalFileResult>>[]) - (transitiveDeps: ProcessedNode<'Item, 'State * Finisher<'ChosenItem, 'State, 'FinalFileResult>>[]) + (deps: ProcessedNode<'Item, 'State * Finisher<'Item, 'State, 'FinalFileResult>>[]) + (transitiveDeps: ProcessedNode<'Item, 'State * Finisher<'Item, 'State, 'FinalFileResult>>[]) (sortResultsToAdd: 'Item -> 'Item -> int) - (folder: 'State -> Finisher<'ChosenItem, 'State, 'FinalFileResult> -> 'State) + (folder: 'State -> Finisher<'Item, 'State, 'FinalFileResult> -> 'State) : 'State = match deps with | [||] -> emptyState @@ -66,9 +66,9 @@ let combineResults /// let processTypeCheckingGraph<'Item, 'ChosenItem, 'State, 'FinalFileResult when 'Item: equality and 'Item: comparison> (graph: Graph<'Item>) - (work: 'Item -> 'State -> Finisher<'ChosenItem, 'State, 'FinalFileResult>) + (work: 'Item -> 'State -> Finisher<'Item, 'State, 'FinalFileResult>) (sortResultsToAdd: 'Item -> 'Item -> int) - (folder: 'State -> Finisher<'ChosenItem, 'State, 'FinalFileResult> -> 'FinalFileResult * 'State) + (folder: 'State -> Finisher<'Item, 'State, 'FinalFileResult> -> 'FinalFileResult * 'State) // Decides whether a result for an item should be included in the final state, and how to map the item if it should. (finalStateChooser: 'Item -> 'ChosenItem option) (emptyState: 'State) @@ -76,9 +76,9 @@ let processTypeCheckingGraph<'Item, 'ChosenItem, 'State, 'FinalFileResult when ' : ('ChosenItem * 'FinalFileResult) list * 'State = let workWrapper - (getProcessedNode: 'Item -> ProcessedNode<'Item, 'State * Finisher<'ChosenItem, 'State, 'FinalFileResult>>) + (getProcessedNode: 'Item -> ProcessedNode<'Item, 'State * Finisher<'Item, 'State, 'FinalFileResult>>) (node: NodeInfo<'Item>) - : 'State * Finisher<'ChosenItem, 'State, 'FinalFileResult> = + : 'State * Finisher<'Item, 'State, 'FinalFileResult> = let folder x y = folder x y |> snd let deps = node.Deps |> Array.except [| node.Item |] |> Array.map getProcessedNode diff --git a/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fsi b/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fsi index 07f1d003ffb..7d03d404269 100644 --- a/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fsi +++ b/src/Compiler/Driver/GraphChecking/TypeCheckingGraphProcessing.fsi @@ -9,9 +9,9 @@ open System.Threading /// val processTypeCheckingGraph<'Item, 'ChosenItem, 'State, 'FinalFileResult when 'Item: equality and 'Item: comparison> : graph: Graph<'Item> -> - work: ('Item -> 'State -> Finisher<'ChosenItem, 'State, 'FinalFileResult>) -> + work: ('Item -> 'State -> Finisher<'Item, 'State, 'FinalFileResult>) -> sortResultsToAdd: ('Item -> 'Item -> int) -> - folder: ('State -> Finisher<'ChosenItem, 'State, 'FinalFileResult> -> 'FinalFileResult * 'State) -> + folder: ('State -> Finisher<'Item, 'State, 'FinalFileResult> -> 'FinalFileResult * 'State) -> finalStateChooser: ('Item -> 'ChosenItem option) -> emptyState: 'State -> ct: CancellationToken -> diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index e85f4db1cc0..7bd2962670b 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1477,7 +1477,7 @@ let folder (state: State) (Finisher (finisher = finisher)) : FinalFileResult * S /// Typecheck a single file (or interactive entry into F# Interactive) /// a callback functions that takes a `TcState` and will add the checked result to it. let CheckOneInputWithCallback - (idx: int) + (node: NodeToTypeCheck) ((checkForErrors, tcConfig: TcConfig, tcImports: TcImports, @@ -1487,7 +1487,7 @@ let CheckOneInputWithCallback tcState: TcState, inp: ParsedInput, _skipImplIfSigExists: bool): (unit -> bool) * TcConfig * TcImports * TcGlobals * LongIdent option * TcResultsSink * TcState * ParsedInput * bool) - : Cancellable> = + : Cancellable> = cancellable { try CheckSimulateException tcConfig @@ -1537,7 +1537,7 @@ let CheckOneInputWithCallback return Finisher( - idx, + node, (fun tcState -> if tcState.tcsRootSigs.ContainsKey qualNameOfFile then printfn $"{qualNameOfFile} already part of root sigs?" @@ -1589,7 +1589,7 @@ let CheckOneInputWithCallback return Finisher( - idx, + node, (fun tcState -> // Check if we've already seen an implementation for this fragment if Zset.contains qualNameOfFile tcState.tcsRootImpls then @@ -1613,7 +1613,7 @@ let CheckOneInputWithCallback with e -> errorRecovery e range0 - return Finisher(idx, (fun tcState -> (tcState.TcEnvFromSignatures, EmptyTopAttrs, None, tcState.tcsCcuSig), tcState)) + return Finisher(node, (fun tcState -> (tcState.TcEnvFromSignatures, EmptyTopAttrs, None, tcState.tcsCcuSig), tcState)) } let AddSignatureResultToTcImplEnv (tcImports: TcImports, tcGlobals, prefixPathOpt, tcSink, tcState, input: ParsedInput) = @@ -1724,12 +1724,12 @@ let CheckMultipleInputsUsingGraphMode let priorErrors = checkForErrors () let processArtificialImplFile - (idx: int) + (node: NodeToTypeCheck) (input: ParsedInput) ((currentTcState, _currentPriorErrors): State) - : Finisher = + : Finisher = Finisher( - idx, + node, (fun (state: State) -> let tcState, currentPriorErrors = state @@ -1744,22 +1744,22 @@ let CheckMultipleInputsUsingGraphMode ) let processFile - (idx: int) + (node: NodeToTypeCheck) ((input, logger): ParsedInput * DiagnosticsLogger) ((currentTcState, _currentPriorErrors): State) - : Finisher = + : Finisher = use _ = UseDiagnosticsLogger logger let checkForErrors2 () = priorErrors || (logger.ErrorCount > 0) let tcSink = TcResultsSink.NoSink let (Finisher (finisher = finisher)) = CheckOneInputWithCallback - idx + node (checkForErrors2, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcSink, currentTcState, input, false) |> Cancellable.runWithoutCancellation Finisher( - idx, + node, (fun (state: State) -> printfn "Finisher for %s" input.FileName let tcState, priorErrors = state @@ -1779,14 +1779,14 @@ let CheckMultipleInputsUsingGraphMode let logger = DiagnosticsLoggerForInput(tcConfig, input, oldLogger) input, logger) - let processFile (node: NodeToTypeCheck) (state: State) : Finisher = + let processFile (node: NodeToTypeCheck) (state: State) : Finisher = match node with | NodeToTypeCheck.ArtificialImplFile idx -> let parsedInput, _ = inputsWithLoggers[idx] - processArtificialImplFile idx parsedInput state + processArtificialImplFile node parsedInput state | NodeToTypeCheck.PhysicalFile idx -> let parsedInput, logger = inputsWithLoggers[idx] - processFile idx (parsedInput, logger) state + processFile node (parsedInput, logger) state let state: State = tcState, priorErrors From 8616d7fd1d739f93c84b644960ea54c8fa926787 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 26 Jun 2023 13:24:46 +0200 Subject: [PATCH 068/222] Undo spaces --- src/Compiler/Checking/CheckDeclarations.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index 7704b14266b..a37c39a7d43 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -4661,7 +4661,7 @@ let rec TcSignatureElementNonMutRec (cenv: cenv) parent typeNames endm (env: TcE match TryStripPrefixPath g enclosingNamespacePath with | Some(p, _) -> TcOpenModuleOrNamespaceDecl cenv.tcSink g cenv.amap m.EndRange env ([p], m.EndRange) | None -> env, [] - + // Publish the combined module type env.eModuleOrNamespaceTypeAccumulator.Value <- CombineCcuContentFragments [env.eModuleOrNamespaceTypeAccumulator.Value; modTyRoot] From b6addc1996ae7ef31e9119d097a9cec4b9120aed Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 26 Jun 2023 17:28:20 +0200 Subject: [PATCH 069/222] wip --- src/Compiler/Driver/ParseAndCheckInputs.fsi | 3 +- src/Compiler/Service/TransparentCompiler.fs | 28 +++++++++++++------ .../FSharpChecker/TransparentCompiler.fs | 27 ++++++++---------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fsi b/src/Compiler/Driver/ParseAndCheckInputs.fsi index 9758fc5d0b0..2a6a8f47adf 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fsi +++ b/src/Compiler/Driver/ParseAndCheckInputs.fsi @@ -186,6 +186,7 @@ val CheckOneInput: Cancellable<(TcEnv * TopAttribs * CheckedImplFile option * ModuleOrNamespaceType) * TcState> val CheckOneInputWithCallback: + node: NodeToTypeCheck -> checkForErrors: (unit -> bool) * tcConfig: TcConfig * tcImports: TcImports * @@ -195,7 +196,7 @@ val CheckOneInputWithCallback: tcState: TcState * input: ParsedInput * _skipImplIfSigExists: bool -> - Cancellable> + Cancellable> val AddCheckResultsToTcState: tcGlobals: TcGlobals * diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 465005790be..a574c1a9ecd 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -77,7 +77,7 @@ type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFil [] type internal TcIntermediate = { - finisher: Finisher + finisher: Finisher //tcEnvAtEndOfFile: TcEnv /// Disambiguation table for module names @@ -815,8 +815,9 @@ type internal TransparentCompiler let input, moduleNamesDict = DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input + let node = NodeToTypeCheck.PhysicalFile fileIndex // TODO: is this correct? let! finisher = - CheckOneInputWithCallback( + CheckOneInputWithCallback node ( (fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), tcConfig, tcImports, @@ -845,13 +846,14 @@ type internal TransparentCompiler } ) - let mergeIntermediateResults = + let mergeIntermediateResults x y = Array.fold (fun (tcInfos: TcInfo list) (tcIntermediate: TcIntermediate) -> match tcInfos with | [] -> failwith "need initial tcInfo" | tcInfo :: rest -> + let Finisher(finisher = finisher) = tcIntermediate.finisher let (tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile), tcState = - tcInfo.tcState |> tcIntermediate.finisher.Invoke + tcInfo.tcState |> finisher let tcEnvAtEndOfFile = if keepAllBackgroundResolutions then @@ -867,7 +869,7 @@ type internal TransparentCompiler tcDependencyFiles = tcIntermediate.tcDependencyFiles @ tcInfo.tcDependencyFiles latestCcuSigForFile = Some ccuSigForFile } - :: tcInfo :: rest) + :: tcInfo :: rest) x y let typeCheckLayers bootstrapInfo projectSnapshot (parsedInputs: array<_>) graph layers = let rec processLayer (layers: Set list) tcInfos = @@ -902,7 +904,7 @@ type internal TransparentCompiler let tcIntermediate = { - finisher = finisher + finisher = Finisher(fileNode, finisher) moduleNamesDict = tcInfo.moduleNamesDict tcDiagnosticsRev = [] tcDependencyFiles = [] @@ -912,7 +914,16 @@ type internal TransparentCompiler node.Return(tcIntermediate)) |> NodeCode.Parallel - return! processLayer rest (mergeIntermediateResults tcInfos results) + return! + results + |> Array.filter ( + function + | {finisher = Finisher(node=NodeToTypeCheck.ArtificialImplFile idx)} -> + // When we have the actual PhysicalFile in the same layer as the corresponding ArtificialImplFile then we don't apply the artificial's file finisher because it's not needed and it would lead to doubling of the results + not (layer.Contains (NodeToTypeCheck.PhysicalFile (idx + 1))) + | _ -> true) + |> mergeIntermediateResults tcInfos + |> processLayer rest } node { @@ -1062,8 +1073,9 @@ type internal TransparentCompiler let fileIndex = projectSnapshot.IndexOf fileName let! tcIntermediate = ComputeTcIntermediate projectSnapshot graph fileIndex (parseTree, parseDiagnostics) bootstrapInfo priorTcInfo + let Finisher(finisher = finisher) = tcIntermediate.finisher let (tcEnv, _topAttribs, checkedImplFileOpt, ccuSigForFile), tcState = - priorTcInfo.tcState |> tcIntermediate.finisher.Invoke + priorTcInfo.tcState |> finisher let sink = tcIntermediate.sink diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index fbe69c42d0c..f3a7b986cf1 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -52,9 +52,10 @@ let ``Parallel processing`` () = sourceFile "D" ["A"], sourceFile "E" ["B"; "C"; "D"]) - ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + ProjectWorkflowBuilder(project, useTransparentCompiler = false) { checkFile "E" expectOk updateFile "A" updatePublicSurface + saveFile "A" checkFile "E" expectSignatureChanged } @@ -332,26 +333,22 @@ let ``Multi-project`` signatureFiles = { sourceFile "LibA" [] with Source = "let f (x: int) = x" - SignatureFile = sigFile } - //, - //{ sourceFile "LibB" ["LibA"] with SignatureFile = sigFile }, - //{ sourceFile "LibC" ["LibA"] with SignatureFile = sigFile }, - //{ sourceFile "LibD" ["LibB"; "LibC"] with SignatureFile = sigFile } - ) + SignatureFile = sigFile }, + { sourceFile "LibB" ["LibA"] with SignatureFile = sigFile }, + { sourceFile "LibC" ["LibA"] with SignatureFile = sigFile }, + { sourceFile "LibD" ["LibB"; "LibC"] with SignatureFile = sigFile }) let project = { SyntheticProject.Create("app", - sourceFile "A" ["LibA"] - //, - //sourceFile "B" ["A"; "LibB"], - //sourceFile "C" ["A"; "LibC"], - //sourceFile "D" ["A"; "LibD"] - ) + sourceFile "A" ["LibA"], + sourceFile "B" ["A"; "LibB"], + sourceFile "C" ["A"; "LibC"], + sourceFile "D" ["A"; "LibD"]) with DependsOn = [library] } ProjectWorkflowBuilder(project, useTransparentCompiler = true) { - //updateFile "LibA" updatePublicSurface - checkFile "A" expectOk + updateFile "LibA" updatePublicSurface + checkFile "D" expectOk } From d9c8524f541a40952e73e488f4ffc287f26ad4b7 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 27 Jun 2023 17:53:00 +0200 Subject: [PATCH 070/222] wip --- src/Compiler/Driver/ParseAndCheckInputs.fsi | 2 +- src/Compiler/Service/TransparentCompiler.fs | 141 ++++++++++++------ .../FSharpChecker/TransparentCompiler.fs | 23 +-- 3 files changed, 107 insertions(+), 59 deletions(-) diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fsi b/src/Compiler/Driver/ParseAndCheckInputs.fsi index 2a6a8f47adf..875be616a8e 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fsi +++ b/src/Compiler/Driver/ParseAndCheckInputs.fsi @@ -196,7 +196,7 @@ val CheckOneInputWithCallback: tcState: TcState * input: ParsedInput * _skipImplIfSigExists: bool -> - Cancellable> + Cancellable> val AddCheckResultsToTcState: tcGlobals: TcGlobals * diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index a574c1a9ecd..20e0aead942 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -17,6 +17,7 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.CompilerConfig open FSharp.Compiler.CompilerImports open FSharp.Compiler.CompilerOptions +open FSharp.Compiler.CheckBasics open FSharp.Compiler.DependencyManager open FSharp.Compiler.Diagnostics open FSharp.Compiler.DiagnosticsLogger @@ -49,6 +50,49 @@ type internal FSharpFile = IsExe: bool } +/// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. +[] +type internal TcInfo = + { + tcState: TcState + tcEnvAtEndOfFile: TcEnv + + /// Disambiguation table for module names + moduleNamesDict: ModuleNamesDict + + topAttribs: TopAttribs option + + latestCcuSigForFile: ModuleOrNamespaceType option + + /// Accumulated diagnostics, last file first + tcDiagnosticsRev: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] list + + tcDependencyFiles: string list + + sigNameOpt: (string * QualifiedNameOfFile) option + + graphNode: NodeToTypeCheck option + } + + member x.TcDiagnostics = Array.concat (List.rev x.tcDiagnosticsRev) + +[] +type internal TcIntermediate = + { + finisher: Finisher + //tcEnvAtEndOfFile: TcEnv + + /// Disambiguation table for module names + moduleNamesDict: ModuleNamesDict + + /// Accumulated diagnostics, last file first + tcDiagnosticsRev: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] list + + tcDependencyFiles: string list + + sink: TcResultsSinkImpl + } + /// Things we need to start parsing and checking files for a given project snapshot type internal BootstrapInfo = { @@ -73,24 +117,6 @@ type internal BootstrapInfo = type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string -/// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. -[] -type internal TcIntermediate = - { - finisher: Finisher - //tcEnvAtEndOfFile: TcEnv - - /// Disambiguation table for module names - moduleNamesDict: ModuleNamesDict - - /// Accumulated diagnostics, last file first - tcDiagnosticsRev: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] list - - tcDependencyFiles: string list - - sink: TcResultsSinkImpl - } - [] type internal DependencyGraphType = /// A dependency graph for a single file - it will be missing files which this file does not depend on @@ -312,6 +338,7 @@ type internal TransparentCompiler moduleNamesDict = Map.empty tcDependencyFiles = basicDependencies sigNameOpt = None + graphNode = None } return tcImports, tcInfo @@ -816,18 +843,19 @@ type internal TransparentCompiler DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input let node = NodeToTypeCheck.PhysicalFile fileIndex // TODO: is this correct? + let! finisher = - CheckOneInputWithCallback node ( - (fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), - tcConfig, - tcImports, - tcGlobals, - None, - TcResultsSink.WithSink sink, - prevTcInfo.tcState, - input, - false - ) + CheckOneInputWithCallback + node + ((fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), + tcConfig, + tcImports, + tcGlobals, + None, + TcResultsSink.WithSink sink, + prevTcInfo.tcState, + input, + false) |> NodeCode.FromCancellable //fileChecked.Trigger fileName @@ -846,12 +874,13 @@ type internal TransparentCompiler } ) - let mergeIntermediateResults x y = + let mergeIntermediateResults = Array.fold (fun (tcInfos: TcInfo list) (tcIntermediate: TcIntermediate) -> match tcInfos with | [] -> failwith "need initial tcInfo" | tcInfo :: rest -> - let Finisher(finisher = finisher) = tcIntermediate.finisher + let (Finisher (node = node; finisher = finisher)) = tcIntermediate.finisher + let (tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile), tcState = tcInfo.tcState |> finisher @@ -868,8 +897,9 @@ type internal TransparentCompiler tcDiagnosticsRev = tcIntermediate.tcDiagnosticsRev @ tcInfo.tcDiagnosticsRev tcDependencyFiles = tcIntermediate.tcDependencyFiles @ tcInfo.tcDependencyFiles latestCcuSigForFile = Some ccuSigForFile + graphNode = Some node } - :: tcInfo :: rest) x y + :: tcInfo :: rest) let typeCheckLayers bootstrapInfo projectSnapshot (parsedInputs: array<_>) graph layers = let rec processLayer (layers: Set list) tcInfos = @@ -916,11 +946,12 @@ type internal TransparentCompiler return! results - |> Array.filter ( - function - | {finisher = Finisher(node=NodeToTypeCheck.ArtificialImplFile idx)} -> - // When we have the actual PhysicalFile in the same layer as the corresponding ArtificialImplFile then we don't apply the artificial's file finisher because it's not needed and it would lead to doubling of the results - not (layer.Contains (NodeToTypeCheck.PhysicalFile (idx + 1))) + |> Array.filter (function + | { + finisher = Finisher(node = NodeToTypeCheck.ArtificialImplFile idx) + } -> + // When we have the actual PhysicalFile in the same layer as the corresponding ArtificialImplFile then we don't apply the artificial's file finisher because it's not needed and it would lead to doubling of the results + not (layer.Contains(NodeToTypeCheck.PhysicalFile(idx + 1))) | _ -> true) |> mergeIntermediateResults tcInfos |> processLayer rest @@ -1064,16 +1095,19 @@ type internal TransparentCompiler | Some bootstrapInfo, creationDiags -> let file = bootstrapInfo.GetFile fileName - + let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file let! parseResults, _sourceText = getParseResult bootstrapInfo creationDiags fileName let! priorTcInfo, graph = ComputeTcPrior file bootstrapInfo projectSnapshot - + let fileIndex = projectSnapshot.IndexOf fileName - let! tcIntermediate = ComputeTcIntermediate projectSnapshot graph fileIndex (parseTree, parseDiagnostics) bootstrapInfo priorTcInfo - let Finisher(finisher = finisher) = tcIntermediate.finisher + let! tcIntermediate = + ComputeTcIntermediate projectSnapshot graph fileIndex (parseTree, parseDiagnostics) bootstrapInfo priorTcInfo + + let (Finisher (finisher = finisher)) = tcIntermediate.finisher + let (tcEnv, _topAttribs, checkedImplFileOpt, ccuSigForFile), tcState = priorTcInfo.tcState |> finisher @@ -1082,12 +1116,16 @@ type internal TransparentCompiler let tcResolutions = sink.GetResolutions() let tcSymbolUses = sink.GetSymbolUses() let tcOpenDeclarations = sink.GetOpenDeclarations() - + let tcDependencyFiles = [] // TODO add as a set to TcIntermediate - let tcDiagnostics = seq { - yield! priorTcInfo.TcDiagnostics - for x in tcIntermediate.tcDiagnosticsRev - do yield! x } + + let tcDiagnostics = + seq { + yield! priorTcInfo.TcDiagnostics + + for x in tcIntermediate.tcDiagnosticsRev do + yield! x + } let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions @@ -1135,7 +1173,6 @@ type internal TransparentCompiler } ) - let ComputeParseAndCheckAllFilesInProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = ParseAndCheckAllFilesInProjectCache.Get( projectSnapshot.Key, @@ -1157,7 +1194,15 @@ type internal TransparentCompiler let layers = Graph.leafSequence graph |> Seq.toList let! tcInfos, _ = typeCheckLayers bootstrapInfo projectSnapshot parsedInputs dependencyFiles layers - return tcInfos |> List.rev + + return + tcInfos + |> List.filter (function + | { + graphNode = Some (NodeToTypeCheck.PhysicalFile _) + } -> true + | _ -> false) + |> List.rev } ) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index f3a7b986cf1..d68aeea6e21 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -56,6 +56,7 @@ let ``Parallel processing`` () = checkFile "E" expectOk updateFile "A" updatePublicSurface saveFile "A" + checkFile "E" expectSignatureChanged } @@ -330,25 +331,27 @@ let ``Multi-project`` signatureFiles = let sigFile = if signatureFiles then AutoGenerated else No let library = SyntheticProject.Create("library", - { sourceFile "LibA" [] - with + { sourceFile "LibA" [] + with Source = "let f (x: int) = x" SignatureFile = sigFile }, - { sourceFile "LibB" ["LibA"] with SignatureFile = sigFile }, - { sourceFile "LibC" ["LibA"] with SignatureFile = sigFile }, - { sourceFile "LibD" ["LibB"; "LibC"] with SignatureFile = sigFile }) + { sourceFile "LibB" ["LibA"] with SignatureFile = No }//, + //{ sourceFile "LibC" ["LibA"] with SignatureFile = sigFile }, + //{ sourceFile "LibD" ["LibB"; "LibC"] with SignatureFile = sigFile } + ) let project = { SyntheticProject.Create("app", - sourceFile "A" ["LibA"], - sourceFile "B" ["A"; "LibB"], - sourceFile "C" ["A"; "LibC"], - sourceFile "D" ["A"; "LibD"]) + sourceFile "A" ["LibB"]//, + //sourceFile "B" ["A"; "LibB"], + //sourceFile "C" ["A"; "LibC"], + //sourceFile "D" ["A"; "LibD"] + ) with DependsOn = [library] } ProjectWorkflowBuilder(project, useTransparentCompiler = true) { updateFile "LibA" updatePublicSurface - checkFile "D" expectOk + checkFile "A" expectOk } From ce3464d6efeb9dd28c6660d1bc3928476191fdf3 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 29 Jun 2023 12:14:02 +0200 Subject: [PATCH 071/222] wip --- src/Compiler/Driver/ParseAndCheckInputs.fs | 2 + src/Compiler/Service/TransparentCompiler.fs | 45 +++++++++++++------ .../FSharpChecker/TransparentCompiler.fs | 2 +- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index b6373e549b8..804be160c5b 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1537,6 +1537,7 @@ let CheckOneInputWithCallback Finisher( node, (fun tcState -> + Trace.TraceInformation $"* * * * {node} Finisher [sig]" let rootSigs = Zmap.add qualNameOfFile sigFileType tcState.tcsRootSigs let tcSigEnv = @@ -1585,6 +1586,7 @@ let CheckOneInputWithCallback Finisher( node, (fun tcState -> + Trace.TraceInformation $"* * * * {node} Finisher [impl]" // Check if we've already seen an implementation for this fragment if Zset.contains qualNameOfFile tcState.tcsRootImpls then errorR (Error(FSComp.SR.buildImplementationAlreadyGiven (qualNameOfFile.Text), m)) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 20e0aead942..ad13adde1d7 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -72,6 +72,8 @@ type internal TcInfo = sigNameOpt: (string * QualifiedNameOfFile) option graphNode: NodeToTypeCheck option + + stateContainsNodes: Set } member x.TcDiagnostics = Array.concat (List.rev x.tcDiagnosticsRev) @@ -339,6 +341,7 @@ type internal TransparentCompiler tcDependencyFiles = basicDependencies sigNameOpt = None graphNode = None + stateContainsNodes = Set.empty } return tcImports, tcInfo @@ -774,8 +777,8 @@ type internal TransparentCompiler let debugGraph = nodeGraph |> Graph.map (function - | NodeToTypeCheck.PhysicalFile i -> i, fileNames[i] - | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF : {fileNames[i]}") + | NodeToTypeCheck.PhysicalFile i -> i, $"[{i}] {fileNames[i]}" + | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF [{i}] : {fileNames[i]}") |> Graph.serialiseToMermaid Trace.TraceInformation("\n" + debugGraph) @@ -876,11 +879,14 @@ type internal TransparentCompiler let mergeIntermediateResults = Array.fold (fun (tcInfos: TcInfo list) (tcIntermediate: TcIntermediate) -> - match tcInfos with - | [] -> failwith "need initial tcInfo" - | tcInfo :: rest -> - let (Finisher (node = node; finisher = finisher)) = tcIntermediate.finisher - + let (Finisher (node = node; finisher = finisher)) = tcIntermediate.finisher + match tcInfos, node with + | [], _ -> failwith "need initial tcInfo" + | tcInfo :: _, NodeToTypeCheck.ArtificialImplFile idx + when tcInfo.stateContainsNodes.Contains (NodeToTypeCheck.PhysicalFile (idx + 1)) -> + Trace.TraceInformation $"Filtering out {node}" + tcInfos + | tcInfo :: rest, _ -> let (tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile), tcState = tcInfo.tcState |> finisher @@ -898,6 +904,7 @@ type internal TransparentCompiler tcDependencyFiles = tcIntermediate.tcDependencyFiles @ tcInfo.tcDependencyFiles latestCcuSigForFile = Some ccuSigForFile graphNode = Some node + stateContainsNodes = tcInfo.stateContainsNodes |> Set.add node } :: tcInfo :: rest) @@ -908,8 +915,17 @@ type internal TransparentCompiler | [], _ -> return tcInfos | _, [] -> return failwith "need initial tcInfo" | layer :: rest, tcInfo :: _ -> + Trace.TraceInformation $"<><><> Processing layer: %A{layer}" let! results = layer + |> Seq.filter (function + | NodeToTypeCheck.ArtificialImplFile idx -> + // When we have the actual PhysicalFile in the same layer as the corresponding ArtificialImplFile then we don't apply the artificial's file finisher because it's not needed and it would lead to doubling of the results + if layer.Contains(NodeToTypeCheck.PhysicalFile(idx + 1)) then + Trace.TraceInformation $"Filtering out AIF [{idx}]" + + not (layer.Contains(NodeToTypeCheck.PhysicalFile(idx + 1))) + | _ -> true) |> Seq.map (fun fileNode -> match fileNode with @@ -920,6 +936,7 @@ type internal TransparentCompiler | NodeToTypeCheck.ArtificialImplFile fileIndex -> let finisher tcState = + Trace.TraceInformation $"* * * * {fileNode} Finisher" let parsedInput, _parseErrors, _ = parsedInputs[fileIndex] let prefixPathOpt = None // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. @@ -946,13 +963,13 @@ type internal TransparentCompiler return! results - |> Array.filter (function - | { - finisher = Finisher(node = NodeToTypeCheck.ArtificialImplFile idx) - } -> - // When we have the actual PhysicalFile in the same layer as the corresponding ArtificialImplFile then we don't apply the artificial's file finisher because it's not needed and it would lead to doubling of the results - not (layer.Contains(NodeToTypeCheck.PhysicalFile(idx + 1))) - | _ -> true) + //|> Array.filter (function + // | { + // finisher = Finisher(node = NodeToTypeCheck.ArtificialImplFile idx) + // } -> + // // When we have the actual PhysicalFile in the same layer as the corresponding ArtificialImplFile then we don't apply the artificial's file finisher because it's not needed and it would lead to doubling of the results + // not (layer.Contains(NodeToTypeCheck.PhysicalFile(idx + 1))) + // | _ -> true) |> mergeIntermediateResults tcInfos |> processLayer rest } diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index d68aeea6e21..9a0958303c1 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -335,7 +335,7 @@ let ``Multi-project`` signatureFiles = with Source = "let f (x: int) = x" SignatureFile = sigFile }, - { sourceFile "LibB" ["LibA"] with SignatureFile = No }//, + { sourceFile "LibB" ["LibA"] with SignatureFile = sigFile }//, //{ sourceFile "LibC" ["LibA"] with SignatureFile = sigFile }, //{ sourceFile "LibD" ["LibB"; "LibC"] with SignatureFile = sigFile } ) From a94d7b5629f0b5efab0f704e37e6b1ddac36aa02 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 29 Jun 2023 18:12:44 +0200 Subject: [PATCH 072/222] wip --- .../Driver/GraphChecking/GraphProcessing.fs | 136 ++++++++ .../Driver/GraphChecking/GraphProcessing.fsi | 6 + src/Compiler/Service/TransparentCompiler.fs | 290 +++++++++++++----- .../FSharpChecker/TransparentCompiler.fs | 16 +- 4 files changed, 369 insertions(+), 79 deletions(-) diff --git a/src/Compiler/Driver/GraphChecking/GraphProcessing.fs b/src/Compiler/Driver/GraphChecking/GraphProcessing.fs index 2cadaf25ac5..6092432ff61 100644 --- a/src/Compiler/Driver/GraphChecking/GraphProcessing.fs +++ b/src/Compiler/Driver/GraphChecking/GraphProcessing.fs @@ -162,3 +162,139 @@ let processGraph<'Item, 'Result when 'Item: equality and 'Item: comparison> node.Info.Item, result) |> Seq.sortBy fst |> Seq.toArray + + +let processGraphAsync<'Item, 'Result when 'Item: equality and 'Item: comparison> + (graph: Graph<'Item>) + (work: ('Item -> ProcessedNode<'Item, 'Result>) -> NodeInfo<'Item> -> Async<'Result>) + : Async<('Item * 'Result)[]> = async { + let transitiveDeps = graph |> Graph.transitive + let dependants = graph |> Graph.reverse + // Cancellation source used to signal either an exception in one of the items or end of processing. + let! parentCt = Async.CancellationToken + use localCts = new CancellationTokenSource() + use cts = CancellationTokenSource.CreateLinkedTokenSource(parentCt, localCts.Token) + + let makeNode (item: 'Item) : GraphNode<'Item, 'Result> = + let info = + let exists = graph.ContainsKey item + + if + not exists + || not (transitiveDeps.ContainsKey item) + || not (dependants.ContainsKey item) + then + printfn $"Unexpected inconsistent state of the graph for item '{item}'" + + { + Item = item + Deps = graph[item] + TransitiveDeps = transitiveDeps[item] + Dependants = dependants[item] + } + + { + Info = info + Result = None + ProcessedDepsCount = IncrementableInt(0) + } + + let nodes = graph.Keys |> Seq.map (fun item -> item, makeNode item) |> readOnlyDict + + let lookupMany items = + items |> Array.map (fun item -> nodes[item]) + + let leaves = + nodes.Values |> Seq.filter (fun n -> n.Info.Deps.Length = 0) |> Seq.toArray + + let getItemPublicNode item = + let node = nodes[item] + + { + ProcessedNode.Info = node.Info + ProcessedNode.Result = + node.Result + |> Option.defaultWith (fun () -> failwith $"Results for item '{node.Info.Item}' are not yet available") + } + + let processedCount = IncrementableInt(0) + + /// Create a setter and getter for an exception raised in one of the work items. + /// Only the first exception encountered is stored - this can cause non-deterministic errors if more than one item fails. + let raiseExn, getExn = + let mutable exn: ('Item * System.Exception) option = None + let lockObj = obj () + // Only set the exception if it hasn't been set already + let setExn newExn = + lock lockObj (fun () -> + match exn with + | Some _ -> () + | None -> exn <- newExn + + localCts.Cancel()) + + let getExn () = exn + setExn, getExn + + let incrementProcessedNodesCount () = + if processedCount.Increment() = nodes.Count then + localCts.Cancel() + + let rec queueNode node = + Async.Start( + async { + let! res = processNode node |> Async.Catch + + match res with + | Choice1Of2 () -> () + | Choice2Of2 ex -> raiseExn (Some(node.Info.Item, ex)) + }, + cts.Token + ) + + and processNode (node: GraphNode<'Item, 'Result>) : Async = async { + + let info = node.Info + + let! singleRes = work getItemPublicNode info + node.Result <- Some singleRes + + let unblockedDependants = + node.Info.Dependants + |> lookupMany + // For every dependant, increment its number of processed dependencies, + // and filter dependants which now have all dependencies processed (but didn't before). + |> Array.filter (fun dependant -> + let pdc = dependant.ProcessedDepsCount.Increment() + // Note: We cannot read 'dependant.ProcessedDepsCount' again to avoid returning the same item multiple times. + pdc = dependant.Info.Deps.Length) + + unblockedDependants |> Array.iter queueNode + incrementProcessedNodesCount () + } + + leaves |> Array.iter queueNode + + // Wait for end of processing, an exception, or an external cancellation request. + + cts.Token.WaitHandle.WaitOne() |> ignore + // If we stopped early due to external cancellation, throw. + parentCt.ThrowIfCancellationRequested() + + // If we stopped early due to an exception, reraise it. + match getExn () with + | None -> () + | Some (item, ex) -> raise (System.Exception($"Encountered exception when processing item '{item}'", ex)) + + // All calculations succeeded - extract the results and sort in input order. + return + nodes.Values + |> Seq.map (fun node -> + let result = + node.Result + |> Option.defaultWith (fun () -> failwith $"Unexpected lack of result for item '{node.Info.Item}'") + + node.Info.Item, result) + |> Seq.sortBy fst + |> Seq.toArray +} \ No newline at end of file diff --git a/src/Compiler/Driver/GraphChecking/GraphProcessing.fsi b/src/Compiler/Driver/GraphChecking/GraphProcessing.fsi index cb9a95a59f8..3eb9cc1fbdd 100644 --- a/src/Compiler/Driver/GraphChecking/GraphProcessing.fsi +++ b/src/Compiler/Driver/GraphChecking/GraphProcessing.fsi @@ -33,3 +33,9 @@ val processGraph<'Item, 'Result when 'Item: equality and 'Item: comparison> : work: (('Item -> ProcessedNode<'Item, 'Result>) -> NodeInfo<'Item> -> 'Result) -> parentCt: CancellationToken -> ('Item * 'Result)[] + + +val processGraphAsync<'Item, 'Result when 'Item: equality and 'Item: comparison> : + graph: Graph<'Item> -> + work: (('Item -> ProcessedNode<'Item, 'Result>) -> NodeInfo<'Item> -> Async<'Result>) -> + Async<('Item * 'Result)[]> diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index ad13adde1d7..062b969f369 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -5,6 +5,7 @@ open System.Collections.Generic open System.Runtime.CompilerServices open System.Diagnostics open System.IO +open System.Threading open Internal.Utilities.Collections open Internal.Utilities.Library @@ -132,6 +133,111 @@ type internal Extensions = static member Key(fileSnapshots: FSharpFileSnapshot list) = fileSnapshots |> List.map (fun f -> f.Key) + +module private TypeCheckingGraphProcessing = + open FSharp.Compiler.GraphChecking.GraphProcessing + + // TODO Do we need to suppress some error logging if we + // TODO apply the same partial results multiple times? + // TODO Maybe we can enable logging only for the final fold + /// + /// Combine type-checking results of dependencies needed to type-check a 'higher' node in the graph + /// + /// Initial state + /// Direct dependencies of a node + /// Transitive dependencies of a node + /// A way to fold a single result into existing state + let private combineResults + (emptyState: TcInfo) + (deps: ProcessedNode> array) + (transitiveDeps: ProcessedNode> array) + (folder: TcInfo -> Finisher -> TcInfo) + : TcInfo = + match deps with + | [||] -> emptyState + | _ -> + // Instead of starting with empty state, + // reuse state produced by the dependency with the biggest number of transitive dependencies. + // This is to reduce the number of folds required to achieve the final state. + let biggestDependency = + let sizeMetric (node: ProcessedNode<_, _>) = node.Info.TransitiveDeps.Length + deps |> Array.maxBy sizeMetric + + let firstState = biggestDependency.Result |> fst + + // Find items not already included in the state. + let itemsPresent = + set + [| + yield! biggestDependency.Info.TransitiveDeps + yield biggestDependency.Info.Item + |] + + let resultsToAdd = + transitiveDeps + |> Array.filter (fun dep -> itemsPresent.Contains dep.Info.Item = false) + |> Array.distinctBy (fun dep -> dep.Info.Item) + |> Array.sortWith (fun a b -> + // We preserve the order in which items are folded to the state. + match a.Info.Item, b.Info.Item with + | NodeToTypeCheck.PhysicalFile aIdx, NodeToTypeCheck.PhysicalFile bIdx + | NodeToTypeCheck.ArtificialImplFile aIdx, NodeToTypeCheck.ArtificialImplFile bIdx -> aIdx.CompareTo bIdx + | NodeToTypeCheck.PhysicalFile _, NodeToTypeCheck.ArtificialImplFile _ -> -1 + | NodeToTypeCheck.ArtificialImplFile _, NodeToTypeCheck.PhysicalFile _ -> 1) + |> Array.map (fun dep -> dep.Result |> snd) + + // Fold results not already included and produce the final state + let state = Array.fold folder firstState resultsToAdd + state + + /// + /// Process a graph of items. + /// A version of 'GraphProcessing.processGraph' with a signature specific to type-checking. + /// + let processTypeCheckingGraph + (graph: Graph) + (work: NodeToTypeCheck -> TcInfo -> NodeCode>) + (emptyState: TcInfo) + : NodeCode<(int * PartialResult) list * TcInfo> = node { + + let workWrapper + (getProcessedNode: NodeToTypeCheck -> ProcessedNode>) + (node: NodeInfo) + : Async> = async { + let folder (state: TcInfo) (Finisher (finisher = finisher)) : TcInfo = finisher state |> snd + let deps = node.Deps |> Array.except [| node.Item |] |> Array.map getProcessedNode + + let transitiveDeps = + node.TransitiveDeps + |> Array.except [| node.Item |] + |> Array.map getProcessedNode + + let inputState = combineResults emptyState deps transitiveDeps folder + + let! singleRes = work node.Item inputState |> Async.AwaitNodeCode + let state = folder inputState singleRes + return state, singleRes + } + + let! results = processGraphAsync graph workWrapper |> NodeCode.AwaitAsync + + let finalFileResults, state: (int * PartialResult) list * TcInfo = + (([], emptyState), + results + |> Array.choose (fun (item, res) -> + match item with + | NodeToTypeCheck.ArtificialImplFile _ -> None + | NodeToTypeCheck.PhysicalFile file -> Some(file, res))) + ||> Array.fold (fun (fileResults, state) (item, (_, Finisher (finisher = finisher))) -> + let fileResult, state = finisher state + (item, fileResult) :: fileResults, state) + + return finalFileResults, state + } + +open TypeCheckingGraphProcessing + + type internal TransparentCompiler ( legacyReferenceResolver, @@ -886,7 +992,7 @@ type internal TransparentCompiler when tcInfo.stateContainsNodes.Contains (NodeToTypeCheck.PhysicalFile (idx + 1)) -> Trace.TraceInformation $"Filtering out {node}" tcInfos - | tcInfo :: rest, _ -> + | tcInfo :: rest, _ -> let (tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile), tcState = tcInfo.tcState |> finisher @@ -980,7 +1086,7 @@ type internal TransparentCompiler } // Type check everything that is needed to check given file - let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = + let ComputeTcPrior' (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = let priorSnapshot = projectSnapshot.UpTo file.Source.FileName let key = priorSnapshot.Key @@ -1015,6 +1121,111 @@ type internal TransparentCompiler } ) + let processGraphNode projectSnapshot bootstrapInfo (parsedInputs: _ array) dependencyFiles (fileNode: NodeToTypeCheck) tcInfo = node { + + match fileNode with + | NodeToTypeCheck.PhysicalFile index -> + let input, parseErrors, _ = parsedInputs[index] + let! tcIntermediate = ComputeTcIntermediate projectSnapshot dependencyFiles index (input, parseErrors) bootstrapInfo tcInfo + let (Finisher(node=node; finisher=finisher)) = tcIntermediate.finisher + return + Finisher( + node, + (fun tcInfo -> + let partialResult, tcState = finisher tcInfo.tcState + + let tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile = partialResult + + let tcEnvAtEndOfFile = + if keepAllBackgroundResolutions then + tcEnv + else + tcState.TcEnvFromImpls + + partialResult, + { tcInfo with + tcState = tcState + tcEnvAtEndOfFile = tcEnvAtEndOfFile + topAttribs = Some topAttribs + tcDiagnosticsRev = tcIntermediate.tcDiagnosticsRev @ tcInfo.tcDiagnosticsRev + tcDependencyFiles = tcIntermediate.tcDependencyFiles @ tcInfo.tcDependencyFiles + latestCcuSigForFile = Some ccuSigForFile + graphNode = Some node + stateContainsNodes = tcInfo.stateContainsNodes |> Set.add node + } )) + + | NodeToTypeCheck.ArtificialImplFile index -> + return + Finisher( + fileNode, + (fun tcInfo -> + + let parsedInput, _parseErrors, _ = parsedInputs[index] + let prefixPathOpt = None + // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. + let partialResult, tcState = + AddSignatureResultToTcImplEnv + (bootstrapInfo.TcImports, + bootstrapInfo.TcGlobals, + prefixPathOpt, + TcResultsSink.NoSink, + tcInfo.tcState, + parsedInput) + tcInfo.tcState + + + let tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile = partialResult + + let tcEnvAtEndOfFile = + if keepAllBackgroundResolutions then + tcEnv + else + tcState.TcEnvFromImpls + + partialResult, + { tcInfo with + tcState = tcState + tcEnvAtEndOfFile = tcEnvAtEndOfFile + topAttribs = Some topAttribs + latestCcuSigForFile = Some ccuSigForFile + graphNode = Some fileNode + stateContainsNodes = tcInfo.stateContainsNodes |> Set.add fileNode + } )) + + } + + // Type check everything that is needed to check given file + let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = + + let priorSnapshot = projectSnapshot.UpTo file.Source.FileName + let key = priorSnapshot.Key + + TcPriorCache.Get( + key, + node { + use _ = + Activity.start "ComputeTcPrior" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName |] + + // parse required files + let files = + seq { + yield! bootstrapInfo.SourceFiles |> Seq.takeWhile ((<>) file) + file + } + + let! parsedInputs = files |> Seq.map (ComputeParseFile bootstrapInfo) |> NodeCode.Parallel + + let! graph, dependencyFiles = + ComputeDependencyGraphForFile priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig + + + + let! _results, tcInfo = processTypeCheckingGraph graph (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles) bootstrapInfo.InitialTcInfo + + return tcInfo, dependencyFiles + } + ) + let getParseResult (bootstrapInfo: BootstrapInfo) creationDiags fileName = node { let file = bootstrapInfo.GetFile fileName @@ -1048,54 +1259,6 @@ type internal TransparentCompiler let parseTree = EmptyParsedInput(fileName, (false, false)) FSharpParseFileResults(diagnostics, parseTree, true, [||]) - let ComputeParseAndCheckFileInProject' (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = - let key = fileName, projectSnapshot.Key - - ParseAndCheckFileInProjectCache.Get( - key, - node { - - use _ = - Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - - match! ComputeBootstrapInfo projectSnapshot with - | None, creationDiags -> return emptyParseResult fileName creationDiags, FSharpCheckFileAnswer.Aborted - - | Some bootstrapInfo, creationDiags -> - - let! parseResults, sourceText = getParseResult bootstrapInfo creationDiags fileName - - let file = bootstrapInfo.GetFile fileName - let! tcInfo, _ = ComputeTcPrior file bootstrapInfo projectSnapshot - - let! checkResults = - FSharpCheckFileResults.CheckOneFile( - parseResults, - sourceText, - fileName, - projectSnapshot.ProjectFileName, - bootstrapInfo.TcConfig, - bootstrapInfo.TcGlobals, - bootstrapInfo.TcImports, - tcInfo.tcState, - tcInfo.moduleNamesDict, - bootstrapInfo.LoadClosure, - tcInfo.TcDiagnostics, - projectSnapshot.IsIncompleteTypeCheckEnvironment, - projectSnapshot.ToOptions(), - None, - Array.ofList tcInfo.tcDependencyFiles, - creationDiags, - parseResults.Diagnostics, - keepAssemblyContents, - suggestNamesForErrors - ) - |> NodeCode.FromCancellable - - return parseResults, FSharpCheckFileAnswer.Succeeded checkResults - } - ) - let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = let key = fileName, projectSnapshot.Key @@ -1207,19 +1370,11 @@ type internal TransparentCompiler let! graph, dependencyFiles = ComputeDependencyGraphForProject projectSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig - // layers that can be processed in parallel - let layers = Graph.leafSequence graph |> Seq.toList - - let! tcInfos, _ = typeCheckLayers bootstrapInfo projectSnapshot parsedInputs dependencyFiles layers + //// layers that can be processed in parallel + //let layers = Graph.leafSequence graph |> Seq.toList - return - tcInfos - |> List.filter (function - | { - graphNode = Some (NodeToTypeCheck.PhysicalFile _) - } -> true - | _ -> false) - |> List.rev + //let! tcInfos, _ = typeCheckLayers bootstrapInfo projectSnapshot parsedInputs dependencyFiles layers + return! processTypeCheckingGraph graph (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles) bootstrapInfo.InitialTcInfo } ) @@ -1227,20 +1382,13 @@ type internal TransparentCompiler ProjectExtrasCache.Get( projectSnapshot.Key, node { - let! tcInfos = ComputeParseAndCheckAllFilesInProject bootstrapInfo projectSnapshot + let! results, finalInfo = ComputeParseAndCheckAllFilesInProject bootstrapInfo projectSnapshot let assemblyName = bootstrapInfo.AssemblyName let tcConfig = bootstrapInfo.TcConfig let tcGlobals = bootstrapInfo.TcGlobals - let results = - [ - for tcInfo in tcInfos -> - tcInfo.tcEnvAtEndOfFile, defaultArg tcInfo.topAttribs EmptyTopAttrs, None, tcInfo.latestCcuSigForFile - ] - - // Get the state at the end of the type-checking of the last file - let finalInfo = List.last tcInfos + let results = results |> Seq.sortBy fst |> Seq.map snd |> Seq.toList // Finish the checking let (_tcEnvAtEndOfLastFile, topAttrs, _mimpls, _), tcState = diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 9a0958303c1..a6909089bd3 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -335,23 +335,23 @@ let ``Multi-project`` signatureFiles = with Source = "let f (x: int) = x" SignatureFile = sigFile }, - { sourceFile "LibB" ["LibA"] with SignatureFile = sigFile }//, - //{ sourceFile "LibC" ["LibA"] with SignatureFile = sigFile }, - //{ sourceFile "LibD" ["LibB"; "LibC"] with SignatureFile = sigFile } + { sourceFile "LibB" ["LibA"] with SignatureFile = sigFile }, + { sourceFile "LibC" ["LibA"] with SignatureFile = sigFile }, + { sourceFile "LibD" ["LibB"; "LibC"] with SignatureFile = sigFile } ) let project = { SyntheticProject.Create("app", - sourceFile "A" ["LibB"]//, - //sourceFile "B" ["A"; "LibB"], - //sourceFile "C" ["A"; "LibC"], - //sourceFile "D" ["A"; "LibD"] + sourceFile "A" ["LibB"], + sourceFile "B" ["A"; "LibB"], + sourceFile "C" ["A"; "LibC"], + sourceFile "D" ["A"; "LibD"] ) with DependsOn = [library] } ProjectWorkflowBuilder(project, useTransparentCompiler = true) { updateFile "LibA" updatePublicSurface - checkFile "A" expectOk + checkFile "D" expectOk } From cb737b3bd536cf6a0b80d5040113ee8a1483f49d Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 12 Jul 2023 18:55:40 +0200 Subject: [PATCH 073/222] wip --- src/Compiler/Service/TransparentCompiler.fs | 164 +------- .../FSharpChecker/TransparentCompiler.fs | 349 +++++++++++++++--- .../ProjectGeneration.fs | 23 ++ 3 files changed, 334 insertions(+), 202 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 1a3b66eb415..776a5017429 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -917,6 +917,7 @@ type internal TransparentCompiler TcIntermediateCache.Get( projectSnapshot.OnlyWith(dependencyFiles).Key, + //projectSnapshot.UpTo(fileIndex).Key, node { let input = parsedInput let fileName = input.FileName @@ -983,147 +984,6 @@ type internal TransparentCompiler } ) - let mergeIntermediateResults = - Array.fold (fun (tcInfos: TcInfo list) (tcIntermediate: TcIntermediate) -> - let (Finisher (node = node; finisher = finisher)) = tcIntermediate.finisher - - match tcInfos, node with - | [], _ -> failwith "need initial tcInfo" - | tcInfo :: _, NodeToTypeCheck.ArtificialImplFile idx when - tcInfo.stateContainsNodes.Contains(NodeToTypeCheck.PhysicalFile(idx + 1)) - -> - Trace.TraceInformation $"Filtering out {node}" - tcInfos - | tcInfo :: rest, _ -> - let (tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile), tcState = - tcInfo.tcState |> finisher - - let tcEnvAtEndOfFile = - if keepAllBackgroundResolutions then - tcEnv - else - tcState.TcEnvFromImpls - - { tcInfo with - tcState = tcState - tcEnvAtEndOfFile = tcEnvAtEndOfFile - topAttribs = Some topAttribs - tcDiagnosticsRev = tcIntermediate.tcDiagnosticsRev @ tcInfo.tcDiagnosticsRev - tcDependencyFiles = tcIntermediate.tcDependencyFiles @ tcInfo.tcDependencyFiles - latestCcuSigForFile = Some ccuSigForFile - graphNode = Some node - stateContainsNodes = tcInfo.stateContainsNodes |> Set.add node - } - :: tcInfo :: rest) - - let typeCheckLayers bootstrapInfo projectSnapshot (parsedInputs: array<_>) graph layers = - let rec processLayer (layers: Set list) tcInfos = - node { - match layers, tcInfos with - | [], _ -> return tcInfos - | _, [] -> return failwith "need initial tcInfo" - | layer :: rest, tcInfo :: _ -> - Trace.TraceInformation $"<><><> Processing layer: %A{layer}" - - let! results = - layer - |> Seq.filter (function - | NodeToTypeCheck.ArtificialImplFile idx -> - // When we have the actual PhysicalFile in the same layer as the corresponding ArtificialImplFile then we don't apply the artificial's file finisher because it's not needed and it would lead to doubling of the results - if layer.Contains(NodeToTypeCheck.PhysicalFile(idx + 1)) then - Trace.TraceInformation $"Filtering out AIF [{idx}]" - - not (layer.Contains(NodeToTypeCheck.PhysicalFile(idx + 1))) - | _ -> true) - |> Seq.map (fun fileNode -> - - match fileNode with - | NodeToTypeCheck.PhysicalFile fileIndex -> - let parsedInput, parseErrors, _ = parsedInputs[fileIndex] - - ComputeTcIntermediate projectSnapshot graph fileIndex (parsedInput, parseErrors) bootstrapInfo tcInfo - | NodeToTypeCheck.ArtificialImplFile fileIndex -> - - let finisher tcState = - Trace.TraceInformation $"* * * * {fileNode} Finisher" - let parsedInput, _parseErrors, _ = parsedInputs[fileIndex] - let prefixPathOpt = None - // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. - AddSignatureResultToTcImplEnv - (bootstrapInfo.TcImports, - bootstrapInfo.TcGlobals, - prefixPathOpt, - TcResultsSink.NoSink, - tcState, - parsedInput) - tcState - - let tcIntermediate = - { - finisher = Finisher(fileNode, finisher) - moduleNamesDict = tcInfo.moduleNamesDict - tcDiagnosticsRev = [] - tcDependencyFiles = [] - sink = Unchecked.defaultof<_> // TODO: something nicer - } - - node.Return(tcIntermediate)) - |> NodeCode.Parallel - - return! - results - //|> Array.filter (function - // | { - // finisher = Finisher(node = NodeToTypeCheck.ArtificialImplFile idx) - // } -> - // // When we have the actual PhysicalFile in the same layer as the corresponding ArtificialImplFile then we don't apply the artificial's file finisher because it's not needed and it would lead to doubling of the results - // not (layer.Contains(NodeToTypeCheck.PhysicalFile(idx + 1))) - // | _ -> true) - |> mergeIntermediateResults tcInfos - |> processLayer rest - } - - node { - let! tcInfo = processLayer layers [ bootstrapInfo.InitialTcInfo ] - return tcInfo, graph - } - - // Type check everything that is needed to check given file - let ComputeTcPrior' (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = - - let priorSnapshot = projectSnapshot.UpTo file.Source.FileName - let key = priorSnapshot.Key - - TcPriorCache.Get( - key, - node { - use _ = - Activity.start "ComputeTcPrior" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName |] - - // parse required files - let files = - seq { - yield! bootstrapInfo.SourceFiles |> Seq.takeWhile ((<>) file) - file - } - - let! parsedInputs = files |> Seq.map (ComputeParseFile bootstrapInfo) |> NodeCode.Parallel - - let! graph, dependencyFiles = - ComputeDependencyGraphForFile priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig - - // layers that can be processed in parallel - let layers = Graph.leafSequence graph |> Seq.toList - - // remove the final layer, which should be the target file - let layers = layers |> List.take (layers.Length - 1) - - let! tcInfos, graph = typeCheckLayers bootstrapInfo priorSnapshot parsedInputs dependencyFiles layers - - return tcInfos.Head, graph - } - ) - let processGraphNode projectSnapshot bootstrapInfo (parsedInputs: _ array) dependencyFiles (fileNode: NodeToTypeCheck) tcInfo = node { @@ -1223,14 +1083,17 @@ type internal TransparentCompiler let! graph, dependencyFiles = ComputeDependencyGraphForFile priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig + //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig - let! _results, tcInfo = + let! results, tcInfo = processTypeCheckingGraph graph (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles) bootstrapInfo.InitialTcInfo - return tcInfo, dependencyFiles + let lastResult = results |> List.head |> snd + + return lastResult, tcInfo, dependencyFiles } ) @@ -1287,18 +1150,17 @@ type internal TransparentCompiler let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file let! parseResults, _sourceText = getParseResult bootstrapInfo creationDiags fileName - let! priorTcInfo, graph = ComputeTcPrior file bootstrapInfo projectSnapshot + let! result, priorTcInfo, graph = ComputeTcPrior file bootstrapInfo projectSnapshot + + let (tcEnv, _topAttribs, checkedImplFileOpt, ccuSigForFile) = result + + let tcState = priorTcInfo.tcState let fileIndex = projectSnapshot.IndexOf fileName let! tcIntermediate = ComputeTcIntermediate projectSnapshot graph fileIndex (parseTree, parseDiagnostics) bootstrapInfo priorTcInfo - let (Finisher (finisher = finisher)) = tcIntermediate.finisher - - let (tcEnv, _topAttribs, checkedImplFileOpt, ccuSigForFile), tcState = - priorTcInfo.tcState |> finisher - let sink = tcIntermediate.sink let tcResolutions = sink.GetResolutions() @@ -1367,7 +1229,7 @@ type internal TransparentCompiler node { use _ = Activity.start - "ComputeParseAndCheckFullProject" + "ComputeParseAndCheckAllFilesInProject" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] let! parsedInputs = @@ -1568,7 +1430,7 @@ type internal TransparentCompiler let file = bootstrapInfo.GetFile fileName - let! tcInfo, graph = ComputeTcPrior file bootstrapInfo projectSnapshot + let! _, tcInfo, graph = ComputeTcPrior file bootstrapInfo projectSnapshot let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file let fileIndex = projectSnapshot.IndexOf fileName diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index a6909089bd3..a503b6e51c6 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -5,11 +5,15 @@ open System.Diagnostics open FSharp.Compiler.CodeAnalysis open Internal.Utilities.Collections open FSharp.Compiler.CodeAnalysis.TransparentCompiler +open Internal.Utilities.Library.Extras open Xunit open FSharp.Test.ProjectGeneration open System.IO +open Microsoft.CodeAnalysis +open System +open System.Threading.Tasks [] @@ -52,7 +56,7 @@ let ``Parallel processing`` () = sourceFile "D" ["A"], sourceFile "E" ["B"; "C"; "D"]) - ProjectWorkflowBuilder(project, useTransparentCompiler = false) { + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { checkFile "E" expectOk updateFile "A" updatePublicSurface saveFile "A" @@ -201,6 +205,33 @@ let ``Changes in a referenced project`` () = } +[] +let ``File is not checked twice`` () = + + let cacheEvents = ResizeArray() + + testWorkflow() { + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.CacheEvent.Add cacheEvents.Add + }) + updateFile "First" updatePublicSurface + checkFile "Third" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheEvents + |> Seq.choose (function + | ("TcIntermediate", e, k) -> Some ((k :?> FSharpProjectSnapshotKey).LastFile |> fst |> Path.GetFileName, e) + | _ -> None) + |> Seq.groupBy fst + |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) + |> Map + + Assert.Equal([Started; Finished], intermediateTypeChecks["FileFirst.fs"]) + Assert.Equal([Started; Finished], intermediateTypeChecks["FileThird.fs"]) + [] let ``We don't check files that are not depended on`` () = let project = SyntheticProject.Create( @@ -355,22 +386,35 @@ let ``Multi-project`` signatureFiles = } -type Operation = Update | Check | Add | Remove +type ProjectAction = Get | Modify of (SyntheticProject -> SyntheticProject) +type ProjectModificaiton = Update of int | Add | Remove +type ProjectRequest = ProjectAction * AsyncReplyChannel + +[] +type SignatureFiles = Yes = 1 | No = 2 | Some = 3 //[] -//[] -//[] +//[] +//[] +//[] let Fuzzing signatureFiles = let seed = System.Random().Next() + //let seed = 1093747864 let rng = System.Random(int seed) - let fileCount = 50 + let fileCount = 30 let maxDepsPerFile = 3 - let threads = 4 - let operationCount = 50 + let checkingThreads = 20 + let maxModificationDelayMs = 50 + let maxCheckingDelayMs = 5 + let runTimeMs = 1000 + let signatureFileModificationProbability = 0.25 let fileName i = sprintf $"F%03d{i}" + //let extraCode = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ "src" ++ "Compiler" ++ "Utilities" ++ "EditDistance.fs" |> File.ReadAllLines |> Seq.skip 5 |> String.concat "\n" + let extraCode = "" + let files = [| for i in 1 .. fileCount do let name = fileName i @@ -378,65 +422,268 @@ let Fuzzing signatureFiles = for _ in 1 .. maxDepsPerFile do if i > 1 then fileName <| rng.Next(1, i) ] - let signature = if signatureFiles then AutoGenerated else No - - { sourceFile name deps with SignatureFile = signature } + let signature = + match signatureFiles with + | SignatureFiles.Yes -> AutoGenerated + | SignatureFiles.Some when rng.NextDouble() < 0.5 -> AutoGenerated + | _ -> No + + { sourceFile name deps + with + SignatureFile = signature + ExtraSource = extraCode } |] - let project = SyntheticProject.Create(files) + let initialProject = SyntheticProject.Create("TCFuzzing", files) + + let builder = ProjectWorkflowBuilder(initialProject, useTransparentCompiler = true, autoStart = false) + let checker = builder.Checker - let builder = ProjectWorkflowBuilder(project, useTransparentCompiler = true, autoStart = false) + let initialProject = initialProject |> absorbAutoGeneratedSignatures checker |> Async.RunSynchronously - let operationProbabilities = [ - Update, 20 - Check, 20 - // Add, 2 - // Remove, 1 + let projectAgent = MailboxProcessor.Start(fun (inbox: MailboxProcessor) -> + let rec loop project = + async { + let! action, reply = inbox.Receive() + let! project = + match action with + | Modify f -> async { + let p = f project + do! saveProject p false checker + return p } + | Get -> async.Return project + reply.Reply project + return! loop project + } + loop initialProject) + + let getProject () = + projectAgent.PostAndAsyncReply(pair Get) + + let modifyProject f = + projectAgent.PostAndAsyncReply(pair(Modify f)) |> Async.Ignore + + let modificationProbabilities = [ + Update 1, 80 + Update 2, 5 + Update 10, 5 + Add, 2 + Remove, 1 ] - let operationPicker = [| - for op, prob in operationProbabilities do + let modificationPicker = [| + for op, prob in modificationProbabilities do for _ in 1 .. prob do op |] - let getRandomOperation () = operationPicker[rng.Next(0, operationPicker.Length)] + let addComment s = $"{s}\n\n// {rng.NextDouble()}" + let modifyImplFile f = { f with ExtraSource = f.ExtraSource |> addComment } + let modifySigFile f = { f with SignatureFile = Custom (f.SignatureFile.CustomText |> addComment) } + + let getRandomModification () = modificationPicker[rng.Next(0, modificationPicker.Length)] let getRandomFile (project: SyntheticProject) = project.SourceFiles[rng.Next(0, project.SourceFiles.Length)].Id - let rec fuzzer n actx = async { - if n >= operationCount then - return! actx - else - let! ctx = actx - let project = ctx.Project - return! fuzzer (n + 1) <| - match getRandomOperation () with - | Update -> - let file = getRandomFile project - builder.UpdateFile(actx, file, updatePublicSurface) - | Check -> - let file = getRandomFile project - builder.CheckFile(actx, file, expectOk) // TODO: add timeout/cancelation - | Add -> - let file = getRandomFile project - let newFile = sourceFile (fileName <| project.SourceFiles.Length + 1) [] - builder.AddFileAbove(actx, file, newFile) + let log = ConcurrentBag() + + let modificationLoop = async { + while true do + do! Async.Sleep (rng.Next maxModificationDelayMs) + let modify project = + match getRandomModification() with + | Update n -> + let files = Set [ for _ in 1..n -> getRandomFile project ] + (project, files) + ||> Seq.fold (fun p fileId -> + let project, file = project.FindInAllProjects fileId + let opName, f = + if file.HasSignatureFile && rng.NextDouble() < signatureFileModificationProbability + then nameof modifySigFile, modifySigFile + else nameof modifyImplFile, modifyImplFile + log.Add $"{project.Name} -> {fileId} |> {opName}" + p |> updateFileInAnyProject fileId f) + | Add | Remove -> - let file = getRandomFile project - builder.RemoveFile(actx, file) + // TODO: + project + do! modifyProject modify + } + + let checkingLoop = async { + while true do + let! project = getProject() + let file = project |> getRandomFile + + // TODO: timeout & cancelation + log.Add $"Started checking {file}" + let! result = checker |> checkFile file project + + log.Add $"Checked {file} %A{snd result}" + expectOk result () + + do! Async.Sleep (rng.Next maxCheckingDelayMs) } - let initialCtx = builder.Yield() + async { + let! threads = + seq { + //Async.StartChild(modificationLoop, runTimeMs) + ignore modificationLoop + for _ in 1..checkingThreads do + Async.StartChild(checkingLoop, runTimeMs) + } + |> Async.Parallel + try + do! threads |> Async.Parallel |> Async.Ignore + with + | :? TimeoutException + | :? TaskCanceledException -> () + | :? AggregateException as e when e.InnerExceptions |> Seq.exists (fun e -> e :? TaskCanceledException) -> () + | e -> failwith $"Seed: {seed}\nException: %A{e}" + } |> Async.RunSynchronously + + builder.DeleteProjectDir() - let results = - async { - let! ctx = initialCtx // Save and initial check project - return! - [1..threads] - |> Seq.map (fun _ -> fuzzer 0 (async.Return ctx)) - |> Async.Parallel - } - |> Async.RunSynchronously - ignore results \ No newline at end of file +//[] +//[] +//[] +let GiraffeFuzzing signatureFiles = + let seed = System.Random().Next() + //let seed = 1514219769 + let rng = System.Random(int seed) + + let getRandomItem (xs: 'x array) = xs[rng.Next(0, xs.Length)] + + let checkingThreads = 16 + let maxModificationDelayMs = 20 + let maxCheckingDelayMs = 40 + //let runTimeMs = 30000 + let signatureFileModificationProbability = 0.25 + let modificationLoopIterations = 100 + let checkingLoopIterations = 50 + + let giraffe = if signatureFiles then "giraffe-signatures" else "Giraffe" + let giraffeDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ giraffe ++ "src" ++ "Giraffe" + let giraffeTestsDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ giraffe ++ "tests" ++ "Giraffe.Tests" + + let giraffeProject = SyntheticProject.CreateFromRealProject giraffeDir + let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } + + let testsProject = SyntheticProject.CreateFromRealProject giraffeTestsDir + let testsProject = + { testsProject + with + OtherOptions = "--nowarn:FS3520"::testsProject.OtherOptions + DependsOn = [ giraffeProject ] + NugetReferences = giraffeProject.NugetReferences @ testsProject.NugetReferences + } + + let builder = ProjectWorkflowBuilder(testsProject, useTransparentCompiler = true, autoStart = false) + let checker = builder.Checker + + // Force creation and caching of options + SaveAndCheckProject testsProject checker |> Async.Ignore |> Async.RunSynchronously + + let projectAgent = MailboxProcessor.Start(fun (inbox: MailboxProcessor) -> + let rec loop project = + async { + let! action, reply = inbox.Receive() + let! project = + match action with + | Modify f -> async { + let p = f project + do! saveProject p false checker + return p } + | Get -> async.Return project + reply.Reply project + return! loop project + } + loop testsProject) + + let getProject () = + projectAgent.PostAndAsyncReply(pair Get) + + let modifyProject f = + projectAgent.PostAndAsyncReply(pair(Modify f)) |> Async.Ignore + + let modificationProbabilities = [ + Update 1, 80 + Update 2, 5 + Update 10, 5 + //Add, 2 + //Remove, 1 + ] + + let modificationPicker = [| + for op, prob in modificationProbabilities do + for _ in 1 .. prob do + op + |] + + let addComment s = $"{s}\n\n// {rng.NextDouble()}" + let modifyImplFile f = { f with ExtraSource = f.ExtraSource |> addComment } + let modifySigFile f = { f with SignatureFile = Custom (f.SignatureFile.CustomText |> addComment) } + + let getRandomModification () = modificationPicker |> getRandomItem + + let getRandomFile (project: SyntheticProject) = project.GetAllFiles() |> List.toArray |> getRandomItem + + let log = ConcurrentBag() + + let modificationLoop = async { + for _ in 1 .. modificationLoopIterations do + do! Async.Sleep (rng.Next maxModificationDelayMs) + let modify project = + match getRandomModification() with + | Update n -> + let files = Set [ for _ in 1..n -> getRandomFile project |> snd ] + (project, files) + ||> Seq.fold (fun p file -> + let fileId = file.Id + let project, file = project.FindInAllProjects fileId + let opName, f = + if file.HasSignatureFile && rng.NextDouble() < signatureFileModificationProbability + then nameof modifySigFile, modifySigFile + else nameof modifyImplFile, modifyImplFile + log.Add $"{DateTime.Now.ToShortTimeString()}| {project.Name} -> {fileId} |> {opName}" + p |> updateFileInAnyProject fileId f) + | Add + | Remove -> + // TODO: + project + do! modifyProject modify + } + + let checkingLoop n = async { + for _ in 1 .. checkingLoopIterations do + let! project = getProject() + let p, file = project |> getRandomFile + + // TODO: timeout & cancelation + log.Add $"{DateTime.Now.ToShortTimeString()}| #{n} Started checking {file.Id}" + let! result = checker |> checkFile file.Id p + + log.Add $"{DateTime.Now.ToShortTimeString()}| #{n} Checked {file.Id} %A{snd result}" + expectOk result () + + do! Async.Sleep (rng.Next maxCheckingDelayMs) + } + + async { + let! threads = + seq { + Async.StartChild(modificationLoop) + ignore modificationLoop + for n in 1..checkingThreads do + Async.StartChild(checkingLoop n) + } + |> Async.Parallel + try + do! threads |> Seq.skip 1 |> Async.Parallel |> Async.Ignore + with + | e -> failwith $"Seed: {seed}\nException: %A{e}" + } |> Async.RunSynchronously + + builder.DeleteProjectDir() diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index ce3661488fb..c5503597cf2 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -170,6 +170,7 @@ type SignatureFile = | No | AutoGenerated | Custom of string + member x.CustomText = match x with Custom text -> text | _ -> failwith $"Not a custom signature file (SignatureFile.%A{x})" type SyntheticSourceFile = @@ -678,6 +679,28 @@ module ProjectOperations = writeFileIfChanged (p.ProjectDir ++ $"{p.Name}.fsproj") (renderFsProj p) } + // Convert AutoGenerated signature files to Custom ones so they can be edited independently. + // This will save the project to disk. + let rec absorbAutoGeneratedSignatures checker (p: SyntheticProject) = + async { + do! saveProject p true checker + let files = [ + for file in p.SourceFiles do + if file.SignatureFile = AutoGenerated then + let text = file |> getSignatureFilePath p |> File.ReadAllText + { file with SignatureFile = Custom text } + else file + ] + let! projects = + p.DependsOn + |> Seq.map (absorbAutoGeneratedSignatures checker) + |> Async.Sequential + return + { p with + SourceFiles = files + AutoAddModules = false + DependsOn = projects |> Array.toList } + } module Helpers = From ee0b60bbbd2d86fec3dec2bceaf688ebc0345245 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 14 Jul 2023 14:41:12 +0200 Subject: [PATCH 074/222] Symbols in project results, plug in some tests --- src/Compiler/Service/FSharpCheckerResults.fs | 51 +++++- src/Compiler/Service/FSharpCheckerResults.fsi | 12 +- src/Compiler/Service/TransparentCompiler.fs | 167 ++++++++++-------- tests/service/Common.fs | 2 +- tests/service/MultiProjectAnalysisTests.fs | 59 +++++-- tests/service/ProjectAnalysisTests.fs | 48 +++-- 6 files changed, 230 insertions(+), 109 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 4b4aa047e6c..ce13e75a747 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -411,6 +411,35 @@ type FSharpProjectSnapshot with Stamp = options.Stamp } } + + static member FromOptions(options: FSharpProjectOptions) = + async { + let getFileSnapshot _ fileName = + async { + let timeStamp = FileSystem.GetLastWriteTimeShim(fileName) + let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() + return { + FileName = fileName + Version = timeStamp.Ticks.ToString() + GetSource = fun () -> Task.FromResult (SourceText.ofString contents) + } + } + return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + } + + static member FromOptions(options: FSharpProjectOptions, fileName: string, fileVersion: int, sourceText: ISourceText) = + async { + let! snapshot = FSharpProjectSnapshot.FromOptions options + return + { snapshot + with SourceFiles = + snapshot.SourceFiles + |> List.map (function + | f when f.FileName = fileName -> + { f with GetSource = fun () -> Task.FromResult sourceText + Version = $"{fileVersion}{sourceText.GetHashCode().ToString()}" } + | f -> f) } + } [] module internal FSharpCheckerResultsSettings = @@ -3328,7 +3357,7 @@ type FSharpCheckProjectResults tcConfigOption: TcConfig option, keepAssemblyContents: bool, diagnostics: FSharpDiagnostic[], - details: (TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * Choice * TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * AccessorDomain * CheckedImplFile list option * string[] * FSharpProjectOptions) option + details: (TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * Choice> * TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * AccessorDomain * CheckedImplFile list option * string[] * FSharpProjectOptions) option ) = let getDetails () = @@ -3420,6 +3449,7 @@ type FSharpCheckProjectResults FSharpAssemblyContents(tcGlobals, thisCcu, Some ccuSig, tcImports, mimpls) // Not, this does not have to be a SyncOp, it can be called from any thread + // TODO: this should be async member _.GetUsesOfSymbol(symbol: FSharpSymbol, ?cancellationToken: CancellationToken) = let _, _, _, _, builderOrSymbolUses, _, _, _, _, _, _, _ = getDetails () @@ -3436,11 +3466,15 @@ type FSharpCheckProjectResults | _ -> [||] | _ -> [||]) |> Array.toSeq - | Choice2Of2 tcSymbolUses -> - seq { - for symbolUses in tcSymbolUses do - yield! symbolUses.GetUsesOfSymbol symbol.Item - } + | Choice2Of2 task -> + Async.RunSynchronously(async { + let! tcSymbolUses = task + + return seq { + for symbolUses in tcSymbolUses do + yield! symbolUses.GetUsesOfSymbol symbol.Item + } + }, ?cancellationToken=cancellationToken) results |> Seq.filter (fun symbolUse -> symbolUse.ItemOccurence <> ItemOccurence.RelatedText) @@ -3452,6 +3486,7 @@ type FSharpCheckProjectResults |> Seq.toArray // Not, this does not have to be a SyncOp, it can be called from any thread + // TODO: this should be async member _.GetAllUsesOfAllSymbols(?cancellationToken: CancellationToken) = let tcGlobals, tcImports, thisCcu, ccuSig, builderOrSymbolUses, _, _, _, _, _, _, _ = getDetails () @@ -3471,7 +3506,7 @@ type FSharpCheckProjectResults | _ -> TcSymbolUses.Empty | _ -> TcSymbolUses.Empty) |> Array.toSeq - | Choice2Of2 tcSymbolUses -> tcSymbolUses + | Choice2Of2 tcSymbolUses -> Async.RunSynchronously(tcSymbolUses, ?cancellationToken=cancellationToken) [| for r in tcSymbolUses do @@ -3604,7 +3639,7 @@ type FsiInteractiveChecker(legacyReferenceResolver, tcConfig: TcConfig, tcGlobal tcImports, tcFileInfo.ThisCcu, tcFileInfo.CcuSigForFile, - Choice2Of2(Seq.singleton tcFileInfo.ScopeSymbolUses), + Choice2Of2(tcFileInfo.ScopeSymbolUses |> Seq.singleton |> async.Return), None, (fun () -> None), mkSimpleAssemblyRef "stdin", diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index deec695ce10..c4487bde80b 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -251,10 +251,20 @@ type FSharpProjectSnapshot with member ToOptions: unit -> FSharpProjectOptions + /// Create snapshot from FSharpProjectOptions using given function to retrieve file contnets. static member FromOptions: options: FSharpProjectOptions * getFileSnapshot: (FSharpProjectOptions -> string -> Async) -> Async + /// Create snapshot from FSharpProjectOptions using the filesystem to retrieve file contents. + static member FromOptions: + options: FSharpProjectOptions -> + Async + + /// Create snapshot from FSharpProjectOptions using the filesystem to retrieve file contents except one file that is specified by the arguments. + static member FromOptions: options:FSharpProjectOptions * fileName:string * fileVersion:int * sourceText:ISourceText ->Async + + /// Represents the use of an F# symbol from F# source code [] type public FSharpSymbolUse = @@ -653,7 +663,7 @@ type public FSharpCheckProjectResults = tcConfigOption: TcConfig option * keepAssemblyContents: bool * diagnostics: FSharpDiagnostic[] * - details: (TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * Choice * TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * AccessorDomain * CheckedImplFile list option * string[] * FSharpProjectOptions) option -> + details: (TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * Choice> * TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * AccessorDomain * CheckedImplFile list option * string[] * FSharpProjectOptions) option -> FSharpCheckProjectResults module internal ParseAndCheckFile = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 776a5017429..ec5033d75f0 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -74,6 +74,8 @@ type internal TcInfo = graphNode: NodeToTypeCheck option stateContainsNodes: Set + + sink: TcResultsSinkImpl list } member x.TcDiagnostics = Array.concat (List.rev x.tcDiagnosticsRev) @@ -280,7 +282,7 @@ type internal TransparentCompiler let BootstrapInfoCache = AsyncMemoize(triggerCacheEvent, "BootstrapInfo") - let TcPriorCache = AsyncMemoize(triggerCacheEvent, "TcPrior") + let TcFileCache = AsyncMemoize(triggerCacheEvent, "TcPrior") let TcIntermediateCache = AsyncMemoize(triggerCacheEvent, "TcIntermediate") @@ -448,6 +450,7 @@ type internal TransparentCompiler sigNameOpt = None graphNode = None stateContainsNodes = Set.empty + sink = [] } return tcImports, tcInfo @@ -914,6 +917,7 @@ type internal TransparentCompiler = let dependencyFiles = dependencyGraph |> Graph.subGraphFor fileIndex |> Graph.nodes + ignore dependencyGraph TcIntermediateCache.Get( projectSnapshot.OnlyWith(dependencyFiles).Key, @@ -947,6 +951,7 @@ type internal TransparentCompiler |> ignore let sink = TcResultsSinkImpl(tcGlobals) + let hadParseErrors = not (Array.isEmpty parseErrors) let input, moduleNamesDict = @@ -984,7 +989,7 @@ type internal TransparentCompiler } ) - let processGraphNode projectSnapshot bootstrapInfo (parsedInputs: _ array) dependencyFiles (fileNode: NodeToTypeCheck) tcInfo = + let processGraphNode projectSnapshot bootstrapInfo (parsedInputs: _ array) dependencyFiles collectSinks (fileNode: NodeToTypeCheck) tcInfo = node { match fileNode with @@ -1011,12 +1016,14 @@ type internal TransparentCompiler { tcInfo with tcState = tcState tcEnvAtEndOfFile = tcEnvAtEndOfFile + moduleNamesDict = tcIntermediate.moduleNamesDict topAttribs = Some topAttribs tcDiagnosticsRev = tcIntermediate.tcDiagnosticsRev @ tcInfo.tcDiagnosticsRev tcDependencyFiles = tcIntermediate.tcDependencyFiles @ tcInfo.tcDependencyFiles latestCcuSigForFile = Some ccuSigForFile graphNode = Some node stateContainsNodes = tcInfo.stateContainsNodes |> Set.add node + sink = if collectSinks then tcIntermediate.sink :: tcInfo.sink else [tcIntermediate.sink] }) ) @@ -1060,17 +1067,17 @@ type internal TransparentCompiler } - // Type check everything that is needed to check given file - let ComputeTcPrior (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = + // Type check file and all its dependencies + let ComputeTcFile (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = let priorSnapshot = projectSnapshot.UpTo file.Source.FileName let key = priorSnapshot.Key - TcPriorCache.Get( + TcFileCache.Get( key, node { use _ = - Activity.start "ComputeTcPrior" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName |] + Activity.start "ComputeTcFile" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName |] // parse required files let files = @@ -1083,17 +1090,17 @@ type internal TransparentCompiler let! graph, dependencyFiles = ComputeDependencyGraphForFile priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig - //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig + //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig let! results, tcInfo = processTypeCheckingGraph graph - (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles) + (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles false) bootstrapInfo.InitialTcInfo let lastResult = results |> List.head |> snd - return lastResult, tcInfo, dependencyFiles + return lastResult, tcInfo } ) @@ -1147,21 +1154,15 @@ type internal TransparentCompiler let file = bootstrapInfo.GetFile fileName - let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file let! parseResults, _sourceText = getParseResult bootstrapInfo creationDiags fileName - let! result, priorTcInfo, graph = ComputeTcPrior file bootstrapInfo projectSnapshot + let! result, priorTcInfo = ComputeTcFile file bootstrapInfo projectSnapshot let (tcEnv, _topAttribs, checkedImplFileOpt, ccuSigForFile) = result let tcState = priorTcInfo.tcState - let fileIndex = projectSnapshot.IndexOf fileName - - let! tcIntermediate = - ComputeTcIntermediate projectSnapshot graph fileIndex (parseTree, parseDiagnostics) bootstrapInfo priorTcInfo - - let sink = tcIntermediate.sink + let sink = priorTcInfo.sink.Head // TODO: don't use head let tcResolutions = sink.GetResolutions() let tcSymbolUses = sink.GetSymbolUses() @@ -1173,8 +1174,8 @@ type internal TransparentCompiler seq { yield! priorTcInfo.TcDiagnostics - for x in tcIntermediate.tcDiagnosticsRev do - yield! x + //for x in tcIntermediate.tcDiagnosticsRev do + // yield! x } let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions @@ -1240,14 +1241,10 @@ type internal TransparentCompiler let! graph, dependencyFiles = ComputeDependencyGraphForProject projectSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig - //// layers that can be processed in parallel - //let layers = Graph.leafSequence graph |> Seq.toList - - //let! tcInfos, _ = typeCheckLayers bootstrapInfo projectSnapshot parsedInputs dependencyFiles layers return! processTypeCheckingGraph graph - (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles) + (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles true) bootstrapInfo.InitialTcInfo } ) @@ -1395,12 +1392,14 @@ type internal TransparentCompiler | ProjectAssemblyDataResult.Available data -> Some data | _ -> None + let symbolUses = tcInfo.sink |> Seq.map (fun sink -> sink.GetSymbolUses()) + let details = (bootstrapInfo.TcGlobals, bootstrapInfo.TcImports, tcState.Ccu, tcState.CcuSig, - Choice2Of2 Seq.empty, // TODO: SymbolUses, + Choice2Of2 (async.Return symbolUses), topAttribs, getAssemblyData, ilAssemRef, @@ -1430,15 +1429,9 @@ type internal TransparentCompiler let file = bootstrapInfo.GetFile fileName - let! _, tcInfo, graph = ComputeTcPrior file bootstrapInfo projectSnapshot - let! parseTree, parseDiagnostics, _sourceText = ComputeParseFile bootstrapInfo file - - let fileIndex = projectSnapshot.IndexOf fileName + let! _, tcInfo = ComputeTcFile file bootstrapInfo projectSnapshot - let! { sink = sink } = - ComputeTcIntermediate projectSnapshot graph fileIndex (parseTree, parseDiagnostics) bootstrapInfo tcInfo - - return Some(sink, bootstrapInfo) + return tcInfo.sink |> List.tryHead |> Option.map (fun sink -> sink, bootstrapInfo) } let ComputeSemanticClassification (fileName: string, projectSnapshot: FSharpProjectSnapshot) = @@ -1546,7 +1539,7 @@ type internal TransparentCompiler member this.BeforeBackgroundFileCheck: IEvent = backgroundCompiler.BeforeBackgroundFileCheck - member _.CheckFileInProject + member this.CheckFileInProject ( parseResults: FSharpParseFileResults, fileName: string, @@ -1555,9 +1548,14 @@ type internal TransparentCompiler options: FSharpProjectOptions, userOpName: string ) : NodeCode = - backgroundCompiler.CheckFileInProject(parseResults, fileName, fileVersion, sourceText, options, userOpName) + node { + let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync + ignore parseResults + let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + return result + } - member _.CheckFileInProjectAllowingStaleCachedResults + member this.CheckFileInProjectAllowingStaleCachedResults ( parseResults: FSharpParseFileResults, fileName: string, @@ -1566,14 +1564,12 @@ type internal TransparentCompiler options: FSharpProjectOptions, userOpName: string ) : NodeCode = - backgroundCompiler.CheckFileInProjectAllowingStaleCachedResults( - parseResults, - fileName, - fileVersion, - sourceText, - options, - userOpName - ) + node { + let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync + ignore parseResults + let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + return Some result + } member _.ClearCache(options: seq, userOpName: string) : unit = backgroundCompiler.ClearCache(options, userOpName) @@ -1587,7 +1583,7 @@ type internal TransparentCompiler member _.FileParsed: IEvent = backgroundCompiler.FileParsed - member _.FindReferencesInFile + member this.FindReferencesInFile ( fileName: string, options: FSharpProjectOptions, @@ -1595,7 +1591,11 @@ type internal TransparentCompiler canInvalidateProject: bool, userOpName: string ) : NodeCode> = - backgroundCompiler.FindReferencesInFile(fileName, options, symbol, canInvalidateProject, userOpName) + node { + ignore canInvalidateProject + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + return! this.FindReferencesInFile(fileName, snapshot, symbol, userOpName) + } member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) @@ -1603,38 +1603,57 @@ type internal TransparentCompiler member _.FrameworkImportsCache: FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache - member _.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = - backgroundCompiler.GetAssemblyData(options, userOpName) + member this.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = + node { + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + return! this.GetAssemblyData(snapshot, userOpName) + } member this.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = this.GetAssemblyData(projectSnapshot, userOpName) - member _.GetBackgroundCheckResultsForFileInProject + member this.GetBackgroundCheckResultsForFileInProject ( fileName: string, options: FSharpProjectOptions, userOpName: string ) : NodeCode = - backgroundCompiler.GetBackgroundCheckResultsForFileInProject(fileName, options, userOpName) + node { + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + match! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) with + | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> + return parseResult, checkResult + | parseResult, FSharpCheckFileAnswer.Aborted _ -> + return parseResult, FSharpCheckFileResults.MakeEmpty(fileName, [||], true) + } - member _.GetBackgroundParseResultsForFileInProject + member this.GetBackgroundParseResultsForFileInProject ( fileName: string, options: FSharpProjectOptions, userOpName: string ) : NodeCode = - backgroundCompiler.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName) + node { + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + return! this.ParseFile(fileName, snapshot, userOpName) + } - member _.GetCachedCheckFileResult + member this.GetCachedCheckFileResult ( builder: IncrementalBuilder, fileName: string, sourceText: ISourceText, options: FSharpProjectOptions ) : NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> = - backgroundCompiler.GetCachedCheckFileResult(builder, fileName, sourceText, options) + node { + ignore builder + let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, 1, sourceText) |> NodeCode.AwaitAsync + match! this.ParseAndCheckFileInProject(fileName, snapshot, "GetCachedCheckFileResult") with + | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return Some (parseResult, checkResult) + | _, FSharpCheckFileAnswer.Aborted _ -> return None + } - member _.GetProjectOptionsFromScript + member this.GetProjectOptionsFromScript ( fileName: string, sourceText: ISourceText, @@ -1662,28 +1681,32 @@ type internal TransparentCompiler userOpName ) - member _.GetSemanticClassificationForFile(fileName: string, snapshot: FSharpProjectSnapshot, userOpName: string) = + member this.GetSemanticClassificationForFile(fileName: string, snapshot: FSharpProjectSnapshot, userOpName: string) = ignore userOpName ComputeSemanticClassification(fileName, snapshot) - member _.GetSemanticClassificationForFile + member this.GetSemanticClassificationForFile ( fileName: string, options: FSharpProjectOptions, userOpName: string ) : NodeCode = - backgroundCompiler.GetSemanticClassificationForFile(fileName, options, userOpName) + node { + ignore userOpName + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + return! ComputeSemanticClassification(fileName, snapshot) + } - member _.InvalidateConfiguration(options: FSharpProjectOptions, userOpName: string) : unit = + member this.InvalidateConfiguration(options: FSharpProjectOptions, userOpName: string) : unit = backgroundCompiler.InvalidateConfiguration(options, userOpName) - member _.NotifyFileChanged(fileName: string, options: FSharpProjectOptions, userOpName: string) : NodeCode = + member this.NotifyFileChanged(fileName: string, options: FSharpProjectOptions, userOpName: string) : NodeCode = backgroundCompiler.NotifyFileChanged(fileName, options, userOpName) - member _.NotifyProjectCleaned(options: FSharpProjectOptions, userOpName: string) : Async = + member this.NotifyProjectCleaned(options: FSharpProjectOptions, userOpName: string) : Async = backgroundCompiler.NotifyProjectCleaned(options, userOpName) - member _.ParseAndCheckFileInProject + member this.ParseAndCheckFileInProject ( fileName: string, fileVersion: int, @@ -1691,14 +1714,20 @@ type internal TransparentCompiler options: FSharpProjectOptions, userOpName: string ) : NodeCode = - - backgroundCompiler.ParseAndCheckFileInProject(fileName, fileVersion, sourceText, options, userOpName) + node { + let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync + return! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + } member this.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = this.ParseAndCheckFileInProject(fileName, projectSnapshot, userOpName) - member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = - backgroundCompiler.ParseAndCheckProject(options, userOpName) + member this.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = + node { + ignore userOpName + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + return! ComputeParseAndCheckProject snapshot + } member this.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = ignore userOpName @@ -1707,7 +1736,7 @@ type internal TransparentCompiler member this.ParseFile(fileName, projectSnapshot, userOpName) = this.ParseFile(fileName, projectSnapshot, userOpName) - member _.ParseFile + member this.ParseFile ( fileName: string, sourceText: ISourceText, @@ -1718,10 +1747,10 @@ type internal TransparentCompiler ) : Async = backgroundCompiler.ParseFile(fileName, sourceText, options, cache, flatErrors, userOpName) - member _.ProjectChecked: IEvent = + member this.ProjectChecked: IEvent = backgroundCompiler.ProjectChecked - member _.TryGetRecentCheckResultsForFile + member this.TryGetRecentCheckResultsForFile ( fileName: string, options: FSharpProjectOptions, diff --git a/tests/service/Common.fs b/tests/service/Common.fs index 4f24b831d2c..ca72dabedad 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -31,7 +31,7 @@ type Async with task.Result // Create one global interactive checker instance -let checker = FSharpChecker.Create() +let checker = FSharpChecker.Create(useTransparentCompiler=true) // TODO: tests for both versions type TempFile(ext, contents: string) = let tmpFile = Path.ChangeExtension(tryCreateTemporaryFileName (), ext) diff --git a/tests/service/MultiProjectAnalysisTests.fs b/tests/service/MultiProjectAnalysisTests.fs index eab86f98324..52c294c0ad8 100644 --- a/tests/service/MultiProjectAnalysisTests.fs +++ b/tests/service/MultiProjectAnalysisTests.fs @@ -23,6 +23,7 @@ open TestFramework let toIList (x: _ array) = x :> IList<_> let numProjectsForStressTest = 100 let internal checker = FSharpChecker.Create(projectCacheSize=numProjectsForStressTest + 10) +let internal transparentCompilerChecker = FSharpChecker.Create(projectCacheSize=numProjectsForStressTest + 10, useTransparentCompiler=true) /// Extract range info let internal tups (m:range) = (m.StartLine, m.StartColumn), (m.EndLine, m.EndColumn) @@ -144,7 +145,11 @@ let ``Test multi project 1 basic`` () = |> shouldEqual ["p"; "c"; "u"] [] -let ``Test multi project 1 all symbols`` () = +[] +[] +let ``Test multi project 1 all symbols`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker let p1A = checker.ParseAndCheckProject(Project1A.options) |> Async.RunImmediate let p1B = checker.ParseAndCheckProject(Project1B.options) |> Async.RunImmediate @@ -182,7 +187,11 @@ let ``Test multi project 1 all symbols`` () = usesOfx1FromProject1AInMultiProject1 |> shouldEqual usesOfx1FromMultiProject1InMultiProject1 [] -let ``Test multi project 1 xmldoc`` () = +[] +[] +let ``Test multi project 1 xmldoc`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker let p1A = checker.ParseAndCheckProject(Project1A.options) |> Async.RunImmediate let p1B = checker.ParseAndCheckProject(Project1B.options) |> Async.RunImmediate @@ -319,14 +328,16 @@ let p = (""" |> function Some x -> x | None -> if a = jointProject.FileName then "fileN" else "??" - let makeCheckerForStressTest ensureBigEnough = + let makeCheckerForStressTest ensureBigEnough useTransparentCompiler = let size = (if ensureBigEnough then numProjectsForStressTest + 10 else numProjectsForStressTest / 2 ) - FSharpChecker.Create(projectCacheSize=size) + FSharpChecker.Create(projectCacheSize=size, useTransparentCompiler=useTransparentCompiler) [] -let ``Test ManyProjectsStressTest basic`` () = +[] +[] +let ``Test ManyProjectsStressTest basic`` useTransparentCompiler = - let checker = ManyProjectsStressTest.makeCheckerForStressTest true + let checker = ManyProjectsStressTest.makeCheckerForStressTest true useTransparentCompiler let wholeProjectResults = checker.ParseAndCheckProject(ManyProjectsStressTest.jointProject.Options) |> Async.RunImmediate @@ -338,9 +349,11 @@ let ``Test ManyProjectsStressTest basic`` () = |> shouldEqual ["p"] [] -let ``Test ManyProjectsStressTest cache too small`` () = +[] +[] +let ``Test ManyProjectsStressTest cache too small`` useTransparentCompiler = - let checker = ManyProjectsStressTest.makeCheckerForStressTest false + let checker = ManyProjectsStressTest.makeCheckerForStressTest false useTransparentCompiler let wholeProjectResults = checker.ParseAndCheckProject(ManyProjectsStressTest.jointProject.Options) |> Async.RunImmediate @@ -352,9 +365,11 @@ let ``Test ManyProjectsStressTest cache too small`` () = |> shouldEqual ["p"] [] -let ``Test ManyProjectsStressTest all symbols`` () = +[] +[] +let ``Test ManyProjectsStressTest all symbols`` useTransparentCompiler = - let checker = ManyProjectsStressTest.makeCheckerForStressTest true + let checker = ManyProjectsStressTest.makeCheckerForStressTest true useTransparentCompiler for i in 1 .. 10 do printfn "stress test iteration %d (first may be slow, rest fast)" i let projectsResults = [ for p in ManyProjectsStressTest.projects -> p, checker.ParseAndCheckProject(p.Options) |> Async.RunImmediate ] @@ -432,7 +447,11 @@ let z = Project1.x ReferencedProjects = [| FSharpReferencedProject.FSharpReference(MultiProjectDirty1.dllName, MultiProjectDirty1.getOptions()) |] } [] -let ``Test multi project symbols should pick up changes in dependent projects`` () = +[] +[] +let ``Test multi project symbols should pick up changes in dependent projects`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker // register to count the file checks let count = ref 0 @@ -667,7 +686,11 @@ let v = Project2A.C().InternalMember // access an internal symbol let cleanFileName a = if a = fileName1 then "file1" else "??" [] -let ``Test multi project2 errors`` () = +[] +[] +let ``Test multi project2 errors`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker let wholeProjectResults = checker.ParseAndCheckProject(Project2B.options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -760,7 +783,11 @@ let fizzBuzz = function let cleanFileName a = if a = fileName1 then "file1" else "??" [] -let ``Test multi project 3 whole project errors`` () = +[] +[] +let ``Test multi project 3 whole project errors`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker let wholeProjectResults = checker.ParseAndCheckProject(MultiProject3.options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -769,7 +796,11 @@ let ``Test multi project 3 whole project errors`` () = wholeProjectResults.Diagnostics.Length |> shouldEqual 0 [] -let ``Test active patterns' XmlDocSig declared in referenced projects`` () = +[] +[] +let ``Test active patterns' XmlDocSig declared in referenced projects`` useTransparentCompiler = + + let checker = if useTransparentCompiler then transparentCompilerChecker else checker let wholeProjectResults = checker.ParseAndCheckProject(MultiProject3.options) |> Async.RunImmediate let backgroundParseResults1, backgroundTypedParse1 = diff --git a/tests/service/ProjectAnalysisTests.fs b/tests/service/ProjectAnalysisTests.fs index 0ee58b1898f..68b1ff10544 100644 --- a/tests/service/ProjectAnalysisTests.fs +++ b/tests/service/ProjectAnalysisTests.fs @@ -4636,8 +4636,10 @@ let callToOverload = B(5).Overload(4) let args = mkProjectCommandLineArgs (dllName, fileNames) [] -let ``Test project36 FSharpMemberOrFunctionOrValue.IsBaseValue`` () = - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) +[] +[] +let ``Test project36 FSharpMemberOrFunctionOrValue.IsBaseValue`` useTransparentCompiler = + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(options) @@ -4651,8 +4653,10 @@ let ``Test project36 FSharpMemberOrFunctionOrValue.IsBaseValue`` () = |> fun baseSymbol -> shouldEqual true baseSymbol.IsBaseValue [] -let ``Test project36 FSharpMemberOrFunctionOrValue.IsConstructorThisValue & IsMemberThisValue`` () = - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) +[] +[] +let ``Test project36 FSharpMemberOrFunctionOrValue.IsConstructorThisValue & IsMemberThisValue`` useTransparentCompiler = + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(options) |> Async.RunImmediate let declarations = @@ -4688,8 +4692,10 @@ let ``Test project36 FSharpMemberOrFunctionOrValue.IsConstructorThisValue & IsMe |> shouldEqual true [] -let ``Test project36 FSharpMemberOrFunctionOrValue.LiteralValue`` () = - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) +[] +[] +let ``Test project36 FSharpMemberOrFunctionOrValue.LiteralValue`` useTransparentCompiler = + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(options) |> Async.RunImmediate let project36Module = wholeProjectResults.AssemblySignature.Entities[0] @@ -5305,8 +5311,10 @@ let foo (a: Foo): bool = let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) [] -let ``Test typed AST for struct unions`` () = // See https://github.com/fsharp/FSharp.Compiler.Service/issues/756 - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) +[] +[] +let ``Test typed AST for struct unions`` useTransparentCompiler = // See https://github.com/fsharp/FSharp.Compiler.Service/issues/756 + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(ProjectStructUnions.options) |> Async.RunImmediate let declarations = @@ -5393,7 +5401,9 @@ let ``Test diagnostics with line directives ignored`` () = //------------------------------------------------------ [] -let ``ParseAndCheckFileResults contains ImplFile list if FSharpChecker is created with keepAssemblyContent flag set to true``() = +[] +[] +let ``ParseAndCheckFileResults contains ImplFile list if FSharpChecker is created with keepAssemblyContent flag set to true`` useTransparentCompiler = let fileName1 = Path.ChangeExtension(tryCreateTemporaryFileName (), ".fs") let base2 = tryCreateTemporaryFileName () @@ -5408,7 +5418,7 @@ type A(i:int) = let fileNames = [fileName1] let args = mkProjectCommandLineArgs (dllName, fileNames) - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) let fileCheckResults = @@ -5451,7 +5461,9 @@ let ``#4030, Incremental builder creation warnings`` (args, errorSeverities) = //------------------------------------------------------ [] -let ``Unused opens in rec module smoke test 1``() = +[] +[] +let ``Unused opens in rec module smoke test 1`` useTransparentCompiler = let fileName1 = Path.ChangeExtension(tryCreateTemporaryFileName (), ".fs") let base2 = tryCreateTemporaryFileName () @@ -5499,7 +5511,7 @@ type UseTheThings(i:int) = let fileNames = [fileName1] let args = mkProjectCommandLineArgs (dllName, fileNames) - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) let fileCheckResults = @@ -5524,7 +5536,9 @@ type UseTheThings(i:int) = unusedOpensData |> shouldEqual expected [] -let ``Unused opens in non rec module smoke test 1``() = +[] +[] +let ``Unused opens in non rec module smoke test 1`` useTransparentCompiler = let fileName1 = Path.ChangeExtension(tryCreateTemporaryFileName (), ".fs") let base2 = tryCreateTemporaryFileName () @@ -5572,7 +5586,7 @@ type UseTheThings(i:int) = let fileNames = [fileName1] let args = mkProjectCommandLineArgs (dllName, fileNames) - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) let fileCheckResults = @@ -5597,7 +5611,9 @@ type UseTheThings(i:int) = unusedOpensData |> shouldEqual expected [] -let ``Unused opens smoke test auto open``() = +[] +[] +let ``Unused opens smoke test auto open`` useTransparentCompiler = let fileName1 = Path.ChangeExtension(tryCreateTemporaryFileName (), ".fs") let base2 = tryCreateTemporaryFileName () @@ -5653,7 +5669,7 @@ module M2 = let fileNames = [fileName1] let args = mkProjectCommandLineArgs (dllName, fileNames) - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) let fileCheckResults = From 32ddc2f138ff5d4696e3e633be8d34d3763839f4 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 14 Jul 2023 17:55:20 +0200 Subject: [PATCH 075/222] f --- src/Compiler/Service/FSharpCheckerResults.fs | 57 +++++++++++-------- src/Compiler/Service/FSharpCheckerResults.fsi | 9 ++- src/Compiler/Service/TransparentCompiler.fs | 54 +++++++++++++----- 3 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index ce13e75a747..6c8b1959659 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -411,34 +411,41 @@ type FSharpProjectSnapshot with Stamp = options.Stamp } } - + static member FromOptions(options: FSharpProjectOptions) = async { let getFileSnapshot _ fileName = async { let timeStamp = FileSystem.GetLastWriteTimeShim(fileName) let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() - return { - FileName = fileName - Version = timeStamp.Ticks.ToString() - GetSource = fun () -> Task.FromResult (SourceText.ofString contents) - } + + return + { + FileName = fileName + Version = timeStamp.Ticks.ToString() + GetSource = fun () -> Task.FromResult(SourceText.ofString contents) + } } + return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) } static member FromOptions(options: FSharpProjectOptions, fileName: string, fileVersion: int, sourceText: ISourceText) = async { let! snapshot = FSharpProjectSnapshot.FromOptions options + return - { snapshot - with SourceFiles = - snapshot.SourceFiles - |> List.map (function - | f when f.FileName = fileName -> - { f with GetSource = fun () -> Task.FromResult sourceText - Version = $"{fileVersion}{sourceText.GetHashCode().ToString()}" } - | f -> f) } + { snapshot with + SourceFiles = + snapshot.SourceFiles + |> List.map (function + | f when f.FileName = fileName -> + { f with + GetSource = fun () -> Task.FromResult sourceText + Version = $"{fileVersion}{sourceText.GetHashCode().ToString()}" + } + | f -> f) + } } [] @@ -3467,14 +3474,18 @@ type FSharpCheckProjectResults | _ -> [||]) |> Array.toSeq | Choice2Of2 task -> - Async.RunSynchronously(async { - let! tcSymbolUses = task - - return seq { - for symbolUses in tcSymbolUses do - yield! symbolUses.GetUsesOfSymbol symbol.Item - } - }, ?cancellationToken=cancellationToken) + Async.RunSynchronously( + async { + let! tcSymbolUses = task + + return + seq { + for symbolUses in tcSymbolUses do + yield! symbolUses.GetUsesOfSymbol symbol.Item + } + }, + ?cancellationToken = cancellationToken + ) results |> Seq.filter (fun symbolUse -> symbolUse.ItemOccurence <> ItemOccurence.RelatedText) @@ -3506,7 +3517,7 @@ type FSharpCheckProjectResults | _ -> TcSymbolUses.Empty | _ -> TcSymbolUses.Empty) |> Array.toSeq - | Choice2Of2 tcSymbolUses -> Async.RunSynchronously(tcSymbolUses, ?cancellationToken=cancellationToken) + | Choice2Of2 tcSymbolUses -> Async.RunSynchronously(tcSymbolUses, ?cancellationToken = cancellationToken) [| for r in tcSymbolUses do diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index c4487bde80b..95c6ccd593f 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -257,13 +257,12 @@ type FSharpProjectSnapshot with Async /// Create snapshot from FSharpProjectOptions using the filesystem to retrieve file contents. - static member FromOptions: - options: FSharpProjectOptions -> - Async + static member FromOptions: options: FSharpProjectOptions -> Async /// Create snapshot from FSharpProjectOptions using the filesystem to retrieve file contents except one file that is specified by the arguments. - static member FromOptions: options:FSharpProjectOptions * fileName:string * fileVersion:int * sourceText:ISourceText ->Async - + static member FromOptions: + options: FSharpProjectOptions * fileName: string * fileVersion: int * sourceText: ISourceText -> + Async /// Represents the use of an F# symbol from F# source code [] diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index ec5033d75f0..b5c92b30957 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -989,7 +989,15 @@ type internal TransparentCompiler } ) - let processGraphNode projectSnapshot bootstrapInfo (parsedInputs: _ array) dependencyFiles collectSinks (fileNode: NodeToTypeCheck) tcInfo = + let processGraphNode + projectSnapshot + bootstrapInfo + (parsedInputs: _ array) + dependencyFiles + collectSinks + (fileNode: NodeToTypeCheck) + tcInfo + = node { match fileNode with @@ -1023,7 +1031,11 @@ type internal TransparentCompiler latestCcuSigForFile = Some ccuSigForFile graphNode = Some node stateContainsNodes = tcInfo.stateContainsNodes |> Set.add node - sink = if collectSinks then tcIntermediate.sink :: tcInfo.sink else [tcIntermediate.sink] + sink = + if collectSinks then + tcIntermediate.sink :: tcInfo.sink + else + [ tcIntermediate.sink ] }) ) @@ -1090,7 +1102,7 @@ type internal TransparentCompiler let! graph, dependencyFiles = ComputeDependencyGraphForFile priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig - //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig + //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig let! results, tcInfo = processTypeCheckingGraph @@ -1174,8 +1186,8 @@ type internal TransparentCompiler seq { yield! priorTcInfo.TcDiagnostics - //for x in tcIntermediate.tcDiagnosticsRev do - // yield! x + //for x in tcIntermediate.tcDiagnosticsRev do + // yield! x } let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions @@ -1399,7 +1411,7 @@ type internal TransparentCompiler bootstrapInfo.TcImports, tcState.Ccu, tcState.CcuSig, - Choice2Of2 (async.Return symbolUses), + Choice2Of2(async.Return symbolUses), topAttribs, getAssemblyData, ilAssemRef, @@ -1549,7 +1561,10 @@ type internal TransparentCompiler userOpName: string ) : NodeCode = node { - let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync + let! snapshot = + FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) + |> NodeCode.AwaitAsync + ignore parseResults let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) return result @@ -1565,7 +1580,10 @@ type internal TransparentCompiler userOpName: string ) : NodeCode = node { - let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync + let! snapshot = + FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) + |> NodeCode.AwaitAsync + ignore parseResults let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) return Some result @@ -1620,11 +1638,10 @@ type internal TransparentCompiler ) : NodeCode = node { let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + match! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) with - | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> - return parseResult, checkResult - | parseResult, FSharpCheckFileAnswer.Aborted _ -> - return parseResult, FSharpCheckFileResults.MakeEmpty(fileName, [||], true) + | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return parseResult, checkResult + | parseResult, FSharpCheckFileAnswer.Aborted _ -> return parseResult, FSharpCheckFileResults.MakeEmpty(fileName, [||], true) } member this.GetBackgroundParseResultsForFileInProject @@ -1647,9 +1664,13 @@ type internal TransparentCompiler ) : NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> = node { ignore builder - let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, 1, sourceText) |> NodeCode.AwaitAsync + + let! snapshot = + FSharpProjectSnapshot.FromOptions(options, fileName, 1, sourceText) + |> NodeCode.AwaitAsync + match! this.ParseAndCheckFileInProject(fileName, snapshot, "GetCachedCheckFileResult") with - | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return Some (parseResult, checkResult) + | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return Some(parseResult, checkResult) | _, FSharpCheckFileAnswer.Aborted _ -> return None } @@ -1715,7 +1736,10 @@ type internal TransparentCompiler userOpName: string ) : NodeCode = node { - let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync + let! snapshot = + FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) + |> NodeCode.AwaitAsync + return! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) } From d0ed695b2beccbb37b0043f7c14f4e2d1cd75e2e Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Wed, 30 Aug 2023 13:37:09 +0200 Subject: [PATCH 076/222] wip --- .../Classification/ClassificationService.fs | 6 +- .../Commands/XmlDocCommandService.fs | 120 +++++++++--------- .../Common/CodeAnalysisExtensions.fs | 13 +- .../src/FSharp.Editor/Common/Extensions.fs | 71 +++++++++++ .../src/FSharp.Editor/Common/Pervasive.fs | 15 +++ .../src/FSharp.Editor/Common/RoslynHelpers.fs | 6 +- .../Debugging/BreakpointResolutionService.fs | 17 ++- .../DocumentHighlightsService.fs | 4 +- .../Formatting/BraceMatchingService.fs | 4 +- .../InlineRename/InlineRenameService.fs | 8 +- .../LanguageService/LanguageService.fs | 4 +- .../LanguageService/SymbolHelpers.fs | 4 +- .../LanguageService/WorkspaceExtensions.fs | 8 +- .../Navigation/FindUsagesService.fs | 8 +- .../Navigation/GoToDefinition.fs | 6 +- .../Navigation/NavigateToSearchService.fs | 2 +- .../Navigation/NavigationBarItemService.fs | 57 +++++---- .../QuickInfo/QuickInfoProvider.fs | 4 +- .../Refactor/ChangeDerefToValueRefactoring.fs | 62 +++++---- .../ChangeTypeofWithNameToNameofExpression.fs | 69 +++++----- .../Structure/BlockStructureService.fs | 2 +- 21 files changed, 296 insertions(+), 194 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index b837385c1a9..8900417cdc0 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -52,7 +52,7 @@ type internal FSharpClassificationService [] () = ClassificationTypeNames.Text match RoslynHelpers.TryFSharpRangeToTextSpan(text, tok.Range) with - | Some span -> result.Add(ClassifiedSpan(TextSpan(textSpan.Start + span.Start, span.Length), spanKind)) + | ValueSome span -> result.Add(ClassifiedSpan(TextSpan(textSpan.Start + span.Start, span.Length), spanKind)) | _ -> () let flags = @@ -79,8 +79,8 @@ type internal FSharpClassificationService [] () = = for item in items do match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, item.Range) with - | None -> () - | Some span -> + | ValueNone -> () + | ValueSome span -> let span = match item.Type with | SemanticClassificationType.Printf -> span diff --git a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs index f2bcf18e870..4efd4e51f38 100644 --- a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs @@ -16,6 +16,8 @@ open Microsoft.VisualStudio.TextManager.Interop open Microsoft.VisualStudio.LanguageServices open Microsoft.VisualStudio.Utilities open FSharp.Compiler.EditorServices +open CancellableTasks.CancellableTaskBuilder +open CancellableTasks type internal XmlDocCommandFilter(wpfTextView: IWpfTextView, filePath: string, workspace: VisualStudioWorkspace) = @@ -58,76 +60,80 @@ type internal XmlDocCommandFilter(wpfTextView: IWpfTextView, filePath: string, w match XmlDocComment.IsBlank lineWithLastCharInserted with | Some i when i = indexOfCaret -> - asyncMaybe { + cancellableTask { try // XmlDocable line #1 are 1-based, editor is 0-based let curEditorLineNum = wpfTextView.Caret.Position.BufferPosition.GetContainingLine().LineNumber - let! document = getLastDocument () - let! cancellationToken = Async.CancellationToken |> liftAsync - let! sourceText = document.GetTextAsync(cancellationToken) - let! parseResults = document.GetFSharpParseResultsAsync(nameof (XmlDocCommandFilter)) |> liftAsync - - let xmlDocables = - XmlDocParser.GetXmlDocables(sourceText.ToFSharpSourceText(), parseResults.ParseTree) - - let xmlDocablesBelowThisLine = - // +1 because looking below current line for e.g. a 'member' or 'let' - xmlDocables - |> List.filter (fun (XmlDocable (line, _indent, _paramNames)) -> line = curEditorLineNum + 1) - - match xmlDocablesBelowThisLine with - | [] -> () - | XmlDocable (_line, indent, paramNames) :: _xs -> - // delete the slashes the user typed (they may be indented wrong) - let editorLineToDelete = - wpfTextView.TextBuffer.CurrentSnapshot.GetLineFromLineNumber( - wpfTextView.Caret.Position.BufferPosition.GetContainingLine().LineNumber - ) - - wpfTextView.TextBuffer.Delete(editorLineToDelete.Extent.Span) |> ignore - // add the new xmldoc comment - let toInsert = new Text.StringBuilder() - - toInsert - .Append(' ', indent) - .AppendLine("/// ") - .Append(' ', indent) - .AppendLine("/// ") - .Append(' ', indent) - .Append("/// ") - |> ignore - - paramNames - |> List.iter (fun p -> - toInsert - .AppendLine() - .Append(' ', indent) - .Append(sprintf "/// " p) - |> ignore) + let document = getLastDocument () + + match document with + | None -> () + | Some document -> + let! cancellationToken = CancellableTask.getCancellationToken () + let! sourceText = document.GetTextAsync(cancellationToken) + let! parseResults = document.GetFSharpParseResultsAsync(nameof (XmlDocCommandFilter)) + + let xmlDocables = + XmlDocParser.GetXmlDocables(sourceText.ToFSharpSourceText(), parseResults.ParseTree) - let _newSS = - wpfTextView.TextBuffer.Insert( - wpfTextView.Caret.Position.BufferPosition.Position, - toInsert.ToString() - ) - // move the caret to between the summary tags - let lastLine = wpfTextView.Caret.Position.BufferPosition.GetContainingLine() + let xmlDocablesBelowThisLine = + // +1 because looking below current line for e.g. a 'member' or 'let' + xmlDocables + |> List.filter (fun (XmlDocable (line, _indent, _paramNames)) -> line = curEditorLineNum + 1) - let middleSummaryLine = - wpfTextView.TextSnapshot.GetLineFromLineNumber(lastLine.LineNumber - 1 - paramNames.Length) + match xmlDocablesBelowThisLine with + | [] -> () + | XmlDocable (_line, indent, paramNames) :: _xs -> + // delete the slashes the user typed (they may be indented wrong) + let editorLineToDelete = + wpfTextView.TextBuffer.CurrentSnapshot.GetLineFromLineNumber( + wpfTextView.Caret.Position.BufferPosition.GetContainingLine().LineNumber + ) - wpfTextView.Caret.MoveTo(wpfTextView.GetTextViewLineContainingBufferPosition(middleSummaryLine.Start)) - |> ignore + wpfTextView.TextBuffer.Delete(editorLineToDelete.Extent.Span) |> ignore + // add the new xmldoc comment + let toInsert = new Text.StringBuilder() - shouldCommitCharacter <- false + toInsert + .Append(' ', indent) + .AppendLine("/// ") + .Append(' ', indent) + .AppendLine("/// ") + .Append(' ', indent) + .Append("/// ") + |> ignore + + paramNames + |> List.iter (fun p -> + toInsert + .AppendLine() + .Append(' ', indent) + .Append(sprintf "/// " p) + |> ignore) + + let _newSS = + wpfTextView.TextBuffer.Insert( + wpfTextView.Caret.Position.BufferPosition.Position, + toInsert.ToString() + ) + // move the caret to between the summary tags + let lastLine = wpfTextView.Caret.Position.BufferPosition.GetContainingLine() + + let middleSummaryLine = + wpfTextView.TextSnapshot.GetLineFromLineNumber(lastLine.LineNumber - 1 - paramNames.Length) + + wpfTextView.Caret.MoveTo(wpfTextView.GetTextViewLineContainingBufferPosition(middleSummaryLine.Start)) + |> ignore + + shouldCommitCharacter <- false with ex -> Assert.Exception ex () } - |> Async.Ignore - |> Async.StartImmediate + |> CancellableTask.startAsTaskWithoutCancellation // We don't have a cancellation token here at the moment, maybe there's a better way of handling it in modern VS? + |> ignore | Some _ | None -> () | _ -> () diff --git a/vsintegration/src/FSharp.Editor/Common/CodeAnalysisExtensions.fs b/vsintegration/src/FSharp.Editor/Common/CodeAnalysisExtensions.fs index 888505ecda3..c42ace1a366 100644 --- a/vsintegration/src/FSharp.Editor/Common/CodeAnalysisExtensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/CodeAnalysisExtensions.fs @@ -53,17 +53,8 @@ type Solution with // It's crucial to normalize file path here (specificaly, remove relative parts), // otherwise Roslyn does not find documents. self.GetDocumentIdsWithFilePath(Path.GetFullPath filePath) - |> Seq.tryHead - |> Option.map (fun docId -> self.GetDocument docId) - - /// Try to find the document corresponding to the provided filepath and ProjectId within this solution - member self.TryGetDocumentFromPath(filePath, projId: ProjectId) = - // It's crucial to normalize file path here (specificaly, remove relative parts), - // otherwise Roslyn does not find documents. - self.GetDocumentIdsWithFilePath(Path.GetFullPath filePath) - |> Seq.filter (fun x -> x.ProjectId = projId) - |> Seq.tryHead - |> Option.map (fun docId -> self.GetDocument docId) + |> ImmutableArray.tryHeadV + |> ValueOption.map (fun docId -> self.GetDocument docId) /// Try to get a project inside the solution using the project's id member self.TryGetProject(projId: ProjectId) = diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index d05b5166a08..3c9d54f4261 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -349,6 +349,77 @@ module Array = loop 0 + let inline chooseV ([] chooser: 'T -> 'U voption) (array: 'T[]) = + + let mutable i = 0 + let mutable first = Unchecked.defaultof<'U> + let mutable found = false + + while i < array.Length && not found do + let element = array.[i] + + match chooser element with + | ValueNone -> i <- i + 1 + | ValueSome b -> + first <- b + found <- true + + if i <> array.Length then + + let chunk1: 'U[] = + Array.zeroCreate ((array.Length >>> 2) + 1) + + chunk1.[0] <- first + let mutable count = 1 + i <- i + 1 + + while count < chunk1.Length && i < array.Length do + let element = array.[i] + + match chooser element with + | ValueNone -> () + | ValueSome b -> + chunk1.[count] <- b + count <- count + 1 + + i <- i + 1 + + if i < array.Length then + let chunk2: 'U[] = + Array.zeroCreate (array.Length - i) + + count <- 0 + + while i < array.Length do + let element = array.[i] + + match chooser element with + | ValueNone -> () + | ValueSome b -> + chunk2.[count] <- b + count <- count + 1 + + i <- i + 1 + + let res: 'U[] = + Array.zeroCreate (chunk1.Length + count) + + Array.Copy(chunk1, res, chunk1.Length) + Array.Copy(chunk2, 0, res, chunk1.Length, count) + res + else + Array.sub chunk1 0 count + else + Array.empty + +[] +module ImmutableArray = + let inline tryHeadV (xs: ImmutableArray<'T>) : 'T voption = + if xs.Length = 0 then + ValueNone + else + ValueSome xs[0] + [] module List = let rec tryFindV predicate list = diff --git a/vsintegration/src/FSharp.Editor/Common/Pervasive.fs b/vsintegration/src/FSharp.Editor/Common/Pervasive.fs index 18e5c463c72..57f78491e9c 100644 --- a/vsintegration/src/FSharp.Editor/Common/Pervasive.fs +++ b/vsintegration/src/FSharp.Editor/Common/Pervasive.fs @@ -57,6 +57,13 @@ type MaybeBuilder() = [] member inline _.Bind(value, f: 'T -> 'U option) : 'U option = Option.bind f value + // M<'T> * ('T -> M<'U>) -> M<'U> + [] + member inline _.Bind(value: 'T voption, f: 'T -> 'U option) : 'U option = + match value with + | ValueNone -> None + | ValueSome value -> f value + // 'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable [] member _.Using(resource: ('T :> System.IDisposable), body: _ -> _ option) : _ option = @@ -137,6 +144,14 @@ type AsyncMaybeBuilder() = | Some result -> return! f result } + [] + member _.Bind(value: 'T voption, f: 'T -> Async<'U option>) : Async<'U option> = + async { + match value with + | ValueNone -> return None + | ValueSome result -> return! f result + } + [] member _.Using(resource: ('T :> IDisposable), body: 'T -> Async<'U option>) : Async<'U option> = async { diff --git a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs index 41dee649d81..2c0475d4d37 100644 --- a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs +++ b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs @@ -46,12 +46,12 @@ module internal RoslynHelpers = TextSpan(startPosition, endPosition - startPosition) - let TryFSharpRangeToTextSpan (sourceText: SourceText, range: range) : TextSpan option = + let TryFSharpRangeToTextSpan (sourceText: SourceText, range: range) : TextSpan voption = try - Some(FSharpRangeToTextSpan(sourceText, range)) + ValueSome(FSharpRangeToTextSpan(sourceText, range)) with e -> //Assert.Exception(e) - None + ValueNone let TextSpanToFSharpRange (fileName: string, textSpan: TextSpan, sourceText: SourceText) : range = let startLine = sourceText.Lines.GetLineFromPosition textSpan.Start diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index d73b1a61d05..a7e33fbfcb3 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -33,18 +33,17 @@ type internal FSharpBreakpointResolutionService [] () = sourceText.GetSubText(sourceText.Lines.[textLinePos.Line].Span).ToString() if String.IsNullOrWhiteSpace textInLine then - return None + return ValueNone 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 = document.GetFSharpParseResultsAsync(nameof (FSharpBreakpointResolutionService)) - |> liftAsync - match parseResults with - | Some parseResults -> return parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) - | _ -> return None + let location = parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) + + return ValueOption.ofOption location } interface IFSharpBreakpointResolutionService with @@ -58,14 +57,14 @@ type internal FSharpBreakpointResolutionService [] () = let! range = FSharpBreakpointResolutionService.GetBreakpointLocation(document, textSpan) match range with - | None -> return Unchecked.defaultof<_> - | Some range -> + | ValueNone -> return Unchecked.defaultof<_> + | ValueSome range -> let! sourceText = document.GetTextAsync(cancellationToken) let span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) match span with - | None -> return Unchecked.defaultof<_> - | Some span -> return FSharpBreakpointResolutionResult.CreateSpanResult(document, span) + | ValueNone -> return Unchecked.defaultof<_> + | ValueSome span -> return FSharpBreakpointResolutionResult.CreateSpanResult(document, span) } |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs index aaf2a8a5db1..7947b2dec99 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -97,8 +97,8 @@ type internal FSharpDocumentHighlightsService [] () = [| for symbolUse in symbolUses do match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) with - | None -> () - | Some span -> + | ValueNone -> () + | ValueSome span -> yield { IsDefinition = symbolUse.IsFromDefinition diff --git a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs index 11fa356b1f4..1b58774e7a1 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs @@ -28,8 +28,8 @@ type internal FSharpBraceMatchingService [] () = let isPositionInRange range = match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) with - | None -> false - | Some span -> + | ValueNone -> false + | ValueSome span -> if forFormatting then let length = position - span.Start length >= 0 && length <= span.Length diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index 0cb40be0b10..d63e2d016df 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -167,10 +167,10 @@ type internal InlineRenameInfo [| for symbolUse in symbolUses do match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse) with - | Some span -> + | ValueSome span -> let textSpan = Tokenizer.fixupSpan (sourceText, span) yield FSharpInlineRenameLocation(document, textSpan) - | None -> () + | ValueNone -> () |] } } @@ -220,8 +220,8 @@ type internal InlineRenameService [] () = let span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) match span with - | None -> return Unchecked.defaultof<_> - | Some span -> + | ValueNone -> return Unchecked.defaultof<_> + | ValueSome span -> let triggerSpan = Tokenizer.fixupSpan (sourceText, span) let result = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index ce2b568fe8b..e77ce6424b5 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -95,10 +95,10 @@ type internal FSharpWorkspaceServiceFactory [] let getSource filename = async { match workspace.CurrentSolution.TryGetDocumentFromPath filename with - | Some document -> + | ValueSome document -> let! text = document.GetTextAsync() |> Async.AwaitTask return Some(text.ToFSharpSourceText()) - | None -> return None + | ValueNone -> return None } lock gate (fun () -> diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index b567c74a199..9871d042f2f 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -110,12 +110,12 @@ module internal SymbolHelpers = let! otherFileCheckResults = match currentDocument.Project.Solution.TryGetDocumentFromPath otherFile with - | Some doc -> + | ValueSome doc -> cancellableTask { let! _, checkFileResults = doc.GetFSharpParseAndCheckResultsAsync("findReferencedSymbolsAsync") return [ checkFileResults, doc ] } - | None -> CancellableTask.singleton [] + | ValueNone -> CancellableTask.singleton [] let symbolUses = (checkFileResults, currentDocument) :: otherFileCheckResults diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index b8bc19cc4a3..cb93bfe7d4c 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -25,9 +25,9 @@ module private CheckerExtensions = /// Parse the source text from the Roslyn document. member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, userOpName: string) = - async { - let! ct = Async.CancellationToken - let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask + cancellableTask { + let! ct = CancellableTask.getCancellationToken () + let! sourceText = document.GetTextAsync(ct) return! checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName = userOpName) } @@ -209,7 +209,7 @@ type Document with /// Parses the given F# document. member this.GetFSharpParseResultsAsync(userOpName) = - async { + cancellableTask { let! checker, _, parsingOptions, _ = this.GetFSharpCompilationOptionsAsync(userOpName) return! checker.ParseDocument(this, parsingOptions, userOpName) } diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index 88aad129dc5..ab27f8d92af 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -33,8 +33,8 @@ module FSharpFindUsagesService = match declarationRange, RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse) with | Some declRange, _ when Range.equals declRange symbolUse -> () - | _, None -> () - | _, Some textSpan -> + | _, ValueNone -> () + | _, ValueSome textSpan -> if allReferences then let definitionItem = if isExternal then @@ -71,10 +71,10 @@ module FSharpFindUsagesService = let! sourceText = doc.GetTextAsync(cancellationToken) match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) with - | Some span -> + | ValueSome span -> let span = Tokenizer.fixupSpan (sourceText, span) return Some(FSharpDocumentSpan(doc, span)) - | None -> return None + | ValueNone -> return None } } |> CancellableTask.whenAll diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 905919f4ce9..2545b367849 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -177,8 +177,8 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let! refSourceText = refDocument.GetTextAsync(cancellationToken) |> Async.AwaitTask match RoslynHelpers.TryFSharpRangeToTextSpan(refSourceText, range) with - | None -> return None - | Some refTextSpan -> return Some(FSharpGoToDefinitionNavigableItem(refDocument, refTextSpan)) + | ValueNone -> return None + | ValueSome refTextSpan -> return Some(FSharpGoToDefinitionNavigableItem(refDocument, refTextSpan)) else return None } @@ -585,7 +585,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let span = match RoslynHelpers.TryFSharpRangeToTextSpan(tmpShownDoc.GetTextAsync(cancellationToken).Result, r) with - | Some span -> span + | ValueSome span -> span | _ -> TextSpan() return span diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index 1107e6f5869..b0bb007f161 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -44,7 +44,7 @@ type internal FSharpNavigateToSearchService [] | true, (version, items) when version = currentVersion -> return items | _ -> let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpNavigateToSearchService)) - let items = parseResults.ParseTree |> NavigateTo.GetNavigableItems + let items = NavigateTo.GetNavigableItems parseResults.ParseTree cache[document.Id] <- currentVersion, items return items } diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs index 9173dd5f863..d40a18e9485 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs @@ -9,6 +9,7 @@ open System.Threading.Tasks open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor open FSharp.Compiler.EditorServices +open CancellableTasks type internal NavigationBarSymbolItem(text, glyph, spans, childItems) = inherit FSharpNavigationBarItem(text, glyph, spans, childItems) @@ -20,10 +21,12 @@ type internal FSharpNavigationBarItemService [] () = interface IFSharpNavigationBarItemService with member _.GetItemsAsync(document, cancellationToken) : Task> = - asyncMaybe { + cancellableTask { + + let! cancellationToken = CancellableTask.getCancellationToken () + let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpNavigationBarItemService)) - |> liftAsync let navItems = Navigation.getNavigation parseResults.ParseTree let! sourceText = document.GetTextAsync(cancellationToken) @@ -31,27 +34,29 @@ type internal FSharpNavigationBarItemService [] () = let rangeToTextSpan range = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) - return - navItems.Declarations - |> Array.choose (fun topLevelDecl -> - rangeToTextSpan (topLevelDecl.Declaration.Range) - |> Option.map (fun topLevelTextSpan -> - let childItems = - topLevelDecl.Nested - |> Array.choose (fun decl -> - rangeToTextSpan (decl.Range) - |> Option.map (fun textSpan -> - NavigationBarSymbolItem(decl.LogicalName, decl.RoslynGlyph, [| textSpan |], null) - :> FSharpNavigationBarItem)) - - NavigationBarSymbolItem( - topLevelDecl.Declaration.LogicalName, - topLevelDecl.Declaration.RoslynGlyph, - [| topLevelTextSpan |], - childItems - ) - :> FSharpNavigationBarItem)) - :> IList<_> - } - |> Async.map (Option.defaultValue emptyResult) - |> RoslynHelpers.StartAsyncAsTask(cancellationToken) + if navItems.Declarations.Length = 0 then + return emptyResult + else + + return + navItems.Declarations + |> Array.chooseV (fun topLevelDecl -> + rangeToTextSpan (topLevelDecl.Declaration.Range) + |> ValueOption.map (fun topLevelTextSpan -> + let childItems = + topLevelDecl.Nested + |> Array.chooseV (fun decl -> + rangeToTextSpan (decl.Range) + |> ValueOption.map (fun textSpan -> + NavigationBarSymbolItem(decl.LogicalName, decl.RoslynGlyph, [| textSpan |], null) + :> FSharpNavigationBarItem)) + + NavigationBarSymbolItem( + topLevelDecl.Declaration.LogicalName, + topLevelDecl.Declaration.RoslynGlyph, + [| topLevelTextSpan |], + childItems + ) + :> FSharpNavigationBarItem)) + :> IList<_> + } |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index 7fb51b9f83b..6e7df021a2c 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -64,8 +64,8 @@ type internal FSharpAsyncQuickInfoSource let textSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, lexerSymbol.Range) match textSpan with - | None -> return None - | Some textSpan -> + | ValueNone -> return None + | ValueSome textSpan -> let trackingSpan = textBuffer.CurrentSnapshot.CreateTrackingSpan(textSpan.Start, textSpan.Length, SpanTrackingMode.EdgeInclusive) diff --git a/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs b/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs index 59e7288b623..86607965acf 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs @@ -15,51 +15,59 @@ open FSharp.Compiler.Syntax open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeRefactorings open Microsoft.CodeAnalysis.CodeActions +open CancellableTasks [] type internal FSharpChangeDerefToValueRefactoring [] () = inherit CodeRefactoringProvider() override _.ComputeRefactoringsAsync context = - asyncMaybe { + cancellableTask { let document = context.Document - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) + let! ct = CancellableTask.getCancellationToken () + let! sourceText = context.Document.GetTextAsync(ct) let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpChangeDerefToValueRefactoring)) - |> liftAsync let selectionRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) - let! derefRange = parseResults.TryRangeOfRefCellDereferenceContainingPos selectionRange.Start - let! exprRange = parseResults.TryRangeOfExpressionBeingDereferencedContainingPos selectionRange.Start + let derefRange = parseResults.TryRangeOfRefCellDereferenceContainingPos selectionRange.Start + let exprRange = parseResults.TryRangeOfExpressionBeingDereferencedContainingPos selectionRange.Start - let combinedRange = Range.unionRanges derefRange exprRange - let! combinedSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, combinedRange) + match derefRange, exprRange with + | Some derefRange, Some exprRange -> - let replacementString = - // Trim off the `!` - sourceText.GetSubText(combinedSpan).ToString().[1..] + ".Value" + let combinedRange = Range.unionRanges derefRange exprRange - let title = SR.UseValueInsteadOfDeref() + let combinedSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, combinedRange) - let getChangedText (sourceText: SourceText) = - sourceText.WithChanges(TextChange(combinedSpan, replacementString)) + match combinedSpan with + | ValueNone -> () + | ValueSome combinedSpan -> + let replacementString = + // Trim off the `!` + sourceText.GetSubText(combinedSpan).ToString().[1..] + ".Value" - let codeAction = - CodeAction.Create( - title, - (fun (cancellationToken: CancellationToken) -> - async { - let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask - return context.Document.WithText(getChangedText sourceText) - } - |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), - title - ) + let title = SR.UseValueInsteadOfDeref() - context.RegisterRefactoring(codeAction) + let getChangedText (sourceText: SourceText) = + sourceText.WithChanges(TextChange(combinedSpan, replacementString)) + + let codeAction = + CodeAction.Create( + title, + (fun (cancellationToken: CancellationToken) -> + cancellableTask { + let! sourceText = context.Document.GetTextAsync(cancellationToken) + return context.Document.WithText(getChangedText sourceText) + } + |> CancellableTask.start cancellationToken), + title + ) + + context.RegisterRefactoring(codeAction) + | _ -> () } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + |> CancellableTask.startAsTask context.CancellationToken diff --git a/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs b/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs index d1559708f23..2efeb8bf6e7 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs @@ -15,48 +15,55 @@ open FSharp.Compiler.Syntax open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeRefactorings open Microsoft.CodeAnalysis.CodeActions +open CancellableTasks [] type internal FSharpChangeTypeofWithNameToNameofExpressionRefactoring [] () = inherit CodeRefactoringProvider() override _.ComputeRefactoringsAsync context = - asyncMaybe { + cancellableTask { let document = context.Document - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) + let! ct = CancellableTask.getCancellationToken () + let! sourceText = context.Document.GetTextAsync(ct) let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpChangeTypeofWithNameToNameofExpressionRefactoring)) - |> liftAsync let selectionRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) - let! namedTypeOfResults = parseResults.TryRangeOfTypeofWithNameAndTypeExpr(selectionRange.Start) - - let! namedTypeSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, namedTypeOfResults.NamedIdentRange) - let! typeofAndNameSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, namedTypeOfResults.FullExpressionRange) - let namedTypeName = sourceText.GetSubText(namedTypeSpan) - let replacementString = $"nameof({namedTypeName})" - - let title = SR.UseNameof() - - let getChangedText (sourceText: SourceText) = - sourceText.WithChanges(TextChange(typeofAndNameSpan, replacementString)) - - let codeAction = - CodeAction.Create( - title, - (fun (cancellationToken: CancellationToken) -> - async { - let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask - return context.Document.WithText(getChangedText sourceText) - } - |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), - title - ) - - context.RegisterRefactoring(codeAction) - } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + let namedTypeOfResults = parseResults.TryRangeOfTypeofWithNameAndTypeExpr(selectionRange.Start) + + match namedTypeOfResults with + | None -> () + | Some namedTypeOfResults -> + + let namedTypeSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, namedTypeOfResults.NamedIdentRange) + let typeofAndNameSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, namedTypeOfResults.FullExpressionRange) + + match namedTypeSpan, typeofAndNameSpan with + | ValueSome namedTypeSpan, ValueSome typeofAndNameSpan -> + + let namedTypeName = sourceText.GetSubText(namedTypeSpan) + let replacementString = $"nameof({namedTypeName})" + + let title = SR.UseNameof() + + let getChangedText (sourceText: SourceText) = + sourceText.WithChanges(TextChange(typeofAndNameSpan, replacementString)) + + let codeAction = + CodeAction.Create( + title, + (fun (cancellationToken: CancellationToken) -> + cancellableTask { + let! sourceText = context.Document.GetTextAsync(cancellationToken) + return context.Document.WithText(getChangedText sourceText) + } |> CancellableTask.start cancellationToken), + title + ) + + context.RegisterRefactoring(codeAction) + | _ -> () + } |> CancellableTask.startAsTask context.CancellationToken diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index ac35e69af22..ca11623b55e 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -128,7 +128,7 @@ module internal BlockStructure = let hintSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, scopeRange.Range) match textSpan, hintSpan with - | Some textSpan, Some hintSpan -> + | ValueSome textSpan, ValueSome hintSpan -> let line = sourceText.Lines.GetLineFromPosition textSpan.Start let bannerText = From 81421fe75a332bf4edeb1bcaa475fdc39596cb25 Mon Sep 17 00:00:00 2001 From: Petr Pokorny <0101@innit.cz> Date: Mon, 4 Sep 2023 16:58:50 +0200 Subject: [PATCH 077/222] Experiments 6 (#2) - Added new LRU Cache supporting versions so we can allow GC of document and project related entries once new versions are observed without having to wait for cache capacity to be depleted. - Hashing of cache keys (versions) - they could get pretty big with a lot of project references - Removed MailboxProcessor from AsyncMemoize, replaced with SemaphoreSlim-based async lock - Made the caches more debugger friendly by putting them under a single object that can be pinned and giving them a DebuggerDisplay - Support for on-disk references (tracking DLL modified times) - Graph processing (fork) uses TaskCompletionSource instead of cancellation to signal end of processing --- .../Driver/GraphChecking/GraphProcessing.fs | 53 +- .../Driver/GraphChecking/GraphProcessing.fsi | 4 + src/Compiler/FSharp.Compiler.Service.fsproj | 2 + src/Compiler/Facilities/AsyncMemoize.fs | 577 +++++++++++++++--- src/Compiler/Facilities/TaskAgent.fs | 90 +++ src/Compiler/Service/BackgroundCompiler.fs | 18 +- src/Compiler/Service/FSharpCheckerResults.fs | 109 +++- src/Compiler/Service/FSharpCheckerResults.fsi | 46 +- src/Compiler/Service/IncrementalBuild.fs | 13 +- src/Compiler/Service/IncrementalBuild.fsi | 3 + src/Compiler/Service/TransparentCompiler.fs | 400 +++++++----- src/Compiler/Service/service.fs | 12 +- src/Compiler/Service/service.fsi | 7 +- src/Compiler/TypedTree/TypedTreePickle.fs | 1 + src/Compiler/Utilities/Activity.fsi | 4 +- .../CompilerService/AsyncLock.fs | 26 + .../CompilerService/AsyncMemoize.fs | 348 +++++++---- .../CompilerService/LruCache.fs | 117 ++++ .../CompilerService/TaskAgent.fs | 81 +++ .../FSharp.Compiler.ComponentTests.fsproj | 16 +- .../FSharpChecker/ProjectSnapshot.fs | 66 ++ .../FSharpChecker/TransparentCompiler.fs | 510 ++++++++++------ .../TypeChecks/Graph/GraphOperations.fs | 2 +- .../ProjectGeneration.fs | 11 +- .../LanguageService/WorkspaceExtensions.fs | 7 +- 25 files changed, 1829 insertions(+), 694 deletions(-) create mode 100644 src/Compiler/Facilities/TaskAgent.fs create mode 100644 tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncLock.fs create mode 100644 tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs create mode 100644 tests/FSharp.Compiler.ComponentTests/CompilerService/TaskAgent.fs create mode 100644 tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs diff --git a/src/Compiler/Driver/GraphChecking/GraphProcessing.fs b/src/Compiler/Driver/GraphChecking/GraphProcessing.fs index c83d283ac25..53a2a1a95c6 100644 --- a/src/Compiler/Driver/GraphChecking/GraphProcessing.fs +++ b/src/Compiler/Driver/GraphChecking/GraphProcessing.fs @@ -1,6 +1,9 @@ module internal FSharp.Compiler.GraphChecking.GraphProcessing open System.Threading +open FSharp.Compiler.GraphChecking +open System.Threading.Tasks +open System /// Information about the node in a graph, describing its relation with other nodes. type NodeInfo<'Item> = @@ -32,6 +35,9 @@ type ProcessedNode<'Item, 'Result> = Result: 'Result } +type GraphProcessingException(msg, ex: System.Exception) = + inherit exn(msg, ex) + let processGraph<'Item, 'Result when 'Item: equality and 'Item: comparison> (graph: Graph<'Item>) (work: ('Item -> ProcessedNode<'Item, 'Result>) -> NodeInfo<'Item> -> 'Result) @@ -150,7 +156,7 @@ let processGraph<'Item, 'Result when 'Item: equality and 'Item: comparison> // If we stopped early due to an exception, reraise it. match getExn () with | None -> () - | Some (item, ex) -> raise (System.Exception($"Encountered exception when processing item '{item}'", ex)) + | Some (item, ex) -> raise (GraphProcessingException($"Encountered exception when processing item '{item}'", ex)) // All calculations succeeded - extract the results and sort in input order. nodes.Values @@ -173,6 +179,11 @@ let processGraphAsync<'Item, 'Result when 'Item: equality and 'Item: comparison> // Cancellation source used to signal either an exception in one of the items or end of processing. let! parentCt = Async.CancellationToken use localCts = new CancellationTokenSource() + + let completionSignal = TaskCompletionSource() + + use _ = parentCt.Register(fun () -> completionSignal.TrySetCanceled() |> ignore) + use cts = CancellationTokenSource.CreateLinkedTokenSource(parentCt, localCts.Token) let makeNode (item: 'Item) : GraphNode<'Item, 'Result> = @@ -219,26 +230,18 @@ let processGraphAsync<'Item, 'Result when 'Item: equality and 'Item: comparison> let processedCount = IncrementableInt(0) - /// Create a setter and getter for an exception raised in one of the work items. - /// Only the first exception encountered is stored - this can cause non-deterministic errors if more than one item fails. - let raiseExn, getExn = - let mutable exn: ('Item * System.Exception) option = None - let lockObj = obj () - // Only set the exception if it hasn't been set already - let setExn newExn = - lock lockObj (fun () -> - match exn with - | Some _ -> () - | None -> exn <- newExn - - localCts.Cancel()) - - let getExn () = exn - setExn, getExn + let raiseExn (item, ex: exn) = + localCts.Cancel() + match ex with + | :? OperationCanceledException -> + completionSignal.TrySetCanceled() + | _ -> + completionSignal.TrySetException(GraphProcessingException($"[*] Encountered exception when processing item '{item}': {ex.Message}", ex)) + |> ignore let incrementProcessedNodesCount () = if processedCount.Increment() = nodes.Count then - localCts.Cancel() + completionSignal.TrySetResult() |> ignore let rec queueNode node = Async.Start( @@ -247,7 +250,7 @@ let processGraphAsync<'Item, 'Result when 'Item: equality and 'Item: comparison> match res with | Choice1Of2 () -> () - | Choice2Of2 ex -> raiseExn (Some(node.Info.Item, ex)) + | Choice2Of2 ex -> raiseExn (node.Info.Item, ex) }, cts.Token ) @@ -277,16 +280,8 @@ let processGraphAsync<'Item, 'Result when 'Item: equality and 'Item: comparison> leaves |> Array.iter queueNode // Wait for end of processing, an exception, or an external cancellation request. - - cts.Token.WaitHandle.WaitOne() |> ignore - // If we stopped early due to external cancellation, throw. - parentCt.ThrowIfCancellationRequested() - - // If we stopped early due to an exception, reraise it. - match getExn () with - | None -> () - | Some (item, ex) -> raise (System.Exception($"Encountered exception when processing item '{item}'", ex)) - + do! completionSignal.Task |> Async.AwaitTask + // All calculations succeeded - extract the results and sort in input order. return nodes.Values diff --git a/src/Compiler/Driver/GraphChecking/GraphProcessing.fsi b/src/Compiler/Driver/GraphChecking/GraphProcessing.fsi index 17a2c94e404..585daa52fd7 100644 --- a/src/Compiler/Driver/GraphChecking/GraphProcessing.fsi +++ b/src/Compiler/Driver/GraphChecking/GraphProcessing.fsi @@ -15,6 +15,10 @@ type ProcessedNode<'Item, 'Result> = { Info: NodeInfo<'Item> Result: 'Result } +type GraphProcessingException = + inherit exn + new: msg: string * ex: System.Exception -> GraphProcessingException + /// /// A generic method to generate results for a graph of work items in parallel. /// Processes leaves first, and after each node has been processed, schedules any now unblocked dependants. diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 5d659728ecb..7ce4cf046a3 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -90,6 +90,7 @@ FSStrings.resx FSStrings.resources + @@ -154,6 +155,7 @@ + --module FSharp.Compiler.AbstractIL.AsciiLexer --internal --open Internal.Utilities.Text.Lexing --open FSharp.Compiler.AbstractIL.AsciiParser --unicode --lexlib Internal.Utilities.Text.Lexing diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 2ab3f99f9f6..9568465ad2c 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -2,139 +2,520 @@ namespace Internal.Utilities.Collections open System open System.Collections.Generic +open System.Diagnostics +open System.IO open System.Threading +open System.Threading.Tasks -open FSharp.Compiler.BuildGraph -type internal Action<'TKey, 'TValue> = - | GetOrCompute of NodeCode<'TValue> * CancellationToken +[] +module Utils = + + /// Return file name with one directory above it + let shortPath path = + let dirPath = Path.GetDirectoryName path + let dir = dirPath.Split Path.DirectorySeparatorChar |> Array.tryLast |> Option.map (sprintf "%s/") |> Option.defaultValue "" + $"{dir}{Path.GetFileName path}" + + +type internal StateUpdate<'TValue> = | CancelRequest + | OriginatorCanceled | JobCompleted of 'TValue + | JobFailed of exn + +type internal MemoizeReply<'TValue> = + | New + | Existing of Task<'TValue> -type MemoizeRequest<'TKey, 'TValue> = 'TKey * Action<'TKey, 'TValue> * AsyncReplyChannel> +type MemoizeRequest<'TValue> = + | GetOrCompute of Async<'TValue> * CancellationToken + | Sync type internal Job<'TValue> = - | Running of NodeCode<'TValue> * CancellationTokenSource - | Completed of NodeCode<'TValue> + | Running of TaskCompletionSource<'TValue> * CancellationTokenSource * Async<'TValue> + | Completed of 'TValue + +type internal CacheEvent = + | Evicted + | Collected + | Weakened + | Strengthened -type internal JobEventType = +type internal JobEvent = | Started | Finished | Canceled + | Evicted + | Collected + | Weakened + | Strengthened + | Failed + +[] +type internal ValueLink<'T when 'T: not struct> = + | Strong of 'T + | Weak of WeakReference<'T> + +[] +type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality and 'TValue: not struct>(keepStrongly, ?keepWeakly, ?requiredToKeep, ?event) = + + let keepWeakly = defaultArg keepWeakly 100 + let requiredToKeep = defaultArg requiredToKeep (fun _ -> false) + let event = defaultArg event (fun _ _ -> ()) + + let dictionary = Dictionary<'TKey, Dictionary<'TVersion, _>>() + + // Lists to keep track of when items were last accessed. First item is most recently accessed. + let strongList = LinkedList<'TKey * 'TVersion * string * ValueLink<'TValue>>() + let weakList = LinkedList<'TKey * 'TVersion * string * ValueLink<'TValue>>() + + let rec removeCollected (node: LinkedListNode<_>) = + if node <> null then + let key, version, label, value = node.Value + match value with + | Weak w -> + let next = node.Next + match w.TryGetTarget() with + | false, _ -> + weakList.Remove node + dictionary[key].Remove version |> ignore + if dictionary[key].Count = 0 then + dictionary.Remove key |> ignore + event CacheEvent.Collected (label, key, version) + | _ -> () + removeCollected next + | _ -> + failwith "Illegal state, strong reference in weak list" + + let cutWeakListIfTooLong() = + if weakList.Count > keepWeakly then + removeCollected weakList.First + + let mutable node = weakList.Last + while weakList.Count > keepWeakly && node <> null do + let previous = node.Previous + let key, version, label, _ = node.Value + weakList.Remove node + dictionary[key].Remove version |> ignore + if dictionary[key].Count = 0 then + dictionary.Remove key |> ignore + event CacheEvent.Evicted (label, key, version) + node <- previous + + let cutStrongListIfTooLong() = + let mutable node = strongList.Last + while strongList.Count > keepStrongly && node <> null do + let previous = node.Previous + match node.Value with + | _, _, _, Strong v when requiredToKeep v -> () + | key, version, label, Strong v -> + strongList.Remove node + node.Value <- key, version, label, Weak (WeakReference<_> v) + weakList.AddFirst node + event CacheEvent.Weakened (label, key, version) + | _key, _version, _label, _ -> failwith "Invalid state, weak reference in strong list" + node <- previous + cutWeakListIfTooLong() + + let pushNodeToTop (node: LinkedListNode<_>) = + match node.Value with + | _, _, _, Strong _ -> + strongList.AddFirst node + cutStrongListIfTooLong() + | _, _, _, Weak _ -> + failwith "Invalid operation, pushing weak reference to strong list" + + let pushValueToTop key version label value = + let node = strongList.AddFirst(value=(key, version, label, Strong value)) + cutStrongListIfTooLong() + node + + member _.DebuggerDisplay = $"Cache(S:{strongList.Count} W:{weakList.Count})" + + member _.Set(key, version, label, value) = + match dictionary.TryGetValue key with + | true, versionDict -> + + if versionDict.ContainsKey version then + // TODO this is normal for unversioned cache; + // failwith "Suspicious - overwriting existing version" + + let node: LinkedListNode<_> = versionDict[version] + match node.Value with + | _, _, _, Strong _ -> strongList.Remove node + | _, _, _, Weak _ -> + weakList.Remove node + event CacheEvent.Strengthened (label, key, version) + + node.Value <- key, version, label, Strong value + pushNodeToTop node + + else + let node = pushValueToTop key version label value + versionDict[version] <- node + // weaken all other versions (unless they're required to be kept) + for otherVersion in versionDict.Keys do + if otherVersion <> version then + let node = versionDict[otherVersion] + match node.Value with + | _, _, _, Strong value when not (requiredToKeep value) -> + strongList.Remove node + node.Value <- key, otherVersion, label, Weak (WeakReference<_> value) + weakList.AddFirst node + event CacheEvent.Weakened (label, key, otherVersion) + cutWeakListIfTooLong() + | _ -> () + + | false, _ -> + let node = pushValueToTop key version label value + dictionary[key] <- Dictionary() + dictionary[key][version] <- node + + member this.Set(key, version, value) = this.Set(key, version, "[no label]", value) + + member _.TryGet(key, version) = + + match dictionary.TryGetValue key with + | false, _ -> None + | true, versionDict -> + match versionDict.TryGetValue version with + | false, _ -> None + | true, node -> + match node.Value with + | _, _, _, Strong v -> + strongList.Remove node + pushNodeToTop node + Some v + + | _, _, label, Weak w -> + match w.TryGetTarget() with + | true, value -> + weakList.Remove node + let node = pushValueToTop key version label value + event CacheEvent.Strengthened (label, key, version) + versionDict[version] <- node + Some value + | _ -> + weakList.Remove node + versionDict.Remove version |> ignore + if versionDict.Count = 0 then + dictionary.Remove key |> ignore + event CacheEvent.Collected (label, key, version) + None + + member _.Remove(key, version) = + match dictionary.TryGetValue key with + | false, _ -> () + | true, versionDict -> + match versionDict.TryGetValue version with + | true, node -> + versionDict.Remove version |> ignore + if versionDict.Count = 0 then + dictionary.Remove key |> ignore + match node.Value with + | _, _, _, Strong _ -> strongList.Remove node + | _, _, _, Weak _ -> weakList.Remove node + | _ -> () + + member this.Set(key, value) = + this.Set(key, Unchecked.defaultof<_>, value) + + member this.TryGet(key) = + this.TryGet(key, Unchecked.defaultof<_>) + + member this.Remove(key) = + this.Remove(key, Unchecked.defaultof<_>) + + member _.GetValues() = + strongList + |> Seq.append weakList + |> Seq.choose (function + | _, _, _, Strong v -> Some v + | _, _, _, Weak w -> + match w.TryGetTarget() with + | true, v -> Some v + | _ -> None) + +type internal ICacheKey<'TKey, 'TVersion> = + abstract member GetKey: unit -> 'TKey + abstract member GetVersion: unit -> 'TVersion + abstract member GetLabel: unit -> string + +type private KeyData<'TKey, 'TVersion> = + { Label: string; Key: 'TKey; Version: 'TVersion } + +/// Tools for hashing things with MD5 into a string that can be used as a cache key. +module internal Md5Hasher = + + let private md5 = new ThreadLocal<_>(fun () -> System.Security.Cryptography.MD5.Create()) + + let private computeHash (bytes: byte array) = md5.Value.ComputeHash(bytes) + + let empty = String.Empty + + let addBytes (bytes: byte array) (s: string) = + let sbytes = System.Text.Encoding.UTF8.GetBytes(s) + Array.append sbytes bytes |> computeHash |> System.BitConverter.ToString |> (fun x -> x.Replace("-", "")) + + let addString (s: string) (s2: string) = + s |> System.Text.Encoding.UTF8.GetBytes |> addBytes <| s2 + + let addSeq<'item> (items: 'item seq) (addItem: 'item -> string -> string) (s: string) = + items |> Seq.fold (fun s a -> addItem a s) s + + let addStrings strings = addSeq strings addString + + let addVersions<'a, 'b when 'a :> ICacheKey<'b, string>> (versions: 'a seq) (s: string) = + versions |> Seq.map (fun x -> x.GetVersion()) |> addStrings <| s + + let addBool (b: bool) (s: string) = + b |> BitConverter.GetBytes |> addBytes <| s + + let addDateTime (dt: System.DateTime) (s: string) = + dt.Ticks.ToString() |> addString <| s + +type AsyncLock() = + + let semaphore = new SemaphoreSlim(1, 1) + + member _.Semaphore = semaphore + + member _.Do(f) = + task { + do! semaphore.WaitAsync() + try + return! f() + finally + semaphore.Release() |> ignore + } -type internal AsyncMemoize<'TKey, 'TValue when 'TKey: equality>(?logEvent: (string -> JobEventType * 'TKey -> unit), ?name: string) = + interface IDisposable with + member _.Dispose() = semaphore.Dispose() + +[] +type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality>(?keepStrongly, ?keepWeakly, ?name: string) = let name = defaultArg name "N/A" - let tok = obj () + + let event = Event<_>() let cache = - MruCache<_, 'TKey, Job<'TValue>>( - keepStrongly = 30, - keepMax = 200, - areSame = (fun (x, y) -> x = y), - requiredToKeep = - function - | Running _ -> true - | _ -> false - ) - - let requestCounts = Dictionary<'TKey, int>() + LruCache<'TKey, 'TVersion, Job<'TValue>>( + keepStrongly = defaultArg keepStrongly 100, + keepWeakly = defaultArg keepWeakly 100, + requiredToKeep = (function Running _ -> true | _ -> false), + event = (function + | CacheEvent.Evicted -> (fun k -> event.Trigger (JobEvent.Evicted, k)) + | CacheEvent.Collected -> (fun k -> event.Trigger (JobEvent.Collected, k)) + | CacheEvent.Weakened -> (fun k -> event.Trigger (JobEvent.Weakened, k)) + | CacheEvent.Strengthened -> (fun k -> event.Trigger (JobEvent.Strengthened, k)))) + + let requestCounts = Dictionary, int>() + let cancellationRegistrations = Dictionary<_, _>() + + let saveRegistration key registration = + cancellationRegistrations[key] <- + match cancellationRegistrations.TryGetValue key with + | true, registrations -> registration::registrations + | _ -> [registration] + + let cancelRegistration key = + match cancellationRegistrations.TryGetValue key with + | true, registrations -> + for r: CancellationTokenRegistration in registrations do + r.Dispose() + cancellationRegistrations.Remove key |> ignore + | _ -> () let incrRequestCount key = - requestCounts.[key] <- + requestCounts[key] <- if requestCounts.ContainsKey key then - requestCounts.[key] + 1 + requestCounts[key] + 1 else 1 - let sendAsync (inbox: MailboxProcessor<_>) key msg = - inbox.PostAndAsyncReply(fun rc -> key, msg, rc) |> Async.Ignore |> Async.Start - - let log event = - logEvent |> Option.iter (fun x -> x name event) - - let agent = - MailboxProcessor.Start(fun (inbox: MailboxProcessor>) -> - - let post = sendAsync inbox - - async { - while true do - try - let _name = name - - let! key, action, replyChannel = inbox.Receive() - - match action, cache.TryGet(tok, key) with - | GetOrCompute _, Some (Completed job) -> replyChannel.Reply job - | GetOrCompute (_, ct), Some (Running (job, _)) -> - incrRequestCount key - replyChannel.Reply job - ct.Register(fun _ -> post key CancelRequest) |> ignore + let decrRequestCount key = + if requestCounts.ContainsKey key then + requestCounts[key] <- requestCounts[key] - 1 - | GetOrCompute (computation, ct), None -> + let log (eventType, keyData: KeyData<_, _>) = + event.Trigger(eventType, (keyData.Label, keyData.Key, keyData.Version)) - let cts = new CancellationTokenSource() + let lock = new AsyncLock() - let startedComputation = - Async.StartAsTask( - Async.AwaitNodeCode( - node { - log (Started, key) - let! result = computation - post key (JobCompleted result) - return result - } - ), - cancellationToken = cts.Token - ) + let processRequest post (key: KeyData<_, _>, msg) = - let job = NodeCode.AwaitTask startedComputation + lock.Do(fun () -> task { - cache.Set(tok, key, (Running(job, cts))) + let cached = cache.TryGet (key.Key, key.Version) - incrRequestCount key + return + match msg, cached with + | Sync, _ -> New + | GetOrCompute _, Some (Completed result) -> Existing (Task.FromResult result) + | GetOrCompute(_, ct), Some (Running (tcs, _, _)) -> + incrRequestCount key - ct.Register(fun _ -> post key CancelRequest) |> ignore - - replyChannel.Reply job - - | CancelRequest, Some (Running (_, cts)) -> - let requestCount = requestCounts.TryGetValue key |> snd + ct.Register(fun _ -> + let _name = name + post (key, CancelRequest)) + |> saveRegistration key - if requestCount > 1 then - requestCounts.[key] <- requestCount - 1 + Existing tcs.Task - else - cts.Cancel() - cache.RemoveAnySimilar(tok, key) - requestCounts.Remove key |> ignore - log (Canceled, key) + | GetOrCompute(computation, ct), None -> + incrRequestCount key - | CancelRequest, None - | CancelRequest, Some (Completed _) -> () + ct.Register(fun _ -> + let _name = name + post (key, OriginatorCanceled)) + |> saveRegistration key + + cache.Set(key.Key, key.Version, key.Label, (Running(TaskCompletionSource(), (new CancellationTokenSource()), computation))) + + New + }) + + let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = + task { + do! Task.Delay 0 + do! lock.Do(fun () -> task { + + let cached = cache.TryGet (key.Key, key.Version) + + match action, cached with + + | OriginatorCanceled, Some (Running (tcs, cts, computation)) -> + + decrRequestCount key + if requestCounts[key] < 1 then + cancelRegistration key + cts.Cancel() + tcs.TrySetCanceled() |> ignore + cache.Remove (key.Key, key.Version) + requestCounts.Remove key |> ignore + log (Canceled, key) + + else + // We need to restart the computation + Task.Run(fun () -> + task { + do! Task.Delay 0 + try + log (Started, key) + let! result = Async.StartAsTask(computation, cancellationToken = cts.Token) + post (key, (JobCompleted result)) + with + | :? OperationCanceledException -> + post (key, CancelRequest) + () + | ex -> + post (key, (JobFailed ex)) + }, cts.Token) |> ignore + + + + | CancelRequest, Some (Running (tcs, cts, _c)) -> + + decrRequestCount key + if requestCounts[key] < 1 then + cancelRegistration key + cts.Cancel() + tcs.TrySetCanceled() |> ignore + cache.Remove (key.Key, key.Version) + requestCounts.Remove key |> ignore + log (Canceled, key) + + | CancelRequest, None + | CancelRequest, Some (Completed _) -> () + + | JobFailed ex, Some (Running (tcs, _cts, _c)) -> + cancelRegistration key + cache.Remove (key.Key, key.Version) + requestCounts.Remove key |> ignore + log (Failed, key) + tcs.TrySetException ex |> ignore + + | JobCompleted result, Some (Running (tcs, _cts, _c)) -> + cancelRegistration key + cache.Set(key.Key, key.Version, key.Label, (Completed result)) + requestCounts.Remove key |> ignore + log (Finished, key) + if tcs.TrySetResult result = false then + failwith "Invalid state: Completed job already completed" + () + + // Job can't be evicted from cache while it's running because then subsequent requesters would be waiting forever + | JobFailed _, None -> failwith "Invalid state: Running job missing in cache (failed)" + + | OriginatorCanceled, None -> failwith "Invalid state: Running job missing in cache (canceled)" + + | JobCompleted _, None -> failwith "Invalid state: Running job missing in cache (completed)" + + | JobFailed ex, Some (Completed _job) -> + failwith $"Invalid state: Failed Completed job \n%A{ex}" + ignore ex + + | JobCompleted _result, Some (Completed _job) -> + failwith "Invalid state: Double-Completed job" + () + + | OriginatorCanceled, Some (Completed _result) -> + failwith "Invalid state: Canceled Completed job" + () + }) + } - | JobCompleted result, Some (Running _) - // Job could be evicted from cache while it's running - | JobCompleted result, None -> - cache.Set(tok, key, (Completed(node.Return result))) - requestCounts.Remove key |> ignore - log (Finished, key) + let rec post msg = + Task.Run(fun () -> processStateUpdate post msg :> Task) |> ignore + + member this.Get'(key, computation) = + + let wrappedKey = + { new ICacheKey<_, _> with + member _.GetKey() = key + member _.GetVersion() = Unchecked.defaultof<_> + member _.GetLabel() = key.ToString() } + this.Get(wrappedKey, computation) + + member _.Get(key: ICacheKey<_, _>, computation) = + + let key = { Label = key.GetLabel(); Key = key.GetKey(); Version = key.GetVersion() } + + async { + let! ct = Async.CancellationToken + match! processRequest post (key, GetOrCompute(computation, ct)) |> Async.AwaitTask with + | New -> + try + return! Async.StartAsTask(async { + log (Started, key) + let! result = computation + post (key, (JobCompleted result)) + return result + }, cancellationToken = ct) |> Async.AwaitTask + with + | :? TaskCanceledException + | :? OperationCanceledException as ex -> + return raise ex + | ex -> + post (key, (JobFailed ex)) + return raise ex + + | Existing job -> return! job |> Async.AwaitTask + } - | JobCompleted _result, Some (_job) -> failwith "If this happens there's a bug" - with - | :? OperationCanceledException as e -> - System.Diagnostics.Trace.TraceError($"AsyncMemoize OperationCanceledException: {e.Message}") - | ex -> System.Diagnostics.Trace.TraceError($"AsyncMemoize Exception: %A{ex}") - }) + member val Event = event.Publish - member _.Get(key, computation) = - node { - let! ct = NodeCode.CancellationToken + member this.OnEvent = this.Event.Add - let! job = - agent.PostAndAsyncReply(fun rc -> key, (GetOrCompute(computation, ct)), rc) - |> NodeCode.AwaitAsync + member _.Locked = lock.Semaphore.CurrentCount < 1 - return! job - } + member this.DebuggerDisplay = + let locked = if this.Locked then " [LOCKED]" else "" + let valueStats = + cache.GetValues() + |> Seq.countBy (function Running _ -> "Running" | Completed _ -> "Completed") + |> Seq.map ((<||) (sprintf "%s: %d")) + |> String.concat " " + $"{name}{locked} {valueStats} ({cache.DebuggerDisplay})" diff --git a/src/Compiler/Facilities/TaskAgent.fs b/src/Compiler/Facilities/TaskAgent.fs new file mode 100644 index 00000000000..9812c5525d7 --- /dev/null +++ b/src/Compiler/Facilities/TaskAgent.fs @@ -0,0 +1,90 @@ +namespace Internal.Utilities.TaskAgent + +open System.Threading +open System.Threading.Tasks + +open System.Collections.Concurrent +open System + + +type AgentMessage<'Message, 'MessageNoReply, 'Reply> = + | ExpectsReply of 'Message * TaskCompletionSource<'Reply> + | DoNotReply of 'MessageNoReply + + +[] +type TaskInbox<'Msg, 'MsgNoReply, 'Reply>() = + + let queue = ConcurrentQueue>() + + let messageNotifications = new SemaphoreSlim(0) + + member _.PostAndAwaitReply(msg) = + let replySource = TaskCompletionSource<'Reply>() + + queue.Enqueue (ExpectsReply (msg, replySource)) + + messageNotifications.Release() |> ignore + + replySource.Task + + member _.Post(msg) = + queue.Enqueue (DoNotReply msg) + messageNotifications.Release() |> ignore + + member _.Receive() = task { + do! messageNotifications.WaitAsync() + + return + match queue.TryDequeue() with + | true, msg -> msg + | false, _ -> failwith "Message notifications broken" + } + + interface IDisposable with + member _.Dispose() = messageNotifications.Dispose() + + +[] +type TaskAgent<'Msg, 'MsgNoReply, 'Reply>( + processMessage: ('MsgNoReply -> unit) -> 'Msg -> 'Reply, + processMessageNoReply: ('MsgNoReply -> unit) -> 'MsgNoReply -> unit) = + let inbox = new TaskInbox<'Msg, 'MsgNoReply, 'Reply>() + + let exceptionEvent = new Event<_>() + + let mutable running = true + + let _loop = backgroundTask { + while running do + match! inbox.Receive() with + | ExpectsReply (msg, replySource) -> + try + let reply = processMessage inbox.Post msg + replySource.SetResult reply + with ex -> + replySource.SetException ex + + | DoNotReply msg -> + try + processMessageNoReply inbox.Post msg + with ex -> + exceptionEvent.Trigger (msg, ex) + } + + member _.NoReplyExceptions = exceptionEvent.Publish + + member _.Status = _loop.Status + + member _.PostAndAwaitReply(msg) = + if not running then failwith "Agent has been disposed and is no longer processing messages" + inbox.PostAndAwaitReply(msg) + + member _.Post(msg) = + if not running then failwith "Agent has been disposed and is no longer processing messages" + inbox.Post(msg) + + interface IDisposable with + member _.Dispose() = + running <- false + (inbox :> IDisposable).Dispose() diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 778cd6bbc42..fdf2cafc0cd 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -44,6 +44,7 @@ type FilePath = string type ProjectPath = string type FileVersion = int + type internal IBackgroundCompiler = /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. @@ -137,7 +138,7 @@ type internal IBackgroundCompiler = abstract member ParseAndCheckFileInProject: fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> - NodeCode + Async /// Parse and typecheck the whole project. abstract member ParseAndCheckProject: options: FSharpProjectOptions * userOpName: string -> NodeCode @@ -166,8 +167,6 @@ type internal IBackgroundCompiler = abstract member ProjectChecked: IEvent - abstract member CacheEvent: IEvent - type ParseCacheLockToken() = interface LockToken @@ -252,8 +251,6 @@ type internal BackgroundCompiler let fileChecked = Event() let projectChecked = Event() - let cacheEvent = Event() - // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.scriptClosureCache /// Information about the derived script closure. let scriptClosureCache = @@ -1450,10 +1447,9 @@ type internal BackgroundCompiler static member ActualCheckFileCount = actualCheckFileCount + interface IBackgroundCompiler with - member _.CacheEvent = cacheEvent.Publish - member _.BeforeBackgroundFileCheck = self.BeforeBackgroundFileCheck member _.CheckFileInProject @@ -1598,12 +1594,12 @@ type internal BackgroundCompiler fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string - ) : NodeCode = - node { + ) : Async = + async { let fileSnapshot = projectSnapshot.SourceFiles |> Seq.find (fun f -> f.FileName = fileName) - let! sourceText = fileSnapshot.GetSource() |> NodeCode.AwaitTask + let! sourceText = fileSnapshot.GetSource() |> Async.AwaitTask let options = projectSnapshot.ToOptions() - return! self.ParseAndCheckFileInProject(fileName, 0, sourceText, options, userOpName) + return! self.ParseAndCheckFileInProject(fileName, 0, sourceText, options, userOpName) |> Async.AwaitNodeCode } member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index f7fbbc5d454..28af76560f1 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -112,23 +112,6 @@ type DelayedILModuleReader = } | _ -> cancellable.Return(Some this.result) -type FSharpFileKey = string * string - -// TODO: use stamp if we have it? -type FSharpProjectSnapshotKey = - { - ProjectFileName: string - SourceFiles: FSharpFileKey list - OtherOptions: string list - ReferencedProjects: FSharpProjectSnapshotKey list - - // Do we need these? - IsIncompleteTypeCheckEnvironment: bool - UseScriptResolutionRules: bool - } - - member this.LastFile = this.SourceFiles |> List.last - [] type FSharpFileSnapshot = { @@ -137,7 +120,7 @@ type FSharpFileSnapshot = GetSource: unit -> Task } - member this.Key = this.FileName, this.Version + member this.IsSignatureFile = this.FileName.ToLower().EndsWith(".fsi") override this.Equals(o) = match o with @@ -146,12 +129,23 @@ type FSharpFileSnapshot = override this.GetHashCode() = this.Key.GetHashCode() + member this.Key = this :> ICacheKey<_, _> + + interface ICacheKey with + member this.GetLabel() = this.FileName |> shortPath + member this.GetKey() = this.FileName + member this.GetVersion() = this.Version + +type ReferenceOnDisk = { Path: string; LastModified: DateTime } + + [] type FSharpProjectSnapshot = { ProjectFileName: string ProjectId: string option SourceFiles: FSharpFileSnapshot list + ReferencesOnDisk: ReferenceOnDisk list OtherOptions: string list ReferencedProjects: FSharpReferencedProjectSnapshot list IsIncompleteTypeCheckEnvironment: bool @@ -180,6 +174,7 @@ type FSharpProjectSnapshot = FSharpProjectSnapshot.UseSameProject(options1, options2) && options1.SourceFiles = options2.SourceFiles && options1.OtherOptions = options2.OtherOptions + && options1.ReferencesOnDisk = options2.ReferencesOnDisk && options1.UnresolvedReferences = options2.UnresolvedReferences && options1.OriginalLoadReferences = options2.OriginalLoadReferences && options1.ReferencedProjects.Length = options2.ReferencedProjects.Length @@ -214,28 +209,64 @@ type FSharpProjectSnapshot = |> List.choose (fun x -> this.SourceFiles |> List.tryItem x) } - member this.Key = - { - ProjectFileName = this.ProjectFileName - SourceFiles = this.SourceFiles |> List.map (fun x -> x.Key) - OtherOptions = this.OtherOptions - ReferencedProjects = - this.ReferencedProjects - |> List.map (function - | FSharpReference (_, x) -> x.Key) - IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment - UseScriptResolutionRules = this.UseScriptResolutionRules - } + member this.WithoutImplFilesThatHaveSignatures = + let files = + (([], Set.empty), this.SourceFiles) + ||> Seq.fold (fun (res, sigs) file -> + if file.IsSignatureFile then + file::res, sigs |> Set.add file.FileName + else + let sigFileName = $"{file.FileName}i" + if sigs.Contains sigFileName then res, sigs |> Set.remove sigFileName + else file::res, sigs) + |> fst + |> List.rev + { this with SourceFiles = files } + + member this.WithoutImplFilesThatHaveSignaturesExceptLastOne = + let lastFile = this.SourceFiles |> List.last + if lastFile.IsSignatureFile then + this.WithoutImplFilesThatHaveSignatures + else + let snapshot = this.WithoutImplFilesThatHaveSignatures + { snapshot with SourceFiles = snapshot.SourceFiles @ [lastFile] } member this.SourceFileNames = this.SourceFiles |> List.map (fun x -> x.FileName) + member this.CommandLineOptions = + seq { for r in this.ReferencesOnDisk do + $"-r:{r.Path}" + yield! this.OtherOptions } |> Seq.toList + member this.WithoutFileVersions = { this with SourceFiles = this.SourceFiles |> List.map (fun x -> { x with Version = "" }) } override this.ToString() = - "FSharpProjectSnapshot(" + this.ProjectFileName + ")" + Path.GetFileNameWithoutExtension this.ProjectFileName + |> sprintf "FSharpProjectSnapshot(%s)" + + member this.Key = this :> ICacheKey<_, _> + + member this.FileKey(fileName) = + { new ICacheKey<_, _> with + member _.GetLabel() = fileName |> shortPath + member _.GetKey() = fileName, this.Key.GetKey() + member _.GetVersion() = this.UpTo(fileName).WithoutImplFilesThatHaveSignaturesExceptLastOne.Key.GetVersion() } + + interface ICacheKey with + member this.GetLabel() = this.ToString() + member this.GetKey() = this.ProjectFileName + member this.GetVersion() = + Md5Hasher.empty + |> Md5Hasher.addString this.ProjectFileName + |> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) + |> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) + |> Md5Hasher.addStrings this.OtherOptions + |> Md5Hasher.addVersions (this.ReferencedProjects |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key)) + |> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment + |> Md5Hasher.addBool this.UseScriptResolutionRules and [] public FSharpReferencedProjectSnapshot = internal @@ -273,6 +304,10 @@ and [] public FSharpReferencedProjectSnapshot = override this.GetHashCode() = this.OutputFile.GetHashCode() + member this.Key = + match this with + | FSharpReference (_, snapshot) -> snapshot.Key + [] type FSharpReferencedProject = | FSharpReference of projectOutputFile: string * options: FSharpProjectOptions @@ -359,7 +394,7 @@ type FSharpProjectSnapshot with ProjectFileName = this.ProjectFileName ProjectId = this.ProjectId SourceFiles = this.SourceFiles |> Seq.map (fun x -> x.FileName) |> Seq.toArray - OtherOptions = this.OtherOptions |> List.toArray + OtherOptions = this.CommandLineOptions |> List.toArray ReferencedProjects = this.ReferencedProjects |> Seq.map (function @@ -391,12 +426,20 @@ type FSharpProjectSnapshot with | _ -> None) |> Async.Parallel + let referencesOnDisk, otherOptions = + options.OtherOptions + |> Array.partition (fun x -> x.StartsWith("-r:")) + |> map1Of2 (Array.map (fun x -> + let path = x.Substring(3) + { Path = path; LastModified = FileSystem.GetLastWriteTimeShim(path) } )) + return { ProjectFileName = options.ProjectFileName ProjectId = options.ProjectId SourceFiles = sourceFiles |> List.ofArray - OtherOptions = options.OtherOptions |> List.ofArray + ReferencesOnDisk = referencesOnDisk |> List.ofArray + OtherOptions = otherOptions |> List.ofArray ReferencedProjects = referencedProjects |> List.ofArray IsIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment UseScriptResolutionRules = options.UseScriptResolutionRules diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 6bc04e533ee..b5c96c1a763 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -27,6 +27,8 @@ open FSharp.Compiler.TypedTreeOps open FSharp.Compiler.TcGlobals open FSharp.Compiler.Text +open Internal.Utilities.Collections + /// Delays the creation of an ILModuleReader [] type DelayedILModuleReader = @@ -42,27 +44,20 @@ type DelayedILModuleReader = /// Unused in this API type public FSharpUnresolvedReferencesSet = internal FSharpUnresolvedReferencesSet of UnresolvedAssemblyReference list -type internal FSharpFileKey = string * string - -type internal FSharpProjectSnapshotKey = - { ProjectFileName: string - SourceFiles: FSharpFileKey list - OtherOptions: string list - ReferencedProjects: FSharpProjectSnapshotKey list - - // Do we need these? - IsIncompleteTypeCheckEnvironment: bool - UseScriptResolutionRules: bool } - - member LastFile: FSharpFileKey - [] type FSharpFileSnapshot = { FileName: string Version: string GetSource: unit -> Task } - member internal Key: FSharpFileKey + member internal Key: ICacheKey + + interface ICacheKey + + +/// Referenced assembly on disk. Includes last modified time so we know we need to rebuild when it changes. +type ReferenceOnDisk = { Path: string; LastModified: DateTime } + [] type FSharpProjectSnapshot = @@ -73,10 +68,13 @@ type FSharpProjectSnapshot = /// This is the unique identifier for the project, it is case sensitive. If it's None, will key off of ProjectFileName in our caching. ProjectId: string option - /// The files in the project + /// The files in the project. SourceFiles: FSharpFileSnapshot list - /// Additional command line argument options for the project. These can include additional files and references. + /// Referenced assemblies on disk. + ReferencesOnDisk: ReferenceOnDisk list + + /// Additional command line argument options for the project. OtherOptions: string list /// The command line arguments for the other projects referenced by this project, indexed by the @@ -119,6 +117,8 @@ type FSharpProjectSnapshot = member SourceFileNames: string list + member CommandLineOptions: string list + member IndexOf: fileName: string -> FileIndex /// A snapshot of the same project but only up to the given file index (including). @@ -130,10 +130,18 @@ type FSharpProjectSnapshot = /// A snapshot of the same project but only with source files specified by given indexes. member OnlyWith: fileIndexes: Set -> FSharpProjectSnapshot + member WithoutImplFilesThatHaveSignatures: FSharpProjectSnapshot + + member WithoutImplFilesThatHaveSignaturesExceptLastOne: FSharpProjectSnapshot + /// A snapshot of the same project with file versions removed. member WithoutFileVersions: FSharpProjectSnapshot - member internal Key: FSharpProjectSnapshotKey + member internal Key: ICacheKey + + member internal FileKey: fileName:string -> ICacheKey<(string * string), string> + + interface ICacheKey and [] public FSharpReferencedProjectSnapshot = internal @@ -158,6 +166,8 @@ and [] public FSharpReferencedProjectSnapshot = static member CreateFSharp: projectOutputFile: string * options: FSharpProjectSnapshot -> FSharpReferencedProjectSnapshot + member internal Key: ICacheKey + /// A set of information describing a project or script build configuration. type public FSharpProjectOptions = { diff --git a/src/Compiler/Service/IncrementalBuild.fs b/src/Compiler/Service/IncrementalBuild.fs index 60028bdc5c3..300e917f5d5 100644 --- a/src/Compiler/Service/IncrementalBuild.fs +++ b/src/Compiler/Service/IncrementalBuild.fs @@ -485,10 +485,19 @@ type BoundModel private ( syntaxTreeOpt ) - /// Global service state -type FrameworkImportsCacheKey = FrameworkImportsCacheKey of resolvedpath: string list * assemblyName: string * targetFrameworkDirectories: string list * fsharpBinaries: string * langVersion: decimal +type FrameworkImportsCacheKey = + | FrameworkImportsCacheKey of resolvedpath: string list * assemblyName: string * targetFrameworkDirectories: string list * fsharpBinaries: string * langVersion: decimal + + interface ICacheKey with + member this.GetKey() = + this |> function FrameworkImportsCacheKey(assemblyName=a) -> a + + member this.GetLabel() = + this |> function FrameworkImportsCacheKey(assemblyName=a) -> a + member this.GetVersion() = this + /// Represents a cache of 'framework' references that can be shared between multiple incremental builds type FrameworkImportsCache(size) = diff --git a/src/Compiler/Service/IncrementalBuild.fsi b/src/Compiler/Service/IncrementalBuild.fsi index d4fe402cce1..2b2c00dd4b6 100644 --- a/src/Compiler/Service/IncrementalBuild.fsi +++ b/src/Compiler/Service/IncrementalBuild.fsi @@ -22,6 +22,7 @@ open FSharp.Compiler.TcGlobals open FSharp.Compiler.Text open FSharp.Compiler.TypedTree open FSharp.Compiler.BuildGraph +open Internal.Utilities.Collections type internal FrameworkImportsCacheKey = | FrameworkImportsCacheKey of @@ -30,6 +31,8 @@ type internal FrameworkImportsCacheKey = targetFrameworkDirectories: string list * fsharpBinaries: string * langVersion: decimal + + interface ICacheKey /// Lookup the global static cache for building the FrameworkTcImports type internal FrameworkImportsCache = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index b5c92b30957..51727da182a 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -42,6 +42,7 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.CreateILModule open FSharp.Compiler.TypedTreeOps + type internal FSharpFile = { Range: range @@ -131,9 +132,21 @@ type internal DependencyGraphType = [] type internal Extensions = [] - static member Key(fileSnapshots: FSharpFileSnapshot list) = - fileSnapshots |> List.map (fun f -> f.Key) - + static member Key(fileSnapshots: FSharpFileSnapshot list, ?extraKeyFlag) = + + { new ICacheKey<_, _> with + member _.GetLabel() = + let lastFile = fileSnapshots |> List.tryLast |> Option.map (fun f -> f.FileName |> shortPath) |> Option.defaultValue "[no file]" + $"%d{fileSnapshots.Length} files ending with {lastFile}" + member _.GetKey() = + Md5Hasher.empty + |> Md5Hasher.addStrings (fileSnapshots |> Seq.map (fun f -> f.FileName)) + |> pair extraKeyFlag + member _.GetVersion() = + Md5Hasher.empty + |> Md5Hasher.addStrings (fileSnapshots |> Seq.map (fun f -> f.Version)) } + +[] module private TypeCheckingGraphProcessing = open FSharp.Compiler.GraphChecking.GraphProcessing @@ -196,10 +209,10 @@ module private TypeCheckingGraphProcessing = /// let processTypeCheckingGraph (graph: Graph) - (work: NodeToTypeCheck -> TcInfo -> NodeCode>) + (work: NodeToTypeCheck -> TcInfo -> Async>) (emptyState: TcInfo) - : NodeCode<(int * PartialResult) list * TcInfo> = - node { + : Async<(int * PartialResult) list * TcInfo> = + async { let workWrapper (getProcessedNode: NodeToTypeCheck @@ -217,12 +230,12 @@ module private TypeCheckingGraphProcessing = let inputState = combineResults emptyState deps transitiveDeps folder - let! singleRes = work node.Item inputState |> Async.AwaitNodeCode + let! singleRes = work node.Item inputState let state = folder inputState singleRes return state, singleRes } - let! results = processGraphAsync graph workWrapper |> NodeCode.AwaitAsync + let! results = processGraphAsync graph workWrapper let finalFileResults, state: (int * PartialResult) list * TcInfo = (([], emptyState), @@ -238,7 +251,37 @@ module private TypeCheckingGraphProcessing = return finalFileResults, state } -open TypeCheckingGraphProcessing + +type internal CompilerCaches() = + + member val ParseFile = AsyncMemoize(keepStrongly=1000, keepWeakly=2000, name="ParseFile") + + member val ParseAndCheckFileInProject = AsyncMemoize(name="ParseAndCheckFileInProject") + + member val ParseAndCheckAllFilesInProject = AsyncMemoize(name="ParseAndCheckFullProject") + + member val ParseAndCheckProject = AsyncMemoize(name="ParseAndCheckProject") + + member val FrameworkImports = AsyncMemoize(name="FrameworkImports") + + member val BootstrapInfoStatic = AsyncMemoize(name="BootstrapInfoStatic") + + member val BootstrapInfo = AsyncMemoize(name="BootstrapInfo") + + member val TcFile = AsyncMemoize(name="TcPrior") + + member val TcIntermediate = AsyncMemoize(keepStrongly=1000, keepWeakly=2000, name="TcIntermediate") + + member val DependencyGraph = AsyncMemoize(name="DependencyGraph") + + member val ProjectExtras = AsyncMemoize(name="ProjectExtras") + + member val AssemblyData = AsyncMemoize(name="AssemblyData") + + member val SemanticClassification = AsyncMemoize(name="SemanticClassification") + + member val ItemKeyStore = AsyncMemoize(name="ItemKeyStore") + type internal TransparentCompiler ( @@ -261,41 +304,7 @@ type internal TransparentCompiler // Is having just one of these ok? let lexResourceManager = Lexhelp.LexResourceManager() - let cacheEvent = new Event() - let triggerCacheEvent name (e, k) = cacheEvent.Trigger(name, e, k) - - let ParseFileCache = AsyncMemoize(triggerCacheEvent, "ParseFile") - - let ParseAndCheckFileInProjectCache = - AsyncMemoize(triggerCacheEvent, "ParseAndCheckFileInProject") - - let ParseAndCheckAllFilesInProjectCache = - AsyncMemoize(triggerCacheEvent, "ParseAndCheckFullProject") - - let ParseAndCheckProjectCache = - AsyncMemoize(triggerCacheEvent, "ParseAndCheckProject") - - let FrameworkImportsCache = AsyncMemoize(triggerCacheEvent, "FrameworkImports") - - let BootstrapInfoStaticCache = - AsyncMemoize(triggerCacheEvent, "BootstrapInfoStatic") - - let BootstrapInfoCache = AsyncMemoize(triggerCacheEvent, "BootstrapInfo") - - let TcFileCache = AsyncMemoize(triggerCacheEvent, "TcPrior") - - let TcIntermediateCache = AsyncMemoize(triggerCacheEvent, "TcIntermediate") - - let DependencyGraphCache = AsyncMemoize(triggerCacheEvent, "DependencyGraph") - - let ProjectExtrasCache = AsyncMemoize(triggerCacheEvent, "ProjectExtras") - - let AssemblyDataCache = AsyncMemoize(triggerCacheEvent, "AssemblyData") - - let SemanticClassificationCache = - AsyncMemoize(triggerCacheEvent, "SemanticClassification") - - let ItemKeyStoreCache = AsyncMemoize(triggerCacheEvent, "ItemKeyStore") + let caches = CompilerCaches() // We currently share one global dependency provider for all scripts for the FSharpChecker. // For projects, one is used per project. @@ -343,12 +352,12 @@ type internal TransparentCompiler tcConfig.langVersion.SpecifiedVersion ) - FrameworkImportsCache.Get( + caches.FrameworkImports.Get( key, - node { + async { use _ = Activity.start "ComputeFrameworkImports" [] let tcConfigP = TcConfigProvider.Constant tcConfig - return! TcImports.BuildFrameworkTcImports(tcConfigP, frameworkDLLs, nonFrameworkResolutions) + return! TcImports.BuildFrameworkTcImports(tcConfigP, frameworkDLLs, nonFrameworkResolutions) |> Async.AwaitNodeCode } ) @@ -368,14 +377,14 @@ type internal TransparentCompiler importsInvalidatedByTypeProvider: Event ) = - node { + async { let diagnosticsLogger = CompilationDiagnosticLogger("CombineImportedAssembliesTask", tcConfig.diagnosticsOptions) use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) let! tcImports = - node { + async { try let! tcImports = TcImports.BuildNonFrameworkTcImports( @@ -384,7 +393,7 @@ type internal TransparentCompiler nonFrameworkResolutions, unresolvedReferences, dependencyProvider - ) + ) |> Async.AwaitNodeCode #if !NO_TYPEPROVIDERS // TODO: review and handle the event tcImports.GetCcusExcludingBase() @@ -414,7 +423,11 @@ type internal TransparentCompiler ignore importsInvalidatedByTypeProvider #endif return tcImports - with exn -> + with + | :? OperationCanceledException -> + // if it's been canceled then it shouldn't be needed anymore + return frameworkTcImports + | exn -> Debug.Assert(false, sprintf "Could not BuildAllReferencedDllTcImports %A" exn) diagnosticsLogger.Warning exn return frameworkTcImports @@ -495,16 +508,16 @@ type internal TransparentCompiler let key = projectSnapshot.WithoutFileVersions.Key - BootstrapInfoStaticCache.Get( + caches.BootstrapInfoStatic.Get( key, - node { + async { use _ = Activity.start "ComputeBootstrapInfoStatic" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] let useSimpleResolutionSwitch = "--simpleresolution" - let commandLineArgs = projectSnapshot.OtherOptions + let commandLineArgs = projectSnapshot.CommandLineOptions let defaultFSharpBinariesDir = FSharpCheckerResultsSettings.defaultFSharpBinariesDir let useScriptResolutionRules = projectSnapshot.UseScriptResolutionRules @@ -643,25 +656,6 @@ type internal TransparentCompiler use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) - // TODO: might need to put something like this somewhere, probably FSharpProjectSnapshot? - //// Get the names and time stamps of all the non-framework referenced assemblies, which will act - //// as inputs to one of the nodes in the build. - //// - //// This operation is done when constructing the builder itself, rather than as an incremental task. - //let nonFrameworkAssemblyInputs = - // // Note we are not calling diagnosticsLogger.GetDiagnostics() anywhere for this task. - // // This is ok because not much can actually go wrong here. - // let diagnosticsLogger = CompilationDiagnosticLogger("nonFrameworkAssemblyInputs", tcConfig.diagnosticsOptions) - // // Return the disposable object that cleans up - // use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) - - // [ for r in nonFrameworkResolutions do - // let fileName = r.resolvedPath - // yield (Choice1Of2 fileName, (fun (cache: TimeStampCache) -> cache.GetFileTimeStamp fileName)) - - // for pr in projectReferences do - // yield Choice2Of2 pr, (fun (cache: TimeStampCache) -> cache.GetProjectReferenceTimeStamp pr) ] - let tcConfigP = TcConfigProvider.Constant tcConfig let importsInvalidatedByTypeProvider = Event() @@ -730,7 +724,7 @@ type internal TransparentCompiler ) let computeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = - node { + async { let! assemblyName, outFile, sourceFiles, @@ -779,9 +773,9 @@ type internal TransparentCompiler } let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) = - BootstrapInfoCache.Get( + caches.BootstrapInfo.Get( projectSnapshot.Key, - node { + async { use _ = Activity.start "ComputeBootstrapInfo" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] @@ -790,7 +784,7 @@ type internal TransparentCompiler use _ = new CompilationGlobalsScope(delayedLogger, BuildPhase.Parameter) let! bootstrapInfoOpt = - node { + async { try return! computeBootstrapInfoInner projectSnapshot with exn -> @@ -822,11 +816,16 @@ type internal TransparentCompiler ) let ComputeParseFile bootstrapInfo (file: FSharpFile) = - let key = file.Source.Key, file.IsLastCompiland, file.IsExe + + let key = + { new ICacheKey<_, _> with + member _.GetLabel() = shortPath file.Source.FileName + member _.GetKey() = file.Source.FileName + member _.GetVersion() = file.Source.Version, file.IsLastCompiland, file.IsExe } - ParseFileCache.Get( + caches.ParseFile.Get( key, - node { + async { use _ = Activity.start "ComputeParseFile" @@ -844,7 +843,7 @@ type internal TransparentCompiler let flags = file.IsLastCompiland, file.IsExe let fileName = file.Source.FileName - let! sourceText = file.Source.GetSource() |> NodeCode.AwaitTask + let! sourceText = file.Source.GetSource() |> Async.AwaitTask let input = ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) @@ -853,8 +852,20 @@ type internal TransparentCompiler } ) - let computeDependencyGraph parsedInputs (tcConfig: TcConfig) (processGraph: Graph -> Graph) = - node { + // In case we don't want to use any parallel processing + let mkLinearGraph count : Graph = + seq { + 0, [| |] + yield! + [0..count-1] + |> Seq.rev + |> Seq.pairwise + |> Seq.map (fun (a, b) -> a, [| b |]) + } + |> Graph.make + + let computeDependencyGraph (tcConfig: TcConfig) parsedInputs (processGraph: Graph -> Graph) = + async { let sourceFiles: FileInProject array = parsedInputs |> Seq.toArray @@ -871,10 +882,14 @@ type internal TransparentCompiler let filePairs = FilePairMap(sourceFiles) // TODO: we will probably want to cache and re-use larger graphs if available + let graph = - DependencyResolution.mkGraph tcConfig.compilingFSharpCore filePairs sourceFiles - |> fst - |> processGraph + if tcConfig.compilingFSharpCore then + mkLinearGraph sourceFiles.Length + else + DependencyResolution.mkGraph filePairs sourceFiles + |> fst + |> processGraph let nodeGraph = TransformDependencyGraph(graph, filePairs) @@ -898,36 +913,74 @@ type internal TransparentCompiler return nodeGraph, graph } - let ComputeDependencyGraphForFile (priorSnapshot: FSharpProjectSnapshot) parsedInputs (tcConfig: TcConfig) = - let key = priorSnapshot.SourceFiles.Key(), DependencyGraphType.File - let lastFileIndex = (parsedInputs |> Array.length) - 1 - DependencyGraphCache.Get(key, computeDependencyGraph parsedInputs tcConfig (Graph.subGraphFor lastFileIndex)) - - let ComputeDependencyGraphForProject (projectSnapshot: FSharpProjectSnapshot) parsedInputs (tcConfig: TcConfig) = - let key = projectSnapshot.SourceFiles.Key(), DependencyGraphType.Project - DependencyGraphCache.Get(key, computeDependencyGraph parsedInputs tcConfig id) + let removeImplFilesThatHaveSignatures (projectSnapshot: FSharpProjectSnapshot) (graph: Graph) = + + let removeIndexes = + projectSnapshot.SourceFileNames + |> Seq.mapi pair + |> Seq.groupBy (snd >> (fun fileName -> if fileName.EndsWith(".fsi") then fileName.Substring(0, fileName.Length - 1) else fileName)) + |> Seq.map (snd >> (Seq.toList)) + |> Seq.choose (function [idx1, _; idx2, _] -> max idx1 idx2 |> Some | _ -> None) + |> Set + + graph + |> Seq.filter (fun x -> not (removeIndexes.Contains x.Key)) + |> Seq.map (fun x -> x.Key, x.Value |> Array.filter (fun node -> not (removeIndexes.Contains node))) + |> Graph.make + + let removeImplFilesThatHaveSignaturesExceptLastOne (projectSnapshot: FSharpProjectSnapshot) (graph: Graph) = + + let removeIndexes = + projectSnapshot.SourceFileNames + |> Seq.mapi pair + |> Seq.groupBy (snd >> (fun fileName -> if fileName.EndsWith(".fsi") then fileName.Substring(0, fileName.Length - 1) else fileName)) + |> Seq.map (snd >> (Seq.toList)) + |> Seq.choose (function [idx1, _; idx2, _] -> max idx1 idx2 |> Some | _ -> None) + |> Set + // Don't remove the last file + |> Set.remove (projectSnapshot.SourceFiles.Length - 1) + + graph + |> Seq.filter (fun x -> not (removeIndexes.Contains x.Key)) + |> Seq.map (fun x -> x.Key, x.Value |> Array.filter (fun node -> not (removeIndexes.Contains node))) + |> Graph.make + + let ComputeDependencyGraphForFile (tcConfig: TcConfig) (priorSnapshot: FSharpProjectSnapshot) parsedInputs = + let key = priorSnapshot.SourceFiles.Key(DependencyGraphType.File) + //let lastFileIndex = (parsedInputs |> Array.length) - 1 + //caches.DependencyGraph.Get(key, computeDependencyGraph parsedInputs (Graph.subGraphFor lastFileIndex)) + caches.DependencyGraph.Get(key, computeDependencyGraph tcConfig parsedInputs (removeImplFilesThatHaveSignaturesExceptLastOne priorSnapshot)) + + let ComputeDependencyGraphForProject (tcConfig: TcConfig) (projectSnapshot: FSharpProjectSnapshot) parsedInputs = + let key = projectSnapshot.SourceFiles.Key(DependencyGraphType.Project) + //caches.DependencyGraph.Get(key, computeDependencyGraph parsedInputs (removeImplFilesThatHaveSignatures projectSnapshot)) + caches.DependencyGraph.Get(key, computeDependencyGraph tcConfig parsedInputs id) let ComputeTcIntermediate (projectSnapshot: FSharpProjectSnapshot) (dependencyGraph: Graph) - (fileIndex: FileIndex) + (node: NodeToTypeCheck) (parsedInput: ParsedInput, parseErrors) bootstrapInfo (prevTcInfo: TcInfo) = - let dependencyFiles = dependencyGraph |> Graph.subGraphFor fileIndex |> Graph.nodes ignore dependencyGraph - TcIntermediateCache.Get( - projectSnapshot.OnlyWith(dependencyFiles).Key, - //projectSnapshot.UpTo(fileIndex).Key, - node { + let key = projectSnapshot.FileKey parsedInput.FileName + + caches.TcIntermediate.Get( + key, + async { let input = parsedInput let fileName = input.FileName use _ = - Activity.start "ComputeTcIntermediate" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + Activity.start "ComputeTcIntermediate" [| + Activity.Tags.fileName, fileName |> Path.GetFileName + "key", key.GetKey() |> sprintf "%A" + "version", key.GetVersion() + |] let tcConfig = bootstrapInfo.TcConfig let tcGlobals = bootstrapInfo.TcGlobals @@ -957,8 +1010,6 @@ type internal TransparentCompiler let input, moduleNamesDict = DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input - let node = NodeToTypeCheck.PhysicalFile fileIndex // TODO: is this correct? - let! finisher = CheckOneInputWithCallback node @@ -970,8 +1021,8 @@ type internal TransparentCompiler TcResultsSink.WithSink sink, prevTcInfo.tcState, input, - false) - |> NodeCode.FromCancellable + true) + |> Cancellable.toAsync //fileChecked.Trigger fileName @@ -998,18 +1049,25 @@ type internal TransparentCompiler (fileNode: NodeToTypeCheck) tcInfo = - node { + async { match fileNode with | NodeToTypeCheck.PhysicalFile index -> let input, parseErrors, _ = parsedInputs[index] - let! tcIntermediate = ComputeTcIntermediate projectSnapshot dependencyFiles index (input, parseErrors) bootstrapInfo tcInfo + let! tcIntermediate = ComputeTcIntermediate projectSnapshot dependencyFiles fileNode (input, parseErrors) bootstrapInfo tcInfo let (Finisher (node = node; finisher = finisher)) = tcIntermediate.finisher return Finisher( node, (fun tcInfo -> + + if tcInfo.stateContainsNodes |> Set.contains fileNode then + failwith $"Oops!" + + if tcInfo.stateContainsNodes |> Set.contains (NodeToTypeCheck.ArtificialImplFile (index - 1)) then + failwith $"Oops???" + let partialResult, tcState = finisher tcInfo.tcState let tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile = partialResult @@ -1045,6 +1103,12 @@ type internal TransparentCompiler fileNode, (fun tcInfo -> + if tcInfo.stateContainsNodes |> Set.contains fileNode then + failwith $"Oops!" + + if tcInfo.stateContainsNodes |> Set.contains (NodeToTypeCheck.PhysicalFile (index + 1)) then + failwith $"Oops!!!" + let parsedInput, _parseErrors, _ = parsedInputs[index] let prefixPathOpt = None // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. @@ -1083,11 +1147,10 @@ type internal TransparentCompiler let ComputeTcFile (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = let priorSnapshot = projectSnapshot.UpTo file.Source.FileName - let key = priorSnapshot.Key - TcFileCache.Get( - key, - node { + caches.TcFile.Get( + priorSnapshot.FileKey file.Source.FileName, + async { use _ = Activity.start "ComputeTcFile" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName |] @@ -1098,11 +1161,11 @@ type internal TransparentCompiler file } - let! parsedInputs = files |> Seq.map (ComputeParseFile bootstrapInfo) |> NodeCode.Parallel + let! parsedInputs = files |> Seq.map (ComputeParseFile bootstrapInfo) |> Async.Parallel let! graph, dependencyFiles = - ComputeDependencyGraphForFile priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig - //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig + ComputeDependencyGraphForFile bootstrapInfo.TcConfig priorSnapshot (parsedInputs |> Array.map p13) + //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) let! results, tcInfo = processTypeCheckingGraph @@ -1117,7 +1180,7 @@ type internal TransparentCompiler ) let getParseResult (bootstrapInfo: BootstrapInfo) creationDiags fileName = - node { + async { let file = bootstrapInfo.GetFile fileName let! parseTree, parseDiagnostics, sourceText = ComputeParseFile bootstrapInfo file @@ -1150,11 +1213,9 @@ type internal TransparentCompiler FSharpParseFileResults(diagnostics, parseTree, true, [||]) let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = - let key = fileName, projectSnapshot.Key - - ParseAndCheckFileInProjectCache.Get( - key, - node { + caches.ParseAndCheckFileInProject.Get( + projectSnapshot.FileKey fileName, + async { use _ = Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] @@ -1237,9 +1298,9 @@ type internal TransparentCompiler ) let ComputeParseAndCheckAllFilesInProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = - ParseAndCheckAllFilesInProjectCache.Get( + caches.ParseAndCheckAllFilesInProject.Get( projectSnapshot.Key, - node { + async { use _ = Activity.start "ComputeParseAndCheckAllFilesInProject" @@ -1248,10 +1309,10 @@ type internal TransparentCompiler let! parsedInputs = bootstrapInfo.SourceFiles |> Seq.map (ComputeParseFile bootstrapInfo) - |> NodeCode.Parallel + |> Async.Parallel let! graph, dependencyFiles = - ComputeDependencyGraphForProject projectSnapshot (parsedInputs |> Array.map p13) bootstrapInfo.TcConfig + ComputeDependencyGraphForProject bootstrapInfo.TcConfig projectSnapshot (parsedInputs |> Array.map p13) return! processTypeCheckingGraph @@ -1262,9 +1323,9 @@ type internal TransparentCompiler ) let ComputeProjectExtras (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = - ProjectExtrasCache.Get( - projectSnapshot.Key, - node { + caches.ProjectExtras.Get( + projectSnapshot.WithoutImplFilesThatHaveSignatures.Key, + async { let! results, finalInfo = ComputeParseAndCheckAllFilesInProject bootstrapInfo projectSnapshot let assemblyName = bootstrapInfo.AssemblyName @@ -1354,10 +1415,10 @@ type internal TransparentCompiler } ) - let ComputeGetAssemblyData (projectSnapshot: FSharpProjectSnapshot) = - AssemblyDataCache.Get( - projectSnapshot.Key, - node { + let ComputeAssemblyData (projectSnapshot: FSharpProjectSnapshot) = + caches.AssemblyData.Get( + projectSnapshot.WithoutImplFilesThatHaveSignatures.Key, + async { match! ComputeBootstrapInfo projectSnapshot with | None, _ -> return ProjectAssemblyDataResult.Unavailable true | Some bootstrapInfo, _creationDiags -> @@ -1367,9 +1428,9 @@ type internal TransparentCompiler ) let ComputeParseAndCheckProject (projectSnapshot: FSharpProjectSnapshot) = - ParseAndCheckProjectCache.Get( + caches.ParseAndCheckProject.Get( projectSnapshot.Key, - node { + async { match! ComputeBootstrapInfo projectSnapshot with | None, creationDiags -> @@ -1434,7 +1495,7 @@ type internal TransparentCompiler ) let tryGetSink fileName (projectSnapshot: FSharpProjectSnapshot) = - node { + async { match! ComputeBootstrapInfo projectSnapshot with | None, _ -> return None | Some bootstrapInfo, _creationDiags -> @@ -1447,11 +1508,9 @@ type internal TransparentCompiler } let ComputeSemanticClassification (fileName: string, projectSnapshot: FSharpProjectSnapshot) = - let key = (projectSnapshot.UpTo fileName).Key - - SemanticClassificationCache.Get( - key, - node { + caches.SemanticClassification.Get( + projectSnapshot.FileKey fileName, + async { use _ = Activity.start "ComputeSemanticClassification" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] @@ -1479,11 +1538,9 @@ type internal TransparentCompiler ) let ComputeItemKeyStore (fileName: string, projectSnapshot: FSharpProjectSnapshot) = - let key = (projectSnapshot.UpTo fileName).Key - - ItemKeyStoreCache.Get( - key, - node { + caches.ItemKeyStore.Get( + projectSnapshot.FileKey fileName, + async { use _ = Activity.start "ComputeItemKeyStore" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] @@ -1518,7 +1575,7 @@ type internal TransparentCompiler ) member _.ParseFile(fileName, projectSnapshot: FSharpProjectSnapshot, _userOpName) = - node { + async { //use _ = // Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] @@ -1536,17 +1593,18 @@ type internal TransparentCompiler member _.FindReferencesInFile(fileName: string, projectSnapshot: FSharpProjectSnapshot, symbol: FSharpSymbol, userOpName: string) = ignore userOpName - node { + async { match! ComputeItemKeyStore(fileName, projectSnapshot) with | None -> return Seq.empty | Some itemKeyStore -> return itemKeyStore.FindAll symbol.Item } - member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, _userOpName) = ComputeGetAssemblyData projectSnapshot + member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, _userOpName) = + ComputeAssemblyData projectSnapshot |> NodeCode.AwaitAsync - interface IBackgroundCompiler with + member _.Caches = caches - member _.CacheEvent = cacheEvent.Publish + interface IBackgroundCompiler with member this.BeforeBackgroundFileCheck: IEvent = backgroundCompiler.BeforeBackgroundFileCheck @@ -1566,7 +1624,7 @@ type internal TransparentCompiler |> NodeCode.AwaitAsync ignore parseResults - let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) |> NodeCode.AwaitAsync return result } @@ -1585,7 +1643,7 @@ type internal TransparentCompiler |> NodeCode.AwaitAsync ignore parseResults - let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) |> NodeCode.AwaitAsync return Some result } @@ -1612,11 +1670,13 @@ type internal TransparentCompiler node { ignore canInvalidateProject let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! this.FindReferencesInFile(fileName, snapshot, symbol, userOpName) + return! this.FindReferencesInFile(fileName, snapshot, symbol, userOpName) |> NodeCode.AwaitAsync } member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = - this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) + node { + return! this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) |> NodeCode.AwaitAsync + } member _.FrameworkImportsCache: FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache @@ -1638,10 +1698,11 @@ type internal TransparentCompiler ) : NodeCode = node { let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync + - match! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) with + match! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) |> NodeCode.AwaitAsync with | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return parseResult, checkResult - | parseResult, FSharpCheckFileAnswer.Aborted _ -> return parseResult, FSharpCheckFileResults.MakeEmpty(fileName, [||], true) + | parseResult, FSharpCheckFileAnswer.Aborted -> return parseResult, FSharpCheckFileResults.MakeEmpty(fileName, [||], true) } member this.GetBackgroundParseResultsForFileInProject @@ -1652,7 +1713,7 @@ type internal TransparentCompiler ) : NodeCode = node { let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! this.ParseFile(fileName, snapshot, userOpName) + return! this.ParseFile(fileName, snapshot, userOpName) |> NodeCode.AwaitAsync } member this.GetCachedCheckFileResult @@ -1669,9 +1730,10 @@ type internal TransparentCompiler FSharpProjectSnapshot.FromOptions(options, fileName, 1, sourceText) |> NodeCode.AwaitAsync - match! this.ParseAndCheckFileInProject(fileName, snapshot, "GetCachedCheckFileResult") with + + match! this.ParseAndCheckFileInProject(fileName, snapshot, "GetCachedCheckFileResult") |> NodeCode.AwaitAsync with | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return Some(parseResult, checkResult) - | _, FSharpCheckFileAnswer.Aborted _ -> return None + | _, FSharpCheckFileAnswer.Aborted -> return None } member this.GetProjectOptionsFromScript @@ -1703,8 +1765,10 @@ type internal TransparentCompiler ) member this.GetSemanticClassificationForFile(fileName: string, snapshot: FSharpProjectSnapshot, userOpName: string) = - ignore userOpName - ComputeSemanticClassification(fileName, snapshot) + node { + ignore userOpName + return! ComputeSemanticClassification(fileName, snapshot) |> NodeCode.AwaitAsync + } member this.GetSemanticClassificationForFile ( @@ -1715,7 +1779,7 @@ type internal TransparentCompiler node { ignore userOpName let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! ComputeSemanticClassification(fileName, snapshot) + return! ComputeSemanticClassification(fileName, snapshot) |> NodeCode.AwaitAsync } member this.InvalidateConfiguration(options: FSharpProjectOptions, userOpName: string) : unit = @@ -1740,7 +1804,7 @@ type internal TransparentCompiler FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync - return! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + return! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) |> NodeCode.AwaitAsync } member this.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = @@ -1750,15 +1814,19 @@ type internal TransparentCompiler node { ignore userOpName let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! ComputeParseAndCheckProject snapshot + return! ComputeParseAndCheckProject snapshot |> NodeCode.AwaitAsync } member this.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = - ignore userOpName - ComputeParseAndCheckProject projectSnapshot + node { + ignore userOpName + return! ComputeParseAndCheckProject projectSnapshot |> NodeCode.AwaitAsync + } member this.ParseFile(fileName, projectSnapshot, userOpName) = - this.ParseFile(fileName, projectSnapshot, userOpName) + node { + return! this.ParseFile(fileName, projectSnapshot, userOpName) |> NodeCode.AwaitAsync + } member this.ParseFile ( diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 4c07ece861b..d71b45475b9 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -272,6 +272,14 @@ type FSharpChecker member _.UsesTransparentCompiler = useTransparentCompiler = Some true + member _.TransparentCompiler = + match useTransparentCompiler with + | Some true -> backgroundCompiler :?> TransparentCompiler + | _ -> failwith "Transparent Compiler is not enabled." + + member this.Caches = + this.TransparentCompiler.Caches + member _.ReferenceResolver = legacyReferenceResolver member _.MatchBraces(fileName, sourceText: ISourceText, options: FSharpParsingOptions, ?userOpName: string) = @@ -444,9 +452,7 @@ type FSharpChecker member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" - backgroundCompiler.ParseAndCheckFileInProject(fileName, projectSnapshot, userOpName) - |> Async.AwaitNodeCode member _.ParseAndCheckProject(options: FSharpProjectOptions, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" @@ -623,8 +629,6 @@ type FSharpChecker member _.ProjectChecked = backgroundCompiler.ProjectChecked - member _.CacheEvent = backgroundCompiler.CacheEvent - static member ActualParseFileCount = BackgroundCompiler.ActualParseFileCount static member ActualCheckFileCount = BackgroundCompiler.ActualCheckFileCount diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index b1f893dbba7..c6bb4e2e4ab 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -6,8 +6,11 @@ namespace FSharp.Compiler.CodeAnalysis open System open System.IO +open System.Threading +open System.Threading.Tasks open FSharp.Compiler.AbstractIL.ILBinaryReader open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CodeAnalysis.TransparentCompiler open FSharp.Compiler.CompilerConfig open FSharp.Compiler.Diagnostics open FSharp.Compiler.EditorServices @@ -449,7 +452,9 @@ type public FSharpChecker = /// The event may be raised on a background thread. member ProjectChecked: IEvent - member internal CacheEvent: IEvent + member internal TransparentCompiler: TransparentCompiler + + member internal Caches: CompilerCaches [] static member Instance: FSharpChecker diff --git a/src/Compiler/TypedTree/TypedTreePickle.fs b/src/Compiler/TypedTree/TypedTreePickle.fs index 6a25d4cddbb..94c3548313a 100644 --- a/src/Compiler/TypedTree/TypedTreePickle.fs +++ b/src/Compiler/TypedTree/TypedTreePickle.fs @@ -762,6 +762,7 @@ let check (ilscope: ILScopeRef) (inMap: NodeInTable<_, _>) = let n = inMap.Get i if not (inMap.IsLinked n) then warning(Error(FSComp.SR.pickleMissingDefinition (i, inMap.Name, ilscope.QualifiedName), range0)) + failwith "It's broken" // Note for compiler developers: to get information about which item this index relates to, // enable the conditional in Pickle.p_osgn_ref to refer to the given index number and recompile // an identical copy of the source for the DLL containing the data being unpickled. A message will diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index b05f0c4ba54..925173ec5cb 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -1,7 +1,9 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. namespace FSharp.Compiler.Diagnostics + open System /// For activities following the dotnet distributed tracing concept diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncLock.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncLock.fs new file mode 100644 index 00000000000..ef4b69a3910 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncLock.fs @@ -0,0 +1,26 @@ +module CompilerService.AsyncLock + +open Internal.Utilities.Collections + +open Xunit +open System.Threading.Tasks + + +[] +let ``Async lock works`` () = + task { + use lock = new AsyncLock() + + let mutable x = 0 + + let job () = task { + let y = x + do! Task.Delay(10) + x <- y + 1 + } + + let jobs = [ for _ in 1..100 -> lock.Do job ] + let! _ = Task.WhenAll(jobs) + + Assert.Equal(100, x) + } \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 6a2ed957c2c..cd6f2c0da14 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -1,38 +1,63 @@ -module FSharp.Compiler.ComponentTests.CompilerService.AsyncMemoize +module CompilerService.AsyncMemoize open System open System.Threading open Xunit open FSharp.Test -open FSharp.Compiler.BuildGraph open Internal.Utilities.Collections open System.Threading.Tasks open System.Diagnostics open System.Collections.Concurrent + +[] +let ``Stack trace`` () = + + let memoize = AsyncMemoize() + + let computation key = async { + // do! Async.Sleep 1 |> NodeCode.AwaitAsync + + let! result = memoize.Get'(key * 2, async { + //do! Async.Sleep 1 |> NodeCode.AwaitAsync + return key * 5 + }) + + return result * 2 + } + + //let _r2 = computation 10 + + let result = memoize.Get'(1, computation 1) |> Async.RunSynchronously + + Assert.Equal(10, result) + + [] let ``Basics``() = - let computation key = node { - do! Async.Sleep 1 |> NodeCode.AwaitAsync + let computation key = async { + do! Async.Sleep 1 return key * 2 } - let eventLog = ResizeArray() + let eventLog = ConcurrentBag() - let memoize = AsyncMemoize(fun _ -> eventLog.Add) + let memoize = AsyncMemoize() + memoize.OnEvent(fun (e, (_label, k, _version)) -> eventLog.Add (e, k)) - let task = - NodeCode.Parallel(seq { - memoize.Get(5, computation 5) - memoize.Get(5, computation 5) - memoize.Get(2, computation 2) - memoize.Get(5, computation 5) - memoize.Get(3, computation 3) - memoize.Get(2, computation 2) - }) |> NodeCode.StartAsTask_ForTesting + let result = + seq { + memoize.Get'(5, computation 5) + memoize.Get'(5, computation 5) + memoize.Get'(2, computation 2) + memoize.Get'(5, computation 5) + memoize.Get'(3, computation 3) + memoize.Get'(2, computation 2) + } + |> Async.Parallel + |> Async.RunSynchronously - let result = task.Result let expected = [| 10; 10; 4; 10; 6; 4|] Assert.Equal(expected, result) @@ -40,86 +65,149 @@ let ``Basics``() = let groups = eventLog |> Seq.groupBy snd |> Seq.toList Assert.Equal(3, groups.Length) for key, events in groups do - Assert.Equal<(JobEventType * int) array>([| Started, key; Finished, key |], events |> Seq.toArray) + Assert.Equal>(Set [ Started, key; Finished, key ], Set events) [] let ``We can cancel a job`` () = + task { - let resetEvent = new ManualResetEvent(false) + let jobStarted = new ManualResetEvent(false) - let computation key = node { - resetEvent.Set() |> ignore - do! Async.Sleep 1000 |> NodeCode.AwaitAsync - failwith "Should be canceled before it gets here" - return key * 2 - } + let computation key = async { + jobStarted.Set() |> ignore + do! Async.Sleep 1000 + failwith "Should be canceled before it gets here" + return key * 2 + } - let eventLog = ResizeArray() - let memoize = AsyncMemoize(fun _ -> eventLog.Add) + let eventLog = ResizeArray() + let memoize = AsyncMemoize() + memoize.OnEvent(fun (e, (_label, k, _version)) -> eventLog.Add (e, k)) - use cts1 = new CancellationTokenSource() - use cts2 = new CancellationTokenSource() - use cts3 = new CancellationTokenSource() + use cts1 = new CancellationTokenSource() + use cts2 = new CancellationTokenSource() + use cts3 = new CancellationTokenSource() - let key = 1 + let key = 1 - let _task1 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation key), cts1.Token) - let _task2 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation key), cts2.Token) - let _task3 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation key), cts3.Token) + let _task1 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts1.Token) + let _task2 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts2.Token) + let _task3 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts3.Token) - resetEvent.WaitOne() |> ignore + jobStarted.WaitOne() |> ignore - Assert.Equal<(JobEventType * int) array>([| Started, key |], eventLog |> Seq.toArray ) + jobStarted.Reset() |> ignore - cts1.Cancel() - cts2.Cancel() + Assert.Equal<(JobEvent * int) array>([| Started, key |], eventLog |> Seq.toArray ) - Thread.Sleep 10 + cts1.Cancel() + cts2.Cancel() - Assert.Equal<(JobEventType * int) array>([| Started, key |], eventLog |> Seq.toArray ) + jobStarted.WaitOne() |> ignore - cts3.Cancel() + Assert.Equal<(JobEvent * int) array>([| Started, key; Started, key |], eventLog |> Seq.toArray ) - Thread.Sleep 100 + cts3.Cancel() - Assert.Equal<(JobEventType * int) array>([| Started, key; Canceled, key |], eventLog |> Seq.toArray ) + do! Task.Delay 100 - try - Task.WaitAll(_task1, _task2, _task3) - with :? AggregateException as ex -> - Assert.Equal(3, ex.InnerExceptions.Count) - Assert.True(ex.InnerExceptions |> Seq.forall (fun e -> e :? TaskCanceledException)) + Assert.Equal<(JobEvent * int) array>([| Started, key; Started, key; Canceled, key |], eventLog |> Seq.toArray ) + } [] -let ``Job keeps running even if first requestor cancels`` () = - let computation key = node { - do! Async.Sleep 100 |> NodeCode.AwaitAsync - return key * 2 +let ``Job is restarted if first requestor cancels`` () = + task { + let jobStarted = new ManualResetEvent(false) + + let computation key = async { + jobStarted.Set() |> ignore + + for _ in 1 .. 5 do + do! Async.Sleep 100 + + return key * 2 + } + + let eventLog = ConcurrentBag() + let memoize = AsyncMemoize() + memoize.OnEvent(fun (e, (_, k, _version)) -> eventLog.Add (DateTime.Now.Ticks, (e, k))) + + use cts1 = new CancellationTokenSource() + use cts2 = new CancellationTokenSource() + use cts3 = new CancellationTokenSource() + + let key = 1 + + let _task1 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts1.Token) + let _task2 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts2.Token) + let _task3 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts3.Token) + + + jobStarted.WaitOne() |> ignore + + cts1.Cancel() + + do! Task.Delay 100 + cts3.Cancel() + + let! result = _task2 + Assert.Equal(2, result) + + Assert.Equal(TaskStatus.Canceled, _task1.Status) + + let orderedLog = eventLog |> Seq.sortBy fst |> Seq.map snd |> Seq.toList + let expected = [ Started, key; Started, key; Finished, key ] + + Assert.Equal<_ list>(expected, orderedLog) } - let eventLog = ResizeArray() - let memoize = AsyncMemoize(fun _ -> eventLog.Add) +[] +let ``Job is restarted if first requestor cancels but keeps running if second requestor cancels`` () = + task { + let jobStarted = new ManualResetEvent(false) + + let computation key = async { + jobStarted.Set() |> ignore + + for _ in 1 .. 5 do + do! Async.Sleep 100 + + return key * 2 + } + + let eventLog = ConcurrentBag() + let memoize = AsyncMemoize() + memoize.OnEvent(fun (e, (_label, k, _version)) -> eventLog.Add (DateTime.Now.Ticks, (e, k))) - use cts1 = new CancellationTokenSource() - use cts2 = new CancellationTokenSource() - use cts3 = new CancellationTokenSource() + use cts1 = new CancellationTokenSource() + use cts2 = new CancellationTokenSource() + use cts3 = new CancellationTokenSource() - let key = 1 + let key = 1 - let _task1 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation key), cts1.Token) - let _task2 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation key), cts2.Token) - let _task3 = NodeCode.StartAsTask_ForTesting(memoize.Get(key, computation key), cts3.Token) + let _task1 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts1.Token) - Thread.Sleep 10 + jobStarted.WaitOne() |> ignore + jobStarted.Reset() |> ignore + let _task2 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts2.Token) + let _task3 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts3.Token) - cts1.Cancel() - cts3.Cancel() + cts1.Cancel() - let result = _task2.Result - Assert.Equal(2, result) + jobStarted.WaitOne() |> ignore - Thread.Sleep 1 // Wait for event log to be updated - Assert.Equal<(JobEventType * int) array>([| Started, key; Finished, key |], eventLog |> Seq.toArray ) + cts2.Cancel() + + let! result = _task3 + Assert.Equal(2, result) + + Assert.Equal(TaskStatus.Canceled, _task1.Status) + + let orderedLog = eventLog |> Seq.sortBy fst |> Seq.map snd |> Seq.toList + let expected = [ Started, key; Started, key; Finished, key ] + + Assert.Equal<_ list>(expected, orderedLog) + } type ExpectedException() = @@ -127,18 +215,23 @@ type ExpectedException() = [] let ``Stress test`` () = + let seed = System.Random().Next() let rng = System.Random seed - let threads = 100 - let iterations = 100 + let threads = 30 + let iterations = 30 let maxDuration = 100 - let maxTimeout = 100 + let minTimeout = 0 + let maxTimeout = 500 let exceptionProbability = 0.01 + let gcProbability = 0.1 let stepMs = 10 - let keyCount = 300 + let keyCount = rng.Next(5, 200) let keys = [| 1 .. keyCount |] + let testTimeoutMs = threads * iterations * maxDuration / 2 + let intenseComputation durationMs result = async { if rng.NextDouble() < exceptionProbability then @@ -147,31 +240,32 @@ let ``Stress test`` () = let mutable number = 0 while (int s.ElapsedMilliseconds) < durationMs do number <- number + 1 % 12345 - return result + return [result] } - |> NodeCode.AwaitAsync let rec sleepyComputation durationMs result = - node { + async { if rng.NextDouble() < (exceptionProbability / (float durationMs / float stepMs)) then raise (ExpectedException()) if durationMs > 0 then - do! Async.Sleep (min stepMs durationMs) |> NodeCode.AwaitAsync + do! Async.Sleep (min stepMs durationMs) return! sleepyComputation (durationMs - stepMs) result else - return result + return [result] } let rec mixedComputation durationMs result = - node { + async { if durationMs > 0 then if rng.NextDouble() < 0.5 then - do! intenseComputation (min stepMs durationMs) () + let! _ = intenseComputation (min stepMs durationMs) () + () else - do! sleepyComputation (min stepMs durationMs) () + let! _ = sleepyComputation (min stepMs durationMs) () + () return! mixedComputation (durationMs - stepMs) result else - return result + return [result] } let computations = [| @@ -180,56 +274,60 @@ let ``Stress test`` () = mixedComputation |] - let cacheEvents = ConcurrentBag() - - let cache = AsyncMemoize(fun _ x -> cacheEvents.Add x) + let cache = AsyncMemoize(keepStrongly=5, keepWeakly=10) let mutable started = 0 let mutable canceled = 0 + let mutable timeout = 0 let mutable failed = 0 let mutable completed = 0 - seq { - for _ in 1..threads do - let rec loop iteration = - async { - let computation = computations[rng.Next computations.Length] - let durationMs = rng.Next maxDuration - let timeoutMs = rng.Next maxTimeout - let key = keys[rng.Next keys.Length] - let result = key * 2 - let job = cache.Get(key, computation durationMs result) |> Async.AwaitNodeCode - let! runningJob = Async.StartChild(job, timeoutMs) - Interlocked.Increment &started |> ignore - try - let! actual = runningJob - Assert.Equal(result, actual) - Interlocked.Increment &completed |> ignore - with - | :? TaskCanceledException - | :? TimeoutException -> Interlocked.Increment &canceled |> ignore - | :? ExpectedException -> Interlocked.Increment &failed |> ignore - | :? AggregateException as ex when - ex.InnerExceptions |> Seq.exists (fun e -> e :? ExpectedException) -> - Interlocked.Increment &failed |> ignore - | e -> - failwith $"Seed {seed} failed on iteration {iteration}: %A{e}" - if iteration < iterations then - return! loop (iteration + 1) - return () - } - loop 1 - } - |> Async.Parallel - |> Async.RunSynchronously - |> ignore - - Assert.Equal (started, threads * iterations) - Assert.Equal (started, completed + canceled + failed) + let test = + seq { + for _ in 1..threads do + let rec loop iteration = + task { + if gcProbability > rng.NextDouble() then + GC.Collect(2, GCCollectionMode.Forced, false) + + let computation = computations[rng.Next computations.Length] + let durationMs = rng.Next maxDuration + let timeoutMs = rng.Next(minTimeout, maxTimeout) + let key = keys[rng.Next keys.Length] + let result = key * 2 + let job = cache.Get'(key, computation durationMs result) + let cts = new CancellationTokenSource() + let runningJob = Async.StartAsTask(job, cancellationToken = cts.Token) + cts.CancelAfter timeoutMs + Interlocked.Increment &started |> ignore + try + let! actual = runningJob + Assert.Equal(result, actual.Head) + Interlocked.Increment &completed |> ignore + with + | :? TaskCanceledException as _e -> + Interlocked.Increment &canceled |> ignore + | :? OperationCanceledException as _e -> + Interlocked.Increment &canceled |> ignore + | :? TimeoutException -> Interlocked.Increment &timeout |> ignore + | :? ExpectedException -> Interlocked.Increment &failed |> ignore + | :? AggregateException as ex when + ex.Flatten().InnerExceptions |> Seq.exists (fun e -> e :? ExpectedException) -> + Interlocked.Increment &failed |> ignore + | e -> + failwith $"Seed {seed} failed on iteration {iteration}: %A{e}" + if iteration < iterations then + return! loop (iteration + 1) + return () + } + loop 1 + } + |> Task.WhenAll - let events = - cacheEvents - |> Seq.countBy id - |> Seq.toArray + if not (test.Wait testTimeoutMs) then failwith "Test timed out - most likely deadlocked" + + Assert.Equal (threads * iterations, started) + // Assert.Equal((0,0,0,0,0),(started, completed, canceled, failed, timeout)) + Assert.Equal (started, completed + canceled + failed + timeout) - ignore events + Assert.True ((float completed) > ((float started) * 0.1), "Less than 10 % completed jobs") diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs new file mode 100644 index 00000000000..00bcd48d61d --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs @@ -0,0 +1,117 @@ +module CompilerService.LruCache +open Internal.Utilities.Collections + +open Xunit +open System +open System.Threading +open System.Runtime.CompilerServices + +[] +let ``Adding an item to the cache should make it retrievable``() = + let cache = new LruCache(keepStrongly = 2) + cache.Set(1, "one") + let result = cache.TryGet(1) + Assert.Equal("one", result.Value) + +[] +let ``Adding an item to the cache should evict the least recently used item if the cache is full``() = + let cache = new LruCache(keepStrongly = 2, keepWeakly = 0) + cache.Set(1, "one") + cache.Set(2, "two") + cache.Set(3, "three") + let result = cache.TryGet(1) + Assert.Null(result) + +[] +let ``Adding an item to the cache should not evict a required item``() = + let cache = new LruCache(keepStrongly = 2, requiredToKeep = (fun v -> v = "one")) + cache.Set(1, "one") + cache.Set(2, "two") + cache.Set(3, "three") + let result = cache.TryGet(1) + Assert.Equal("one", result.Value) + +[] +let ``Adding an item to the cache should not evict a strongly kept item``() = + let cache = new LruCache(keepStrongly = 2, keepWeakly = 0) + cache.Set(1, "one") + cache.Set(2, "two") + cache.Set(1, "one") + cache.Set(3, "three") + let result = cache.TryGet(1) + Assert.Equal("one", result.Value) + +[] +let ``Adding an item to the cache should not evict a strongly kept item, even if it is the least recently used``() = + let cache = new LruCache(keepStrongly = 2, keepWeakly = 0, requiredToKeep = (fun v -> v = "one")) + cache.Set(1, "one") + cache.Set(2, "two") + cache.Set(3, "three") + let result = cache.TryGet(1) + Assert.Equal("one", result.Value) + +[] +let ``Adding an item to the cache should not evict a weakly kept item if its reference is still valid``() = + let cache = new LruCache(keepStrongly = 2, keepWeakly = 1) + let value = "one" + cache.Set(1, value) + cache.Set(2, "two") + GC.Collect(2, GCCollectionMode.Forced, true) + let result = cache.TryGet(1) + Assert.Equal(value, result.Value) + + +// Doing this directly in the test prevents GC for some reason +let private addObjToCache (cache: LruCache<_, int,_>) key = + let o = obj () + cache.Set(key, o) + +[] +let ``Adding an item to the cache should evict a weakly kept item if its reference is no longer valid``() = + let cache = new LruCache<_, int, _>(keepStrongly = 2, keepWeakly = 1) + addObjToCache cache 1 + addObjToCache cache 2 + addObjToCache cache 3 + GC.Collect(2, GCCollectionMode.Forced, true) + + let result = cache.TryGet(1) + Assert.True(result.IsNone) + + +[] +let ``When a new version is added other versions get weakened`` () = + let eventLog = ResizeArray() + + let cache = new LruCache<_, int, _>(keepStrongly = 2, keepWeakly = 2, event = (fun e v -> eventLog.Add(e, v))) + + cache.Set(1, 1, "one1") + cache.Set(1, 2, "one2") + cache.Set(1, 3, "one3") + cache.Set(1, 4, "one4") + + let expected = [ + CacheEvent.Weakened, ("[no label]", 1, 1) + CacheEvent.Weakened, ("[no label]", 1, 2) + CacheEvent.Weakened, ("[no label]", 1, 3) + CacheEvent.Evicted, ("[no label]", 1, 1) + ] + + Assert.Equal>(expected, eventLog |> Seq.toList) + +[] +let ``When a new version is added other versions don't get weakened when they're required to keep`` () = + let eventLog = ResizeArray() + + let cache = new LruCache<_, int, _>(keepStrongly = 2, keepWeakly = 2, requiredToKeep = ((=) "one1"), event = (fun e v -> eventLog.Add(e, v))) + + cache.Set(1, 1, "one1") + cache.Set(1, 2, "one2") + cache.Set(1, 3, "one3") + cache.Set(1, 4, "one4") + + let expected = [ + CacheEvent.Weakened, ("[no label]", 1, 2) + CacheEvent.Weakened, ("[no label]", 1, 3) + ] + + Assert.Equal>(expected, eventLog |> Seq.toList) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/TaskAgent.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/TaskAgent.fs new file mode 100644 index 00000000000..8952e6dce1d --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/TaskAgent.fs @@ -0,0 +1,81 @@ +module CompilerService.TaskAgent +open Internal.Utilities.TaskAgent + +open Xunit +open System +open System.Threading.Tasks +open System.Runtime.CompilerServices +open System.Threading + + +[] +let ``TaskAgent should process messages that expect a reply`` () = + // Arrange + let agent = new TaskAgent<_,_,_>( + processMessage = (fun _ msg -> msg + 1), + processMessageNoReply = (fun _ _ -> ())) + + // Act + let replyTask = agent.PostAndAwaitReply(1) + + // Assert + Assert.Equal(2, replyTask.Result) + +[] +let ``TaskAgent should process messages that do not expect a reply`` () = + // Arrange + let mutable messageProcessed = false + + let agent = new TaskAgent<_,_,_>( + processMessage = (fun _ msg -> msg + 1), + processMessageNoReply = (fun _ _ -> messageProcessed <- true)) + + // Act + agent.Post(1) + agent.PostAndAwaitReply(1).Wait() + + // Assert + Assert.True(messageProcessed) + +[] +let ``TaskAgent should publish exceptions that occur while processing messages that do not expect a reply`` () = + // Arrange + let mutable exceptionPublished = false + + let agent = new TaskAgent<_,_,_>( + processMessage = (fun _ msg -> msg + 1), + processMessageNoReply = (fun _ _ -> failwith "Test exception")) + + use _ = agent.NoReplyExceptions.Subscribe(fun _ -> exceptionPublished <- true) + + // Act + agent.Post(1) + agent.PostAndAwaitReply(1).Wait() + + // Assert + Assert.True(exceptionPublished) + +[] +let ``TaskAgent should not deadlock under heavy use`` () = + // Arrange + use messagesProcessed = new ThreadLocal<_>((fun () -> ResizeArray()), true) + let agent = new TaskAgent<_,_,_>( + processMessage = (fun post msg -> + if msg > 0 then + post (msg + 1) + msg + 1), + processMessageNoReply = (fun _ msg -> + messagesProcessed.Value.Add(msg))) + let numMessages = 100000 + + // Act + let replyTasks = + [ for i in 1..numMessages do + yield agent.PostAndAwaitReply(i) ] + let replies = (replyTasks |> Task.WhenAll).Result + + // Assert + Assert.Equal(numMessages, replies.Length) + Assert.True(replies |> Seq.forall (fun r -> r > 0)) + agent.PostAndAwaitReply(0).Wait() + Assert.Equal(numMessages, messagesProcessed.Values |> Seq.sumBy (fun x -> x.Count)) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index e18a48c2b5c..12674fa0db9 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -24,7 +24,7 @@ FsUnit.fs - + @@ -107,7 +107,7 @@ - + @@ -170,7 +170,7 @@ - + @@ -186,7 +186,7 @@ - + @@ -255,7 +255,10 @@ - + + + + @@ -284,6 +287,7 @@ + @@ -318,5 +322,5 @@ - + diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs new file mode 100644 index 00000000000..20590a80e11 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs @@ -0,0 +1,66 @@ +module FSharpChecker.ProjectSnapshot + +open Xunit + +open FSharp.Compiler.CodeAnalysis + + +[] +let WithoutImplFilesThatHaveSignatures () = + + let snapshot: FSharpProjectSnapshot = { + ProjectFileName = Unchecked.defaultof<_> + ProjectId = Unchecked.defaultof<_> + SourceFiles = [ + { FileName = "A.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } + { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } + { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } + { FileName = "C.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } + { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } + ] + ReferencesOnDisk = Unchecked.defaultof<_> + OtherOptions = Unchecked.defaultof<_> + ReferencedProjects = Unchecked.defaultof<_> + IsIncompleteTypeCheckEnvironment = Unchecked.defaultof<_> + UseScriptResolutionRules = Unchecked.defaultof<_> + LoadTime = Unchecked.defaultof<_> + UnresolvedReferences = Unchecked.defaultof<_> + OriginalLoadReferences = Unchecked.defaultof<_> + Stamp = Unchecked.defaultof<_> + } + + let result = snapshot.WithoutImplFilesThatHaveSignatures + + let expected = [| "A.fsi"; "B.fs"; "C.fsi" |] + + Assert.Equal(expected, result.SourceFileNames |> List.toArray) + +[] +let WithoutImplFilesThatHaveSignaturesExceptLastOne () = + + let snapshot: FSharpProjectSnapshot = { + ProjectFileName = Unchecked.defaultof<_> + ProjectId = Unchecked.defaultof<_> + SourceFiles = [ + { FileName = "A.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } + { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } + { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } + { FileName = "C.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } + { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } + ] + ReferencesOnDisk = Unchecked.defaultof<_> + OtherOptions = Unchecked.defaultof<_> + ReferencedProjects = Unchecked.defaultof<_> + IsIncompleteTypeCheckEnvironment = Unchecked.defaultof<_> + UseScriptResolutionRules = Unchecked.defaultof<_> + LoadTime = Unchecked.defaultof<_> + UnresolvedReferences = Unchecked.defaultof<_> + OriginalLoadReferences = Unchecked.defaultof<_> + Stamp = Unchecked.defaultof<_> + } + + let result = snapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne + + let expected = [| "A.fsi"; "B.fs"; "C.fsi"; "C.fs" |] + + Assert.Equal(expected, result.SourceFileNames |> List.toArray) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index a503b6e51c6..856b5c25645 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -6,6 +6,7 @@ open FSharp.Compiler.CodeAnalysis open Internal.Utilities.Collections open FSharp.Compiler.CodeAnalysis.TransparentCompiler open Internal.Utilities.Library.Extras +open FSharp.Compiler.GraphChecking.GraphProcessing open Xunit @@ -14,6 +15,8 @@ open System.IO open Microsoft.CodeAnalysis open System open System.Threading.Tasks +open System.Threading +open TypeChecks [] @@ -74,10 +77,10 @@ let ``Parallel processing with signatures`` () = sourceFile "D" ["A"] |> addSignatureFile, sourceFile "E" ["B"; "C"; "D"] |> addSignatureFile) - let cacheEvents = ConcurrentBag<_>() + //let cacheEvents = ConcurrentBag<_>() ProjectWorkflowBuilder(project, useTransparentCompiler = true) { - withChecker (fun checker -> checker.CacheEvent.Add cacheEvents.Add) + //withChecker (fun checker -> checker.CacheEvent.Add cacheEvents.Add) checkFile "E" expectOk updateFile "A" updatePublicSurface checkFile "E" expectNoChanges @@ -214,7 +217,7 @@ let ``File is not checked twice`` () = withChecker (fun checker -> async { do! Async.Sleep 50 // wait for events from initial project check - checker.CacheEvent.Add cacheEvents.Add + checker.Caches.TcIntermediate.OnEvent cacheEvents.Add }) updateFile "First" updatePublicSurface checkFile "Third" expectOk @@ -222,15 +225,12 @@ let ``File is not checked twice`` () = let intermediateTypeChecks = cacheEvents - |> Seq.choose (function - | ("TcIntermediate", e, k) -> Some ((k :?> FSharpProjectSnapshotKey).LastFile |> fst |> Path.GetFileName, e) - | _ -> None) - |> Seq.groupBy fst - |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) + |> Seq.groupBy (fun (_e, (_l, (f, _p), _)) -> f |> Path.GetFileName) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) |> Map - Assert.Equal([Started; Finished], intermediateTypeChecks["FileFirst.fs"]) - Assert.Equal([Started; Finished], intermediateTypeChecks["FileThird.fs"]) + Assert.Equal([Weakened; Started; Finished], intermediateTypeChecks["FileFirst.fs"]) + Assert.Equal([Weakened; Started; Finished], intermediateTypeChecks["FileThird.fs"]) [] let ``We don't check files that are not depended on`` () = @@ -246,7 +246,7 @@ let ``We don't check files that are not depended on`` () = withChecker (fun checker -> async { do! Async.Sleep 50 // wait for events from initial project check - checker.CacheEvent.Add cacheEvents.Add + checker.Caches.TcIntermediate.OnEvent cacheEvents.Add }) updateFile "First" updatePublicSurface checkFile "Last" expectOk @@ -254,15 +254,12 @@ let ``We don't check files that are not depended on`` () = let intermediateTypeChecks = cacheEvents - |> Seq.choose (function - | ("TcIntermediate", e, k) -> Some ((k :?> FSharpProjectSnapshotKey).LastFile |> fst |> Path.GetFileName, e) - | _ -> None) - |> Seq.groupBy fst - |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) + |> Seq.groupBy (fun (_e, (_l, (f, _p), _)) -> Path.GetFileName f) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) |> Map - Assert.Equal([Started; Finished], intermediateTypeChecks["FileFirst.fs"]) - Assert.Equal([Started; Finished], intermediateTypeChecks["FileThird.fs"]) + Assert.Equal([Started; Finished], intermediateTypeChecks["FileFirst.fs"]) + Assert.Equal([Started; Finished], intermediateTypeChecks["FileThird.fs"]) Assert.False (intermediateTypeChecks.ContainsKey "FileSecond.fs") [] @@ -273,7 +270,8 @@ let ``Files that are not depended on don't invalidate cache`` () = sourceFile "Third" ["First"], sourceFile "Last" ["Third"]) - let cacheEvents = ResizeArray() + let cacheTcIntermediateEvents = ResizeArray() + let cacheGraphConstructionEvents = ResizeArray() ProjectWorkflowBuilder(project, useTransparentCompiler = true) { updateFile "First" updatePublicSurface @@ -281,33 +279,29 @@ let ``Files that are not depended on don't invalidate cache`` () = withChecker (fun checker -> async { do! Async.Sleep 50 // wait for events from initial project check - checker.CacheEvent.Add cacheEvents.Add + checker.Caches.TcIntermediate.OnEvent cacheTcIntermediateEvents.Add + checker.Caches.DependencyGraph.OnEvent cacheGraphConstructionEvents.Add + }) updateFile "Second" updatePublicSurface checkFile "Last" expectOk } |> ignore let intermediateTypeChecks = - cacheEvents - |> Seq.choose (function - | ("TcIntermediate", e, k) -> Some ((k :?> FSharpProjectSnapshotKey).LastFile |> fst |> Path.GetFileName, e) - | _ -> None) - |> Seq.groupBy fst - |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) + cacheTcIntermediateEvents + |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) |> Map let graphConstructions = - cacheEvents - |> Seq.choose (function - | ("DependencyGraph", e, k) -> Some ((k :?> (FSharpFileKey list * DependencyGraphType)) |> fst |> List.last |> fst |> Path.GetFileName, e) - | _ -> None) - |> Seq.groupBy fst - |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) + cacheGraphConstructionEvents + |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) |> Map - Assert.Equal([Started; Finished], graphConstructions["FileLast.fs"]) + Assert.Equal([Started; Finished], graphConstructions["FileLast.fs"]) - Assert.Equal([], intermediateTypeChecks |> Map.toList) + Assert.Equal([], intermediateTypeChecks |> Map.toList) [] let ``Files that are not depended on don't invalidate cache part 2`` () = @@ -318,7 +312,8 @@ let ``Files that are not depended on don't invalidate cache part 2`` () = sourceFile "D" ["B"; "C"], sourceFile "E" ["C"]) - let cacheEvents = ResizeArray() + let cacheTcIntermediateEvents = ResizeArray() + let cacheGraphConstructionEvents = ResizeArray() ProjectWorkflowBuilder(project, useTransparentCompiler = true) { updateFile "A" updatePublicSurface @@ -326,32 +321,85 @@ let ``Files that are not depended on don't invalidate cache part 2`` () = withChecker (fun checker -> async { do! Async.Sleep 50 // wait for events from initial project check - checker.CacheEvent.Add cacheEvents.Add + checker.Caches.TcIntermediate.OnEvent cacheTcIntermediateEvents.Add + checker.Caches.DependencyGraph.OnEvent cacheGraphConstructionEvents.Add }) updateFile "B" updatePublicSurface checkFile "E" expectOk } |> ignore let intermediateTypeChecks = - cacheEvents - |> Seq.choose (function - | ("TcIntermediate", e, k) -> Some ((k :?> FSharpProjectSnapshotKey).LastFile |> fst |> Path.GetFileName, e) - | _ -> None) - |> Seq.groupBy fst - |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) + cacheTcIntermediateEvents + |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) |> Seq.toList let graphConstructions = + cacheGraphConstructionEvents + |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Seq.toList + + Assert.Equal(["FileE.fs", [Started; Finished]], graphConstructions) + Assert.Equal(["FileE.fs", [Started; Finished]], intermediateTypeChecks) + +[] +let ``Changing impl files doesn't invalidate cache when they have signatures`` () = + let project = SyntheticProject.Create( + { sourceFile "A" [] with SignatureFile = AutoGenerated }, + { sourceFile "B" ["A"] with SignatureFile = AutoGenerated }, + { sourceFile "C" ["B"] with SignatureFile = AutoGenerated }) + + let cacheEvents = ResizeArray() + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + updateFile "A" updatePublicSurface + checkFile "C" expectOk + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.Caches.TcIntermediate.OnEvent cacheEvents.Add + }) + updateFile "A" updateInternal + checkFile "C" expectOk + } |> ignore + + let intermediateTypeChecks = cacheEvents - |> Seq.choose (function - | ("DependencyGraph", e, k) -> Some ((k :?> (FSharpFileKey list * DependencyGraphType)) |> fst |> List.last |> fst |> Path.GetFileName, e) - | _ -> None) - |> Seq.groupBy fst - |> Seq.map (fun (k, g) -> k, g |> Seq.map snd |> Seq.toList) + |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) |> Seq.toList - Assert.Equal(["FileE.fs", [Started; Finished]], graphConstructions) - Assert.Equal(["FileE.fs", [Started; Finished]], intermediateTypeChecks) + Assert.Equal([], intermediateTypeChecks) + +[] +let ``Changing impl file doesn't invalidate an in-memory referenced project`` () = + let library = SyntheticProject.Create("library", { sourceFile "A" [] with SignatureFile = AutoGenerated }) + + let project = { + SyntheticProject.Create("project", sourceFile "B" ["A"] ) + with DependsOn = [library] } + + let cacheEvents = ResizeArray() + + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + checkFile "B" expectOk + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.Caches.TcIntermediate.OnEvent cacheEvents.Add + }) + updateFile "A" updateInternal + checkFile "B" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheEvents + |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Seq.toList + + Assert.Equal([], intermediateTypeChecks) [] @@ -390,56 +438,30 @@ type ProjectAction = Get | Modify of (SyntheticProject -> SyntheticProject) type ProjectModificaiton = Update of int | Add | Remove type ProjectRequest = ProjectAction * AsyncReplyChannel +type FuzzingEvent = StartedChecking | FinishedChecking of bool | AbortedChecking of string | ModifiedImplFile | ModifiedSigFile + [] type SignatureFiles = Yes = 1 | No = 2 | Some = 3 -//[] -//[] -//[] -//[] -let Fuzzing signatureFiles = - let seed = System.Random().Next() - //let seed = 1093747864 - let rng = System.Random(int seed) +let fuzzingTest seed (project: SyntheticProject) = task { + let rng = System.Random seed - let fileCount = 30 - let maxDepsPerFile = 3 let checkingThreads = 20 - let maxModificationDelayMs = 50 - let maxCheckingDelayMs = 5 - let runTimeMs = 1000 + let maxModificationDelayMs = 10 + let maxCheckingDelayMs = 20 + //let runTimeMs = 30000 let signatureFileModificationProbability = 0.25 + let modificationLoopIterations = 10 + let checkingLoopIterations = 20 - let fileName i = sprintf $"F%03d{i}" - - //let extraCode = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ "src" ++ "Compiler" ++ "Utilities" ++ "EditDistance.fs" |> File.ReadAllLines |> Seq.skip 5 |> String.concat "\n" - let extraCode = "" - - let files = - [| for i in 1 .. fileCount do - let name = fileName i - let deps = [ - for _ in 1 .. maxDepsPerFile do - if i > 1 then - fileName <| rng.Next(1, i) ] - let signature = - match signatureFiles with - | SignatureFiles.Yes -> AutoGenerated - | SignatureFiles.Some when rng.NextDouble() < 0.5 -> AutoGenerated - | _ -> No - - { sourceFile name deps - with - SignatureFile = signature - ExtraSource = extraCode } - |] - - let initialProject = SyntheticProject.Create("TCFuzzing", files) + let minCheckingTimeoutMs = 0 + let maxCheckingTimeoutMs = 300 - let builder = ProjectWorkflowBuilder(initialProject, useTransparentCompiler = true, autoStart = false) + let builder = ProjectWorkflowBuilder(project, useTransparentCompiler = true, autoStart = false) let checker = builder.Checker - let initialProject = initialProject |> absorbAutoGeneratedSignatures checker |> Async.RunSynchronously + // Force creation and caching of options + do! SaveAndCheckProject project checker |> Async.Ignore let projectAgent = MailboxProcessor.Start(fun (inbox: MailboxProcessor) -> let rec loop project = @@ -447,15 +469,15 @@ let Fuzzing signatureFiles = let! action, reply = inbox.Receive() let! project = match action with - | Modify f -> async { + | Modify f -> async { let p = f project - do! saveProject p false checker + do! saveProject p false checker return p } | Get -> async.Return project reply.Reply project return! loop project } - loop initialProject) + loop project) let getProject () = projectAgent.PostAndAsyncReply(pair Get) @@ -467,8 +489,8 @@ let Fuzzing signatureFiles = Update 1, 80 Update 2, 5 Update 10, 5 - Add, 2 - Remove, 1 + //Add, 2 + //Remove, 1 ] let modificationPicker = [| @@ -481,27 +503,32 @@ let Fuzzing signatureFiles = let modifyImplFile f = { f with ExtraSource = f.ExtraSource |> addComment } let modifySigFile f = { f with SignatureFile = Custom (f.SignatureFile.CustomText |> addComment) } - let getRandomModification () = modificationPicker[rng.Next(0, modificationPicker.Length)] + let getRandomItem (xs: 'x array) = xs[rng.Next(0, xs.Length)] - let getRandomFile (project: SyntheticProject) = project.SourceFiles[rng.Next(0, project.SourceFiles.Length)].Id + let getRandomModification () = modificationPicker |> getRandomItem - let log = ConcurrentBag() + let getRandomFile (project: SyntheticProject) = project.GetAllFiles() |> List.toArray |> getRandomItem - let modificationLoop = async { - while true do - do! Async.Sleep (rng.Next maxModificationDelayMs) + let log = new ThreadLocal<_>((fun () -> ResizeArray<_>()), true) + + let exceptions = ConcurrentBag() + + let modificationLoop _ = task { + for _ in 1 .. modificationLoopIterations do + do! Task.Delay (rng.Next maxModificationDelayMs) let modify project = match getRandomModification() with | Update n -> - let files = Set [ for _ in 1..n -> getRandomFile project ] + let files = Set [ for _ in 1..n -> getRandomFile project |> snd ] (project, files) - ||> Seq.fold (fun p fileId -> + ||> Seq.fold (fun p file -> + let fileId = file.Id let project, file = project.FindInAllProjects fileId - let opName, f = - if file.HasSignatureFile && rng.NextDouble() < signatureFileModificationProbability - then nameof modifySigFile, modifySigFile - else nameof modifyImplFile, modifyImplFile - log.Add $"{project.Name} -> {fileId} |> {opName}" + let opName, f = + if file.HasSignatureFile && rng.NextDouble() < signatureFileModificationProbability + then ModifiedSigFile, modifySigFile + else ModifiedImplFile, modifyImplFile + log.Value.Add (DateTime.Now.Ticks, opName, $"{project.Name} / {fileId}") p |> updateFileInAnyProject fileId f) | Add | Remove -> @@ -509,82 +536,158 @@ let Fuzzing signatureFiles = project do! modifyProject modify } - - let checkingLoop = async { - while true do - let! project = getProject() - let file = project |> getRandomFile - // TODO: timeout & cancelation - log.Add $"Started checking {file}" - let! result = checker |> checkFile file project - - log.Add $"Checked {file} %A{snd result}" - expectOk result () + let checkingLoop n _ = task { + for _ in 1 .. checkingLoopIterations do + let! project = getProject() + let p, file = project |> getRandomFile - do! Async.Sleep (rng.Next maxCheckingDelayMs) + let timeout = rng.Next(minCheckingTimeoutMs, maxCheckingTimeoutMs) + + log.Value.Add (DateTime.Now.Ticks, StartedChecking, $"Loop #{n} {file.Id} ({timeout} ms timeout)") + let ct = new CancellationTokenSource() + ct.CancelAfter(timeout) + let job = Async.StartAsTask(checker |> checkFile file.Id p, cancellationToken = ct.Token) + try + let! parseResult, checkResult = job + log.Value.Add (DateTime.Now.Ticks, FinishedChecking (match checkResult with FSharpCheckFileAnswer.Succeeded _ -> true | _ -> false), $"Loop #{n} {file.Id}") + expectOk (parseResult, checkResult) () + with ex -> + let message = + match ex with + | :? AggregateException as e -> + match e.InnerException with + | :? GraphProcessingException as e -> $"GPE: {e.InnerException.Message}" + | _ -> e.Message + | _ -> ex.Message + log.Value.Add (DateTime.Now.Ticks, AbortedChecking (message), $"Loop #{n} {file.Id} %A{ex}") + if ex.Message <> "A task was canceled." then exceptions.Add ex + + do! Task.Delay (rng.Next maxCheckingDelayMs) } - async { - let! threads = - seq { - //Async.StartChild(modificationLoop, runTimeMs) - ignore modificationLoop - for _ in 1..checkingThreads do - Async.StartChild(checkingLoop, runTimeMs) + do! task { + let threads = + seq { + modificationLoop CancellationToken.None + // ignore modificationLoop + for n in 1..checkingThreads do + checkingLoop n CancellationToken.None } - |> Async.Parallel - try - do! threads |> Async.Parallel |> Async.Ignore - with - | :? TimeoutException - | :? TaskCanceledException -> () - | :? AggregateException as e when e.InnerExceptions |> Seq.exists (fun e -> e :? TaskCanceledException) -> () - | e -> failwith $"Seed: {seed}\nException: %A{e}" - } |> Async.RunSynchronously + + try + let! _x = threads |> Seq.skip 1 |> Task.WhenAll + () + with + | e -> + let _log = log.Values |> Seq.collect id |> Seq.sortBy p13 |> Seq.toArray + failwith $"Seed: {seed}\nException: %A{e}" + } + let log = log.Values |> Seq.collect id |> Seq.sortBy p13 |> Seq.toArray + + let _stats = log |> Array.groupBy (p23) |> Array.map (fun (op, xs) -> op, xs.Length) |> Map + + let _errors = _stats |> Map.toSeq |> Seq.filter (fst >> function AbortedChecking ex when ex <> "A task was canceled." -> true | _ -> false) |> Seq.toArray + + let _exceptions = exceptions + + Assert.Equal>([||], _errors) + + //Assert.Equal>(Map.empty, _stats) builder.DeleteProjectDir() +} -//[] -//[] -//[] -let GiraffeFuzzing signatureFiles = - let seed = System.Random().Next() - //let seed = 1514219769 +[] +[] +[] +[] +let Fuzzing signatureFiles = + + let seed = 1106087513 let rng = System.Random(int seed) - let getRandomItem (xs: 'x array) = xs[rng.Next(0, xs.Length)] + let fileCount = 30 + let maxDepsPerFile = 3 - let checkingThreads = 16 - let maxModificationDelayMs = 20 - let maxCheckingDelayMs = 40 - //let runTimeMs = 30000 + let fileName i = sprintf $"F%03d{i}" + + //let extraCode = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ "src" ++ "Compiler" ++ "Utilities" ++ "EditDistance.fs" |> File.ReadAllLines |> Seq.skip 5 |> String.concat "\n" + let extraCode = "" + + let files = + [| for i in 1 .. fileCount do + let name = fileName i + let deps = [ + for _ in 1 .. maxDepsPerFile do + if i > 1 then + fileName <| rng.Next(1, i) ] + let signature = + match signatureFiles with + | SignatureFiles.Yes -> AutoGenerated + | SignatureFiles.Some when rng.NextDouble() < 0.5 -> AutoGenerated + | _ -> No + + { sourceFile name deps + with + SignatureFile = signature + ExtraSource = extraCode } + |] + + let initialProject = SyntheticProject.Create("TCFuzzing", files) + + let builder = ProjectWorkflowBuilder(initialProject, useTransparentCompiler = true, autoStart = false) + let checker = builder.Checker + + let initialProject = initialProject |> absorbAutoGeneratedSignatures checker |> Async.RunSynchronously + + fuzzingTest seed initialProject + + +let Fuzzing' signatureFiles = + //let seed = System.Random().Next() + let seed = 1106087513 + let rng = System.Random(int seed) + + let fileCount = 30 + let maxDepsPerFile = 3 + let checkingThreads = 20 + let maxModificationDelayMs = 50 + let maxCheckingDelayMs = 5 + let runTimeMs = 1000 let signatureFileModificationProbability = 0.25 - let modificationLoopIterations = 100 - let checkingLoopIterations = 50 - let giraffe = if signatureFiles then "giraffe-signatures" else "Giraffe" - let giraffeDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ giraffe ++ "src" ++ "Giraffe" - let giraffeTestsDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ giraffe ++ "tests" ++ "Giraffe.Tests" + let fileName i = sprintf $"F%03d{i}" - let giraffeProject = SyntheticProject.CreateFromRealProject giraffeDir - let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } + //let extraCode = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ "src" ++ "Compiler" ++ "Utilities" ++ "EditDistance.fs" |> File.ReadAllLines |> Seq.skip 5 |> String.concat "\n" + let extraCode = "" - let testsProject = SyntheticProject.CreateFromRealProject giraffeTestsDir - let testsProject = - { testsProject - with - OtherOptions = "--nowarn:FS3520"::testsProject.OtherOptions - DependsOn = [ giraffeProject ] - NugetReferences = giraffeProject.NugetReferences @ testsProject.NugetReferences - } + let files = + [| for i in 1 .. fileCount do + let name = fileName i + let deps = [ + for _ in 1 .. maxDepsPerFile do + if i > 1 then + fileName <| rng.Next(1, i) ] + let signature = + match signatureFiles with + | SignatureFiles.Yes -> AutoGenerated + | SignatureFiles.Some when rng.NextDouble() < 0.5 -> AutoGenerated + | _ -> No - let builder = ProjectWorkflowBuilder(testsProject, useTransparentCompiler = true, autoStart = false) + { sourceFile name deps + with + SignatureFile = signature + ExtraSource = extraCode } + |] + + let initialProject = SyntheticProject.Create("TCFuzzing", files) + + let builder = ProjectWorkflowBuilder(initialProject, useTransparentCompiler = true, autoStart = false) let checker = builder.Checker - // Force creation and caching of options - SaveAndCheckProject testsProject checker |> Async.Ignore |> Async.RunSynchronously + let initialProject = initialProject |> absorbAutoGeneratedSignatures checker |> Async.RunSynchronously let projectAgent = MailboxProcessor.Start(fun (inbox: MailboxProcessor) -> let rec loop project = @@ -600,7 +703,7 @@ let GiraffeFuzzing signatureFiles = reply.Reply project return! loop project } - loop testsProject) + loop initialProject) let getProject () = projectAgent.PostAndAsyncReply(pair Get) @@ -612,8 +715,8 @@ let GiraffeFuzzing signatureFiles = Update 1, 80 Update 2, 5 Update 10, 5 - //Add, 2 - //Remove, 1 + Add, 2 + Remove, 1 ] let modificationPicker = [| @@ -626,28 +729,27 @@ let GiraffeFuzzing signatureFiles = let modifyImplFile f = { f with ExtraSource = f.ExtraSource |> addComment } let modifySigFile f = { f with SignatureFile = Custom (f.SignatureFile.CustomText |> addComment) } - let getRandomModification () = modificationPicker |> getRandomItem + let getRandomModification () = modificationPicker[rng.Next(0, modificationPicker.Length)] - let getRandomFile (project: SyntheticProject) = project.GetAllFiles() |> List.toArray |> getRandomItem + let getRandomFile (project: SyntheticProject) = project.SourceFiles[rng.Next(0, project.SourceFiles.Length)].Id let log = ConcurrentBag() let modificationLoop = async { - for _ in 1 .. modificationLoopIterations do + while true do do! Async.Sleep (rng.Next maxModificationDelayMs) let modify project = match getRandomModification() with | Update n -> - let files = Set [ for _ in 1..n -> getRandomFile project |> snd ] + let files = Set [ for _ in 1..n -> getRandomFile project ] (project, files) - ||> Seq.fold (fun p file -> - let fileId = file.Id + ||> Seq.fold (fun p fileId -> let project, file = project.FindInAllProjects fileId let opName, f = if file.HasSignatureFile && rng.NextDouble() < signatureFileModificationProbability then nameof modifySigFile, modifySigFile else nameof modifyImplFile, modifyImplFile - log.Add $"{DateTime.Now.ToShortTimeString()}| {project.Name} -> {fileId} |> {opName}" + log.Add $"{project.Name} -> {fileId} |> {opName}" p |> updateFileInAnyProject fileId f) | Add | Remove -> @@ -656,34 +758,64 @@ let GiraffeFuzzing signatureFiles = do! modifyProject modify } - let checkingLoop n = async { - for _ in 1 .. checkingLoopIterations do + let checkingLoop = async { + while true do let! project = getProject() - let p, file = project |> getRandomFile + let file = project |> getRandomFile // TODO: timeout & cancelation - log.Add $"{DateTime.Now.ToShortTimeString()}| #{n} Started checking {file.Id}" - let! result = checker |> checkFile file.Id p - - log.Add $"{DateTime.Now.ToShortTimeString()}| #{n} Checked {file.Id} %A{snd result}" + log.Add $"Started checking {file}" + let! result = checker |> checkFile file project + + log.Add $"Checked {file} %A{snd result}" expectOk result () do! Async.Sleep (rng.Next maxCheckingDelayMs) } async { - let! threads = - seq { - Async.StartChild(modificationLoop) + let! threads = + seq { + Async.StartChild(modificationLoop, runTimeMs) ignore modificationLoop - for n in 1..checkingThreads do - Async.StartChild(checkingLoop n) + for _ in 1..checkingThreads do + Async.StartChild(checkingLoop, runTimeMs) } - |> Async.Parallel - try + |> Async.Parallel + try do! threads |> Seq.skip 1 |> Async.Parallel |> Async.Ignore - with + with + | :? TimeoutException + | :? TaskCanceledException -> () + | :? AggregateException as e when e.InnerExceptions |> Seq.exists (fun e -> e :? TaskCanceledException) -> () | e -> failwith $"Seed: {seed}\nException: %A{e}" } |> Async.RunSynchronously builder.DeleteProjectDir() + + +[] +[] +[] +let GiraffeFuzzing signatureFiles = + let seed = System.Random().Next() + //let seed = 1044159179 + + let giraffe = if signatureFiles then "giraffe-signatures" else "Giraffe" + let giraffeDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ giraffe ++ "src" ++ "Giraffe" + let giraffeTestsDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ giraffe ++ "tests" ++ "Giraffe.Tests" + + let giraffeProject = SyntheticProject.CreateFromRealProject giraffeDir + let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } + + let testsProject = SyntheticProject.CreateFromRealProject giraffeTestsDir + let testsProject = + { testsProject + with + OtherOptions = "--nowarn:FS3520"::testsProject.OtherOptions + DependsOn = [ giraffeProject ] + NugetReferences = giraffeProject.NugetReferences @ testsProject.NugetReferences + } + + fuzzingTest seed testsProject + diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs index b71add124ca..8344be03309 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs @@ -65,7 +65,7 @@ let ``See what this does`` () = let filePairs = FilePairMap(sourceFiles) let fullGraph = - DependencyResolution.mkGraph false filePairs sourceFiles + DependencyResolution.mkGraph filePairs sourceFiles |> fst //|> Graph.map (fun idx -> project.SourceFilePaths[idx] |> Path.GetFileName) diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index deaa57fec5b..ec20bc6e8f8 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -41,7 +41,7 @@ let private projectRoot = "test-projects" let private defaultFunctionName = "f" type Reference = { - Name: string + Name: string Version: string option } module ReferenceHelpers = @@ -68,13 +68,13 @@ module ReferenceHelpers = } |> String.concat "\n" - let runtimeList = lazy ( + let runtimeList = lazy ( // You can see which versions of the .NET runtime are currently installed with the following command. let psi = ProcessStartInfo("dotnet", "--list-runtimes", RedirectStandardOutput = true, UseShellExecute = false) let proc = Process.Start(psi) - proc.WaitForExit() + proc.WaitForExit(1000) |> ignore let output = seq { @@ -92,7 +92,8 @@ module ReferenceHelpers = { Name = matches.Groups.[1].Value Version = version - Path = DirectoryInfo(Path.Combine(matches.Groups[3].Value, version)) })) + Path = DirectoryInfo(Path.Combine(matches.Groups[3].Value, version)) }) + |> Seq.toList) let getFrameworkReference (reference: Reference) = @@ -554,7 +555,7 @@ module ProjectOperations = use md5 = System.Security.Cryptography.MD5.Create() let inputBytes = Encoding.UTF8.GetBytes(source.ToString()) let hash = md5.ComputeHash(inputBytes) |> Array.map (fun b -> b.ToString("X2")) |> String.concat "" - + return { FileName = filePath Version = hash diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index e48d68b1476..cea2a5cc016 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -4,7 +4,6 @@ module internal Microsoft.VisualStudio.FSharp.Editor.WorkspaceExtensions open System open System.Diagnostics open System.Runtime.CompilerServices -open System.Threading open System.Threading.Tasks open Microsoft.CodeAnalysis @@ -13,7 +12,6 @@ open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols -open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks open CancellableTasks open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks @@ -101,7 +99,7 @@ module private CheckerExtensions = options: FSharpProjectOptions, userOpName: string ) = - async { + cancellableTask { let! projectSnapshot = getProjectSnapshot (document, options) let! (parseResults, checkFileAnswer) = checker.ParseAndCheckFileInProject(document.FilePath, projectSnapshot, userOpName) @@ -317,7 +315,6 @@ type Document with member this.GetFSharpSemanticClassificationAsync(userOpName) = cancellableTask { let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync(userOpName) - let! result = checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectOptions) let! result = if this.Project.UseTransparentCompiler then @@ -431,7 +428,7 @@ type Project with |> CancellableTask.whenAll else for doc in documents do - do! doc.FindFSharpReferencesAsync(symbol, (fun range -> onFound doc range), userOpName) + do! doc.FindFSharpReferencesAsync(symbol, (onFound doc), userOpName) } member this.GetFSharpCompilationOptionsAsync() = From 761284301e310558506d704ceb4ccf3ae7e1140f Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 4 Sep 2023 17:12:40 +0200 Subject: [PATCH 078/222] cleanup --- src/Compiler/Driver/GraphChecking/Graph.fs | 42 -- src/Compiler/Driver/GraphChecking/Graph.fsi | 2 - .../Driver/GraphChecking/GraphProcessing.fs | 16 +- src/Compiler/Facilities/AsyncMemoize.fs | 403 +++++++++++------- src/Compiler/Facilities/TaskAgent.fs | 71 +-- src/Compiler/Service/BackgroundCompiler.fs | 11 +- src/Compiler/Service/FSharpCheckerResults.fs | 65 ++- src/Compiler/Service/FSharpCheckerResults.fsi | 7 +- src/Compiler/Service/IncrementalBuild.fsi | 2 +- src/Compiler/Service/TransparentCompiler.fs | 186 +++++--- src/Compiler/Service/service.fs | 7 +- src/Compiler/Utilities/Activity.fsi | 4 +- .../FSharp.Compiler.ComponentTests.fsproj | 1 - .../TypeChecks/Graph/GraphOperations.fs | 84 ---- 14 files changed, 470 insertions(+), 431 deletions(-) delete mode 100644 tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs diff --git a/src/Compiler/Driver/GraphChecking/Graph.fs b/src/Compiler/Driver/GraphChecking/Graph.fs index 84d96422811..03047b2ec4d 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fs +++ b/src/Compiler/Driver/GraphChecking/Graph.fs @@ -75,48 +75,6 @@ module internal Graph = |> readOnlyDict |> addIfMissing originalGraph.Keys - /// Returns leaves of the graph and the remaining graph without the leaves. - let cutLeaves (graph: Graph<'Node>) = - let notLeaves = - set - [ - for (KeyValue (node, deps)) in graph do - if deps.Length > 0 then - node - ] - - let leaves = - set - [ - for (KeyValue (node, deps)) in graph do - if deps.Length = 0 then - node - - yield! deps |> Array.filter (notLeaves.Contains >> not) - ] - - leaves, - seq { - for (KeyValue (node, deps)) in graph do - if deps.Length > 0 then - node, deps |> Array.filter (leaves.Contains >> not) - } - |> make - - /// Returns layers of leaves repeatedly removed from the graph until there's nothing left - let leafSequence (graph: Graph<'Node>) = - let rec loop (graph: Graph<'Node>) acc = - match graph |> cutLeaves with - | leaves, _ when leaves.IsEmpty -> acc - | leaves, graph -> - seq { - yield! acc - leaves - } - |> loop graph - - loop graph Seq.empty - let printCustom (graph: Graph<'Node>) (nodePrinter: 'Node -> string) : unit = printfn "Graph:" let join (xs: string[]) = System.String.Join(", ", xs) diff --git a/src/Compiler/Driver/GraphChecking/Graph.fsi b/src/Compiler/Driver/GraphChecking/Graph.fsi index f92a2a58468..a93e429d2fe 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fsi +++ b/src/Compiler/Driver/GraphChecking/Graph.fsi @@ -19,8 +19,6 @@ module internal Graph = val subGraphFor: node: 'Node -> graph: Graph<'Node> -> Graph<'Node> when 'Node: equality /// Create a reverse of the graph. val reverse<'Node when 'Node: equality> : originalGraph: Graph<'Node> -> Graph<'Node> - /// Returns layers of leaves repeatedly removed from the graph until there's nothing left - val leafSequence: graph: Graph<'Node> -> Set<'Node> seq /// Print the contents of the graph to the standard output. val print: graph: Graph<'Node> -> unit /// Create a simple Mermaid graph diff --git a/src/Compiler/Driver/GraphChecking/GraphProcessing.fs b/src/Compiler/Driver/GraphChecking/GraphProcessing.fs index 53a2a1a95c6..29af88bc64e 100644 --- a/src/Compiler/Driver/GraphChecking/GraphProcessing.fs +++ b/src/Compiler/Driver/GraphChecking/GraphProcessing.fs @@ -232,11 +232,13 @@ let processGraphAsync<'Item, 'Result when 'Item: equality and 'Item: comparison> let raiseExn (item, ex: exn) = localCts.Cancel() - match ex with - | :? OperationCanceledException -> - completionSignal.TrySetCanceled() - | _ -> - completionSignal.TrySetException(GraphProcessingException($"[*] Encountered exception when processing item '{item}': {ex.Message}", ex)) + + match ex with + | :? OperationCanceledException -> completionSignal.TrySetCanceled() + | _ -> + completionSignal.TrySetException( + GraphProcessingException($"[*] Encountered exception when processing item '{item}': {ex.Message}", ex) + ) |> ignore let incrementProcessedNodesCount () = @@ -280,8 +282,8 @@ let processGraphAsync<'Item, 'Result when 'Item: equality and 'Item: comparison> leaves |> Array.iter queueNode // Wait for end of processing, an exception, or an external cancellation request. - do! completionSignal.Task |> Async.AwaitTask - + do! completionSignal.Task |> Async.AwaitTask + // All calculations succeeded - extract the results and sort in input order. return nodes.Values diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 9568465ad2c..8f785b459d4 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -7,16 +7,20 @@ open System.IO open System.Threading open System.Threading.Tasks - [] module Utils = - + /// Return file name with one directory above it - let shortPath path = - let dirPath = Path.GetDirectoryName path - let dir = dirPath.Split Path.DirectorySeparatorChar |> Array.tryLast |> Option.map (sprintf "%s/") |> Option.defaultValue "" + let shortPath path = + let dirPath = Path.GetDirectoryName path + + let dir = + dirPath.Split Path.DirectorySeparatorChar + |> Array.tryLast + |> Option.map (sprintf "%s/") + |> Option.defaultValue "" + $"{dir}{Path.GetFileName path}" - type internal StateUpdate<'TValue> = | CancelRequest @@ -58,7 +62,13 @@ type internal ValueLink<'T when 'T: not struct> = | Weak of WeakReference<'T> [] -type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality and 'TValue: not struct>(keepStrongly, ?keepWeakly, ?requiredToKeep, ?event) = +type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality and 'TValue: not struct> + ( + keepStrongly, + ?keepWeakly, + ?requiredToKeep, + ?event + ) = let keepWeakly = defaultArg keepWeakly 100 let requiredToKeep = defaultArg requiredToKeep (fun _ -> false) @@ -73,62 +83,72 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers let rec removeCollected (node: LinkedListNode<_>) = if node <> null then let key, version, label, value = node.Value + match value with | Weak w -> let next = node.Next + match w.TryGetTarget() with | false, _ -> weakList.Remove node - dictionary[key].Remove version |> ignore + dictionary[ key ].Remove version |> ignore + if dictionary[key].Count = 0 then dictionary.Remove key |> ignore + event CacheEvent.Collected (label, key, version) | _ -> () + removeCollected next - | _ -> - failwith "Illegal state, strong reference in weak list" + | _ -> failwith "Illegal state, strong reference in weak list" - let cutWeakListIfTooLong() = + let cutWeakListIfTooLong () = if weakList.Count > keepWeakly then removeCollected weakList.First let mutable node = weakList.Last + while weakList.Count > keepWeakly && node <> null do let previous = node.Previous let key, version, label, _ = node.Value weakList.Remove node - dictionary[key].Remove version |> ignore + dictionary[ key ].Remove version |> ignore + if dictionary[key].Count = 0 then dictionary.Remove key |> ignore + event CacheEvent.Evicted (label, key, version) node <- previous - let cutStrongListIfTooLong() = + let cutStrongListIfTooLong () = let mutable node = strongList.Last + while strongList.Count > keepStrongly && node <> null do let previous = node.Previous + match node.Value with | _, _, _, Strong v when requiredToKeep v -> () | key, version, label, Strong v -> strongList.Remove node - node.Value <- key, version, label, Weak (WeakReference<_> v) + node.Value <- key, version, label, Weak(WeakReference<_> v) weakList.AddFirst node event CacheEvent.Weakened (label, key, version) | _key, _version, _label, _ -> failwith "Invalid state, weak reference in strong list" + node <- previous - cutWeakListIfTooLong() + + cutWeakListIfTooLong () let pushNodeToTop (node: LinkedListNode<_>) = match node.Value with | _, _, _, Strong _ -> strongList.AddFirst node - cutStrongListIfTooLong() - | _, _, _, Weak _ -> - failwith "Invalid operation, pushing weak reference to strong list" + cutStrongListIfTooLong () + | _, _, _, Weak _ -> failwith "Invalid operation, pushing weak reference to strong list" let pushValueToTop key version label value = - let node = strongList.AddFirst(value=(key, version, label, Strong value)) - cutStrongListIfTooLong() + let node = strongList.AddFirst(value = (key, version, label, Strong value)) + cutStrongListIfTooLong () node member _.DebuggerDisplay = $"Cache(S:{strongList.Count} W:{weakList.Count})" @@ -142,6 +162,7 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers // failwith "Suspicious - overwriting existing version" let node: LinkedListNode<_> = versionDict[version] + match node.Value with | _, _, _, Strong _ -> strongList.Remove node | _, _, _, Weak _ -> @@ -158,13 +179,14 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers for otherVersion in versionDict.Keys do if otherVersion <> version then let node = versionDict[otherVersion] + match node.Value with | _, _, _, Strong value when not (requiredToKeep value) -> strongList.Remove node - node.Value <- key, otherVersion, label, Weak (WeakReference<_> value) + node.Value <- key, otherVersion, label, Weak(WeakReference<_> value) weakList.AddFirst node event CacheEvent.Weakened (label, key, otherVersion) - cutWeakListIfTooLong() + cutWeakListIfTooLong () | _ -> () | false, _ -> @@ -172,7 +194,8 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers dictionary[key] <- Dictionary() dictionary[key][version] <- node - member this.Set(key, version, value) = this.Set(key, version, "[no label]", value) + member this.Set(key, version, value) = + this.Set(key, version, "[no label]", value) member _.TryGet(key, version) = @@ -199,8 +222,10 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers | _ -> weakList.Remove node versionDict.Remove version |> ignore + if versionDict.Count = 0 then dictionary.Remove key |> ignore + event CacheEvent.Collected (label, key, version) None @@ -211,8 +236,10 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers match versionDict.TryGetValue version with | true, node -> versionDict.Remove version |> ignore + if versionDict.Count = 0 then dictionary.Remove key |> ignore + match node.Value with | _, _, _, Strong _ -> strongList.Remove node | _, _, _, Weak _ -> weakList.Remove node @@ -227,13 +254,13 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers member this.Remove(key) = this.Remove(key, Unchecked.defaultof<_>) - member _.GetValues() = - strongList + member _.GetValues() = + strongList |> Seq.append weakList - |> Seq.choose (function - | _, _, _, Strong v -> Some v - | _, _, _, Weak w -> - match w.TryGetTarget() with + |> Seq.choose (function + | _, _, _, Strong v -> Some v + | _, _, _, Weak w -> + match w.TryGetTarget() with | true, v -> Some v | _ -> None) @@ -243,12 +270,17 @@ type internal ICacheKey<'TKey, 'TVersion> = abstract member GetLabel: unit -> string type private KeyData<'TKey, 'TVersion> = - { Label: string; Key: 'TKey; Version: 'TVersion } + { + Label: string + Key: 'TKey + Version: 'TVersion + } /// Tools for hashing things with MD5 into a string that can be used as a cache key. module internal Md5Hasher = - let private md5 = new ThreadLocal<_>(fun () -> System.Security.Cryptography.MD5.Create()) + let private md5 = + new ThreadLocal<_>(fun () -> System.Security.Cryptography.MD5.Create()) let private computeHash (bytes: byte array) = md5.Value.ComputeHash(bytes) @@ -256,7 +288,11 @@ module internal Md5Hasher = let addBytes (bytes: byte array) (s: string) = let sbytes = System.Text.Encoding.UTF8.GetBytes(s) - Array.append sbytes bytes |> computeHash |> System.BitConverter.ToString |> (fun x -> x.Replace("-", "")) + + Array.append sbytes bytes + |> computeHash + |> System.BitConverter.ToString + |> (fun x -> x.Replace("-", "")) let addString (s: string) (s2: string) = s |> System.Text.Encoding.UTF8.GetBytes |> addBytes <| s2 @@ -272,8 +308,7 @@ module internal Md5Hasher = let addBool (b: bool) (s: string) = b |> BitConverter.GetBytes |> addBytes <| s - let addDateTime (dt: System.DateTime) (s: string) = - dt.Ticks.ToString() |> addString <| s + let addDateTime (dt: System.DateTime) (s: string) = dt.Ticks.ToString() |> addString <| s type AsyncLock() = @@ -284,8 +319,9 @@ type AsyncLock() = member _.Do(f) = task { do! semaphore.WaitAsync() + try - return! f() + return! f () finally semaphore.Release() |> ignore } @@ -294,7 +330,12 @@ type AsyncLock() = member _.Dispose() = semaphore.Dispose() [] -type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality>(?keepStrongly, ?keepWeakly, ?name: string) = +type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> + ( + ?keepStrongly, + ?keepWeakly, + ?name: string + ) = let name = defaultArg name "N/A" @@ -304,12 +345,17 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T LruCache<'TKey, 'TVersion, Job<'TValue>>( keepStrongly = defaultArg keepStrongly 100, keepWeakly = defaultArg keepWeakly 100, - requiredToKeep = (function Running _ -> true | _ -> false), - event = (function - | CacheEvent.Evicted -> (fun k -> event.Trigger (JobEvent.Evicted, k)) - | CacheEvent.Collected -> (fun k -> event.Trigger (JobEvent.Collected, k)) - | CacheEvent.Weakened -> (fun k -> event.Trigger (JobEvent.Weakened, k)) - | CacheEvent.Strengthened -> (fun k -> event.Trigger (JobEvent.Strengthened, k)))) + requiredToKeep = + (function + | Running _ -> true + | _ -> false), + event = + (function + | CacheEvent.Evicted -> (fun k -> event.Trigger(JobEvent.Evicted, k)) + | CacheEvent.Collected -> (fun k -> event.Trigger(JobEvent.Collected, k)) + | CacheEvent.Weakened -> (fun k -> event.Trigger(JobEvent.Weakened, k)) + | CacheEvent.Strengthened -> (fun k -> event.Trigger(JobEvent.Strengthened, k))) + ) let requestCounts = Dictionary, int>() let cancellationRegistrations = Dictionary<_, _>() @@ -317,14 +363,15 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let saveRegistration key registration = cancellationRegistrations[key] <- match cancellationRegistrations.TryGetValue key with - | true, registrations -> registration::registrations - | _ -> [registration] + | true, registrations -> registration :: registrations + | _ -> [ registration ] let cancelRegistration key = match cancellationRegistrations.TryGetValue key with | true, registrations -> for r: CancellationTokenRegistration in registrations do r.Dispose() + cancellationRegistrations.Remove key |> ignore | _ -> () @@ -346,125 +393,137 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let processRequest post (key: KeyData<_, _>, msg) = - lock.Do(fun () -> task { + lock.Do(fun () -> + task { - let cached = cache.TryGet (key.Key, key.Version) + let cached = cache.TryGet(key.Key, key.Version) - return - match msg, cached with - | Sync, _ -> New - | GetOrCompute _, Some (Completed result) -> Existing (Task.FromResult result) - | GetOrCompute(_, ct), Some (Running (tcs, _, _)) -> - incrRequestCount key + return + match msg, cached with + | Sync, _ -> New + | GetOrCompute _, Some (Completed result) -> Existing(Task.FromResult result) + | GetOrCompute (_, ct), Some (Running (tcs, _, _)) -> + incrRequestCount key - ct.Register(fun _ -> - let _name = name - post (key, CancelRequest)) + ct.Register(fun _ -> + let _name = name + post (key, CancelRequest)) |> saveRegistration key - Existing tcs.Task + Existing tcs.Task - | GetOrCompute(computation, ct), None -> - incrRequestCount key + | GetOrCompute (computation, ct), None -> + incrRequestCount key - ct.Register(fun _ -> - let _name = name - post (key, OriginatorCanceled)) + ct.Register(fun _ -> + let _name = name + post (key, OriginatorCanceled)) |> saveRegistration key - cache.Set(key.Key, key.Version, key.Label, (Running(TaskCompletionSource(), (new CancellationTokenSource()), computation))) + cache.Set( + key.Key, + key.Version, + key.Label, + (Running(TaskCompletionSource(), (new CancellationTokenSource()), computation)) + ) - New - }) + New + }) - let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = + let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = task { do! Task.Delay 0 - do! lock.Do(fun () -> task { - - let cached = cache.TryGet (key.Key, key.Version) - - match action, cached with - - | OriginatorCanceled, Some (Running (tcs, cts, computation)) -> - - decrRequestCount key - if requestCounts[key] < 1 then - cancelRegistration key - cts.Cancel() - tcs.TrySetCanceled() |> ignore - cache.Remove (key.Key, key.Version) - requestCounts.Remove key |> ignore - log (Canceled, key) - - else - // We need to restart the computation - Task.Run(fun () -> - task { - do! Task.Delay 0 - try - log (Started, key) - let! result = Async.StartAsTask(computation, cancellationToken = cts.Token) - post (key, (JobCompleted result)) - with - | :? OperationCanceledException -> - post (key, CancelRequest) - () - | ex -> - post (key, (JobFailed ex)) - }, cts.Token) |> ignore - - - - | CancelRequest, Some (Running (tcs, cts, _c)) -> - - decrRequestCount key - if requestCounts[key] < 1 then - cancelRegistration key - cts.Cancel() - tcs.TrySetCanceled() |> ignore - cache.Remove (key.Key, key.Version) - requestCounts.Remove key |> ignore - log (Canceled, key) - - | CancelRequest, None - | CancelRequest, Some (Completed _) -> () - - | JobFailed ex, Some (Running (tcs, _cts, _c)) -> - cancelRegistration key - cache.Remove (key.Key, key.Version) - requestCounts.Remove key |> ignore - log (Failed, key) - tcs.TrySetException ex |> ignore - - | JobCompleted result, Some (Running (tcs, _cts, _c)) -> - cancelRegistration key - cache.Set(key.Key, key.Version, key.Label, (Completed result)) - requestCounts.Remove key |> ignore - log (Finished, key) - if tcs.TrySetResult result = false then - failwith "Invalid state: Completed job already completed" - () - - // Job can't be evicted from cache while it's running because then subsequent requesters would be waiting forever - | JobFailed _, None -> failwith "Invalid state: Running job missing in cache (failed)" - - | OriginatorCanceled, None -> failwith "Invalid state: Running job missing in cache (canceled)" - - | JobCompleted _, None -> failwith "Invalid state: Running job missing in cache (completed)" - - | JobFailed ex, Some (Completed _job) -> - failwith $"Invalid state: Failed Completed job \n%A{ex}" - ignore ex - - | JobCompleted _result, Some (Completed _job) -> - failwith "Invalid state: Double-Completed job" - () - - | OriginatorCanceled, Some (Completed _result) -> - failwith "Invalid state: Canceled Completed job" - () - }) + + do! + lock.Do(fun () -> + task { + + let cached = cache.TryGet(key.Key, key.Version) + + match action, cached with + + | OriginatorCanceled, Some (Running (tcs, cts, computation)) -> + + decrRequestCount key + + if requestCounts[key] < 1 then + cancelRegistration key + cts.Cancel() + tcs.TrySetCanceled() |> ignore + cache.Remove(key.Key, key.Version) + requestCounts.Remove key |> ignore + log (Canceled, key) + + else + // We need to restart the computation + Task.Run(fun () -> + task { + do! Task.Delay 0 + + try + log (Started, key) + let! result = Async.StartAsTask(computation, cancellationToken = cts.Token) + post (key, (JobCompleted result)) + with + | :? OperationCanceledException -> + post (key, CancelRequest) + () + | ex -> post (key, (JobFailed ex)) + }, + cts.Token) + |> ignore + + | CancelRequest, Some (Running (tcs, cts, _c)) -> + + decrRequestCount key + + if requestCounts[key] < 1 then + cancelRegistration key + cts.Cancel() + tcs.TrySetCanceled() |> ignore + cache.Remove(key.Key, key.Version) + requestCounts.Remove key |> ignore + log (Canceled, key) + + | CancelRequest, None + | CancelRequest, Some (Completed _) -> () + + | JobFailed ex, Some (Running (tcs, _cts, _c)) -> + cancelRegistration key + cache.Remove(key.Key, key.Version) + requestCounts.Remove key |> ignore + log (Failed, key) + tcs.TrySetException ex |> ignore + + | JobCompleted result, Some (Running (tcs, _cts, _c)) -> + cancelRegistration key + cache.Set(key.Key, key.Version, key.Label, (Completed result)) + requestCounts.Remove key |> ignore + log (Finished, key) + + if tcs.TrySetResult result = false then + failwith "Invalid state: Completed job already completed" + () + + // Job can't be evicted from cache while it's running because then subsequent requesters would be waiting forever + | JobFailed _, None -> failwith "Invalid state: Running job missing in cache (failed)" + + | OriginatorCanceled, None -> failwith "Invalid state: Running job missing in cache (canceled)" + + | JobCompleted _, None -> failwith "Invalid state: Running job missing in cache (completed)" + + | JobFailed ex, Some (Completed _job) -> + failwith $"Invalid state: Failed Completed job \n%A{ex}" + ignore ex + + | JobCompleted _result, Some (Completed _job) -> + failwith "Invalid state: Double-Completed job" + () + + | OriginatorCanceled, Some (Completed _result) -> + failwith "Invalid state: Canceled Completed job" + () + }) } let rec post msg = @@ -476,28 +535,40 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T { new ICacheKey<_, _> with member _.GetKey() = key member _.GetVersion() = Unchecked.defaultof<_> - member _.GetLabel() = key.ToString() } + member _.GetLabel() = key.ToString() + } + this.Get(wrappedKey, computation) member _.Get(key: ICacheKey<_, _>, computation) = - let key = { Label = key.GetLabel(); Key = key.GetKey(); Version = key.GetVersion() } + let key = + { + Label = key.GetLabel() + Key = key.GetKey() + Version = key.GetVersion() + } async { let! ct = Async.CancellationToken + match! processRequest post (key, GetOrCompute(computation, ct)) |> Async.AwaitTask with | New -> - try - return! Async.StartAsTask(async { - log (Started, key) - let! result = computation - post (key, (JobCompleted result)) - return result - }, cancellationToken = ct) |> Async.AwaitTask + try + return! + Async.StartAsTask( + async { + log (Started, key) + let! result = computation + post (key, (JobCompleted result)) + return result + }, + cancellationToken = ct + ) + |> Async.AwaitTask with | :? TaskCanceledException - | :? OperationCanceledException as ex -> - return raise ex + | :? OperationCanceledException as ex -> return raise ex | ex -> post (key, (JobFailed ex)) return raise ex @@ -511,11 +582,15 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T member _.Locked = lock.Semaphore.CurrentCount < 1 - member this.DebuggerDisplay = + member this.DebuggerDisplay = let locked = if this.Locked then " [LOCKED]" else "" - let valueStats = + + let valueStats = cache.GetValues() - |> Seq.countBy (function Running _ -> "Running" | Completed _ -> "Completed") + |> Seq.countBy (function + | Running _ -> "Running" + | Completed _ -> "Completed") |> Seq.map ((<||) (sprintf "%s: %d")) |> String.concat " " + $"{name}{locked} {valueStats} ({cache.DebuggerDisplay})" diff --git a/src/Compiler/Facilities/TaskAgent.fs b/src/Compiler/Facilities/TaskAgent.fs index 9812c5525d7..c39cfba3d36 100644 --- a/src/Compiler/Facilities/TaskAgent.fs +++ b/src/Compiler/Facilities/TaskAgent.fs @@ -6,12 +6,10 @@ open System.Threading.Tasks open System.Collections.Concurrent open System - type AgentMessage<'Message, 'MessageNoReply, 'Reply> = | ExpectsReply of 'Message * TaskCompletionSource<'Reply> | DoNotReply of 'MessageNoReply - [] type TaskInbox<'Msg, 'MsgNoReply, 'Reply>() = @@ -22,66 +20,73 @@ type TaskInbox<'Msg, 'MsgNoReply, 'Reply>() = member _.PostAndAwaitReply(msg) = let replySource = TaskCompletionSource<'Reply>() - queue.Enqueue (ExpectsReply (msg, replySource)) + queue.Enqueue(ExpectsReply(msg, replySource)) messageNotifications.Release() |> ignore replySource.Task member _.Post(msg) = - queue.Enqueue (DoNotReply msg) + queue.Enqueue(DoNotReply msg) messageNotifications.Release() |> ignore - member _.Receive() = task { - do! messageNotifications.WaitAsync() + member _.Receive() = + task { + do! messageNotifications.WaitAsync() - return - match queue.TryDequeue() with - | true, msg -> msg - | false, _ -> failwith "Message notifications broken" - } + return + match queue.TryDequeue() with + | true, msg -> msg + | false, _ -> failwith "Message notifications broken" + } interface IDisposable with member _.Dispose() = messageNotifications.Dispose() - [] -type TaskAgent<'Msg, 'MsgNoReply, 'Reply>( - processMessage: ('MsgNoReply -> unit) -> 'Msg -> 'Reply, - processMessageNoReply: ('MsgNoReply -> unit) -> 'MsgNoReply -> unit) = +type TaskAgent<'Msg, 'MsgNoReply, 'Reply> + ( + processMessage: ('MsgNoReply -> unit) -> 'Msg -> 'Reply, + processMessageNoReply: ('MsgNoReply -> unit) -> 'MsgNoReply -> unit + ) = let inbox = new TaskInbox<'Msg, 'MsgNoReply, 'Reply>() let exceptionEvent = new Event<_>() let mutable running = true - let _loop = backgroundTask { - while running do - match! inbox.Receive() with - | ExpectsReply (msg, replySource) -> - try - let reply = processMessage inbox.Post msg - replySource.SetResult reply - with ex -> - replySource.SetException ex - - | DoNotReply msg -> - try - processMessageNoReply inbox.Post msg - with ex -> - exceptionEvent.Trigger (msg, ex) - } + let _loop = + backgroundTask { + while running do + match! inbox.Receive() with + | ExpectsReply (msg, replySource) -> + try + let reply = processMessage inbox.Post msg + replySource.SetResult reply + with ex -> + replySource.SetException ex + + | DoNotReply msg -> + try + processMessageNoReply inbox.Post msg + with ex -> + exceptionEvent.Trigger(msg, ex) + } member _.NoReplyExceptions = exceptionEvent.Publish member _.Status = _loop.Status member _.PostAndAwaitReply(msg) = - if not running then failwith "Agent has been disposed and is no longer processing messages" + if not running then + failwith "Agent has been disposed and is no longer processing messages" + inbox.PostAndAwaitReply(msg) member _.Post(msg) = - if not running then failwith "Agent has been disposed and is no longer processing messages" + if not running then + failwith "Agent has been disposed and is no longer processing messages" + inbox.Post(msg) interface IDisposable with diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index fdf2cafc0cd..dfb6ce72ffc 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -44,7 +44,6 @@ type FilePath = string type ProjectPath = string type FileVersion = int - type internal IBackgroundCompiler = /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. @@ -1447,7 +1446,6 @@ type internal BackgroundCompiler static member ActualCheckFileCount = actualCheckFileCount - interface IBackgroundCompiler with member _.BeforeBackgroundFileCheck = self.BeforeBackgroundFileCheck @@ -1596,10 +1594,15 @@ type internal BackgroundCompiler userOpName: string ) : Async = async { - let fileSnapshot = projectSnapshot.SourceFiles |> Seq.find (fun f -> f.FileName = fileName) + let fileSnapshot = + projectSnapshot.SourceFiles |> Seq.find (fun f -> f.FileName = fileName) + let! sourceText = fileSnapshot.GetSource() |> Async.AwaitTask let options = projectSnapshot.ToOptions() - return! self.ParseAndCheckFileInProject(fileName, 0, sourceText, options, userOpName) |> Async.AwaitNodeCode + + return! + self.ParseAndCheckFileInProject(fileName, 0, sourceText, options, userOpName) + |> Async.AwaitNodeCode } member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 28af76560f1..14562f22a51 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -133,11 +133,11 @@ type FSharpFileSnapshot = interface ICacheKey with member this.GetLabel() = this.FileName |> shortPath - member this.GetKey() = this.FileName + member this.GetKey() = this.FileName member this.GetVersion() = this.Version -type ReferenceOnDisk = { Path: string; LastModified: DateTime } - +type ReferenceOnDisk = + { Path: string; LastModified: DateTime } [] type FSharpProjectSnapshot = @@ -214,29 +214,41 @@ type FSharpProjectSnapshot = (([], Set.empty), this.SourceFiles) ||> Seq.fold (fun (res, sigs) file -> if file.IsSignatureFile then - file::res, sigs |> Set.add file.FileName + file :: res, sigs |> Set.add file.FileName else let sigFileName = $"{file.FileName}i" - if sigs.Contains sigFileName then res, sigs |> Set.remove sigFileName - else file::res, sigs) + + if sigs.Contains sigFileName then + res, sigs |> Set.remove sigFileName + else + file :: res, sigs) |> fst |> List.rev + { this with SourceFiles = files } member this.WithoutImplFilesThatHaveSignaturesExceptLastOne = let lastFile = this.SourceFiles |> List.last + if lastFile.IsSignatureFile then this.WithoutImplFilesThatHaveSignatures else let snapshot = this.WithoutImplFilesThatHaveSignatures - { snapshot with SourceFiles = snapshot.SourceFiles @ [lastFile] } + + { snapshot with + SourceFiles = snapshot.SourceFiles @ [ lastFile ] + } member this.SourceFileNames = this.SourceFiles |> List.map (fun x -> x.FileName) - member this.CommandLineOptions = - seq { for r in this.ReferencesOnDisk do + member this.CommandLineOptions = + seq { + for r in this.ReferencesOnDisk do $"-r:{r.Path}" - yield! this.OtherOptions } |> Seq.toList + + yield! this.OtherOptions + } + |> Seq.toList member this.WithoutFileVersions = { this with @@ -253,18 +265,27 @@ type FSharpProjectSnapshot = { new ICacheKey<_, _> with member _.GetLabel() = fileName |> shortPath member _.GetKey() = fileName, this.Key.GetKey() - member _.GetVersion() = this.UpTo(fileName).WithoutImplFilesThatHaveSignaturesExceptLastOne.Key.GetVersion() } + + member _.GetVersion() = + this + .UpTo(fileName) + .WithoutImplFilesThatHaveSignaturesExceptLastOne.Key.GetVersion() + } interface ICacheKey with member this.GetLabel() = this.ToString() member this.GetKey() = this.ProjectFileName - member this.GetVersion() = + + member this.GetVersion() = Md5Hasher.empty |> Md5Hasher.addString this.ProjectFileName |> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) |> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) |> Md5Hasher.addStrings this.OtherOptions - |> Md5Hasher.addVersions (this.ReferencedProjects |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key)) + |> Md5Hasher.addVersions ( + this.ReferencedProjects + |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key) + ) |> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment |> Md5Hasher.addBool this.UseScriptResolutionRules @@ -304,7 +325,7 @@ and [] public FSharpReferencedProjectSnapshot = override this.GetHashCode() = this.OutputFile.GetHashCode() - member this.Key = + member this.Key = match this with | FSharpReference (_, snapshot) -> snapshot.Key @@ -426,12 +447,18 @@ type FSharpProjectSnapshot with | _ -> None) |> Async.Parallel - let referencesOnDisk, otherOptions = - options.OtherOptions + let referencesOnDisk, otherOptions = + options.OtherOptions |> Array.partition (fun x -> x.StartsWith("-r:")) - |> map1Of2 (Array.map (fun x -> - let path = x.Substring(3) - { Path = path; LastModified = FileSystem.GetLastWriteTimeShim(path) } )) + |> map1Of2 ( + Array.map (fun x -> + let path = x.Substring(3) + + { + Path = path + LastModified = FileSystem.GetLastWriteTimeShim(path) + }) + ) return { diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index b5c96c1a763..4dc6b84d801 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -54,10 +54,9 @@ type FSharpFileSnapshot = interface ICacheKey - /// Referenced assembly on disk. Includes last modified time so we know we need to rebuild when it changes. -type ReferenceOnDisk = { Path: string; LastModified: DateTime } - +type ReferenceOnDisk = + { Path: string; LastModified: DateTime } [] type FSharpProjectSnapshot = @@ -139,7 +138,7 @@ type FSharpProjectSnapshot = member internal Key: ICacheKey - member internal FileKey: fileName:string -> ICacheKey<(string * string), string> + member internal FileKey: fileName: string -> ICacheKey<(string * string), string> interface ICacheKey diff --git a/src/Compiler/Service/IncrementalBuild.fsi b/src/Compiler/Service/IncrementalBuild.fsi index 2b2c00dd4b6..e678aa52ee3 100644 --- a/src/Compiler/Service/IncrementalBuild.fsi +++ b/src/Compiler/Service/IncrementalBuild.fsi @@ -31,7 +31,7 @@ type internal FrameworkImportsCacheKey = targetFrameworkDirectories: string list * fsharpBinaries: string * langVersion: decimal - + interface ICacheKey /// Lookup the global static cache for building the FrameworkTcImports diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 51727da182a..81601d1cd33 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -42,7 +42,6 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.CreateILModule open FSharp.Compiler.TypedTreeOps - type internal FSharpFile = { Range: range @@ -133,18 +132,26 @@ type internal DependencyGraphType = type internal Extensions = [] static member Key(fileSnapshots: FSharpFileSnapshot list, ?extraKeyFlag) = - + { new ICacheKey<_, _> with member _.GetLabel() = - let lastFile = fileSnapshots |> List.tryLast |> Option.map (fun f -> f.FileName |> shortPath) |> Option.defaultValue "[no file]" + let lastFile = + fileSnapshots + |> List.tryLast + |> Option.map (fun f -> f.FileName |> shortPath) + |> Option.defaultValue "[no file]" + $"%d{fileSnapshots.Length} files ending with {lastFile}" + member _.GetKey() = Md5Hasher.empty |> Md5Hasher.addStrings (fileSnapshots |> Seq.map (fun f -> f.FileName)) |> pair extraKeyFlag + member _.GetVersion() = Md5Hasher.empty - |> Md5Hasher.addStrings (fileSnapshots |> Seq.map (fun f -> f.Version)) } + |> Md5Hasher.addStrings (fileSnapshots |> Seq.map (fun f -> f.Version)) + } [] module private TypeCheckingGraphProcessing = @@ -251,37 +258,35 @@ module private TypeCheckingGraphProcessing = return finalFileResults, state } - type internal CompilerCaches() = - member val ParseFile = AsyncMemoize(keepStrongly=1000, keepWeakly=2000, name="ParseFile") - - member val ParseAndCheckFileInProject = AsyncMemoize(name="ParseAndCheckFileInProject") + member val ParseFile = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "ParseFile") - member val ParseAndCheckAllFilesInProject = AsyncMemoize(name="ParseAndCheckFullProject") + member val ParseAndCheckFileInProject = AsyncMemoize(name = "ParseAndCheckFileInProject") - member val ParseAndCheckProject = AsyncMemoize(name="ParseAndCheckProject") + member val ParseAndCheckAllFilesInProject = AsyncMemoize(name = "ParseAndCheckFullProject") - member val FrameworkImports = AsyncMemoize(name="FrameworkImports") + member val ParseAndCheckProject = AsyncMemoize(name = "ParseAndCheckProject") - member val BootstrapInfoStatic = AsyncMemoize(name="BootstrapInfoStatic") + member val FrameworkImports = AsyncMemoize(name = "FrameworkImports") - member val BootstrapInfo = AsyncMemoize(name="BootstrapInfo") + member val BootstrapInfoStatic = AsyncMemoize(name = "BootstrapInfoStatic") - member val TcFile = AsyncMemoize(name="TcPrior") + member val BootstrapInfo = AsyncMemoize(name = "BootstrapInfo") - member val TcIntermediate = AsyncMemoize(keepStrongly=1000, keepWeakly=2000, name="TcIntermediate") + member val TcFile = AsyncMemoize(name = "TcPrior") - member val DependencyGraph = AsyncMemoize(name="DependencyGraph") + member val TcIntermediate = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "TcIntermediate") - member val ProjectExtras = AsyncMemoize(name="ProjectExtras") + member val DependencyGraph = AsyncMemoize(name = "DependencyGraph") - member val AssemblyData = AsyncMemoize(name="AssemblyData") + member val ProjectExtras = AsyncMemoize(name = "ProjectExtras") - member val SemanticClassification = AsyncMemoize(name="SemanticClassification") + member val AssemblyData = AsyncMemoize(name = "AssemblyData") - member val ItemKeyStore = AsyncMemoize(name="ItemKeyStore") + member val SemanticClassification = AsyncMemoize(name = "SemanticClassification") + member val ItemKeyStore = AsyncMemoize(name = "ItemKeyStore") type internal TransparentCompiler ( @@ -357,7 +362,10 @@ type internal TransparentCompiler async { use _ = Activity.start "ComputeFrameworkImports" [] let tcConfigP = TcConfigProvider.Constant tcConfig - return! TcImports.BuildFrameworkTcImports(tcConfigP, frameworkDLLs, nonFrameworkResolutions) |> Async.AwaitNodeCode + + return! + TcImports.BuildFrameworkTcImports(tcConfigP, frameworkDLLs, nonFrameworkResolutions) + |> Async.AwaitNodeCode } ) @@ -393,7 +401,8 @@ type internal TransparentCompiler nonFrameworkResolutions, unresolvedReferences, dependencyProvider - ) |> Async.AwaitNodeCode + ) + |> Async.AwaitNodeCode #if !NO_TYPEPROVIDERS // TODO: review and handle the event tcImports.GetCcusExcludingBase() @@ -816,12 +825,15 @@ type internal TransparentCompiler ) let ComputeParseFile bootstrapInfo (file: FSharpFile) = - + let key = { new ICacheKey<_, _> with - member _.GetLabel() = shortPath file.Source.FileName + member _.GetLabel() = shortPath file.Source.FileName member _.GetKey() = file.Source.FileName - member _.GetVersion() = file.Source.Version, file.IsLastCompiland, file.IsExe } + + member _.GetVersion() = + file.Source.Version, file.IsLastCompiland, file.IsExe + } caches.ParseFile.Get( key, @@ -854,14 +866,15 @@ type internal TransparentCompiler // In case we don't want to use any parallel processing let mkLinearGraph count : Graph = - seq { - 0, [| |] + seq { + 0, [||] + yield! - [0..count-1] + [ 0 .. count - 1 ] |> Seq.rev |> Seq.pairwise |> Seq.map (fun (a, b) -> a, [| b |]) - } + } |> Graph.make let computeDependencyGraph (tcConfig: TcConfig) parsedInputs (processGraph: Graph -> Graph) = @@ -884,12 +897,10 @@ type internal TransparentCompiler // TODO: we will probably want to cache and re-use larger graphs if available let graph = - if tcConfig.compilingFSharpCore then + if tcConfig.compilingFSharpCore then mkLinearGraph sourceFiles.Length else - DependencyResolution.mkGraph filePairs sourceFiles - |> fst - |> processGraph + DependencyResolution.mkGraph filePairs sourceFiles |> fst |> processGraph let nodeGraph = TransformDependencyGraph(graph, filePairs) @@ -913,14 +924,23 @@ type internal TransparentCompiler return nodeGraph, graph } - let removeImplFilesThatHaveSignatures (projectSnapshot: FSharpProjectSnapshot) (graph: Graph) = + let removeImplFilesThatHaveSignatures (projectSnapshot: FSharpProjectSnapshot) (graph: Graph) = let removeIndexes = projectSnapshot.SourceFileNames |> Seq.mapi pair - |> Seq.groupBy (snd >> (fun fileName -> if fileName.EndsWith(".fsi") then fileName.Substring(0, fileName.Length - 1) else fileName)) + |> Seq.groupBy ( + snd + >> (fun fileName -> + if fileName.EndsWith(".fsi") then + fileName.Substring(0, fileName.Length - 1) + else + fileName) + ) |> Seq.map (snd >> (Seq.toList)) - |> Seq.choose (function [idx1, _; idx2, _] -> max idx1 idx2 |> Some | _ -> None) + |> Seq.choose (function + | [ idx1, _; idx2, _ ] -> max idx1 idx2 |> Some + | _ -> None) |> Set graph @@ -928,14 +948,23 @@ type internal TransparentCompiler |> Seq.map (fun x -> x.Key, x.Value |> Array.filter (fun node -> not (removeIndexes.Contains node))) |> Graph.make - let removeImplFilesThatHaveSignaturesExceptLastOne (projectSnapshot: FSharpProjectSnapshot) (graph: Graph) = + let removeImplFilesThatHaveSignaturesExceptLastOne (projectSnapshot: FSharpProjectSnapshot) (graph: Graph) = let removeIndexes = projectSnapshot.SourceFileNames |> Seq.mapi pair - |> Seq.groupBy (snd >> (fun fileName -> if fileName.EndsWith(".fsi") then fileName.Substring(0, fileName.Length - 1) else fileName)) + |> Seq.groupBy ( + snd + >> (fun fileName -> + if fileName.EndsWith(".fsi") then + fileName.Substring(0, fileName.Length - 1) + else + fileName) + ) |> Seq.map (snd >> (Seq.toList)) - |> Seq.choose (function [idx1, _; idx2, _] -> max idx1 idx2 |> Some | _ -> None) + |> Seq.choose (function + | [ idx1, _; idx2, _ ] -> max idx1 idx2 |> Some + | _ -> None) |> Set // Don't remove the last file |> Set.remove (projectSnapshot.SourceFiles.Length - 1) @@ -946,10 +975,13 @@ type internal TransparentCompiler |> Graph.make let ComputeDependencyGraphForFile (tcConfig: TcConfig) (priorSnapshot: FSharpProjectSnapshot) parsedInputs = - let key = priorSnapshot.SourceFiles.Key(DependencyGraphType.File) + let key = priorSnapshot.SourceFiles.Key(DependencyGraphType.File) //let lastFileIndex = (parsedInputs |> Array.length) - 1 //caches.DependencyGraph.Get(key, computeDependencyGraph parsedInputs (Graph.subGraphFor lastFileIndex)) - caches.DependencyGraph.Get(key, computeDependencyGraph tcConfig parsedInputs (removeImplFilesThatHaveSignaturesExceptLastOne priorSnapshot)) + caches.DependencyGraph.Get( + key, + computeDependencyGraph tcConfig parsedInputs (removeImplFilesThatHaveSignaturesExceptLastOne priorSnapshot) + ) let ComputeDependencyGraphForProject (tcConfig: TcConfig) (projectSnapshot: FSharpProjectSnapshot) parsedInputs = let key = projectSnapshot.SourceFiles.Key(DependencyGraphType.Project) @@ -976,11 +1008,13 @@ type internal TransparentCompiler let fileName = input.FileName use _ = - Activity.start "ComputeTcIntermediate" [| - Activity.Tags.fileName, fileName |> Path.GetFileName - "key", key.GetKey() |> sprintf "%A" - "version", key.GetVersion() - |] + Activity.start + "ComputeTcIntermediate" + [| + Activity.Tags.fileName, fileName |> Path.GetFileName + "key", key.GetKey() |> sprintf "%A" + "version", key.GetVersion() + |] let tcConfig = bootstrapInfo.TcConfig let tcGlobals = bootstrapInfo.TcGlobals @@ -1022,7 +1056,7 @@ type internal TransparentCompiler prevTcInfo.tcState, input, true) - |> Cancellable.toAsync + |> Cancellable.toAsync //fileChecked.Trigger fileName @@ -1054,7 +1088,10 @@ type internal TransparentCompiler match fileNode with | NodeToTypeCheck.PhysicalFile index -> let input, parseErrors, _ = parsedInputs[index] - let! tcIntermediate = ComputeTcIntermediate projectSnapshot dependencyFiles fileNode (input, parseErrors) bootstrapInfo tcInfo + + let! tcIntermediate = + ComputeTcIntermediate projectSnapshot dependencyFiles fileNode (input, parseErrors) bootstrapInfo tcInfo + let (Finisher (node = node; finisher = finisher)) = tcIntermediate.finisher return @@ -1065,7 +1102,10 @@ type internal TransparentCompiler if tcInfo.stateContainsNodes |> Set.contains fileNode then failwith $"Oops!" - if tcInfo.stateContainsNodes |> Set.contains (NodeToTypeCheck.ArtificialImplFile (index - 1)) then + if + tcInfo.stateContainsNodes + |> Set.contains (NodeToTypeCheck.ArtificialImplFile(index - 1)) + then failwith $"Oops???" let partialResult, tcState = finisher tcInfo.tcState @@ -1106,7 +1146,10 @@ type internal TransparentCompiler if tcInfo.stateContainsNodes |> Set.contains fileNode then failwith $"Oops!" - if tcInfo.stateContainsNodes |> Set.contains (NodeToTypeCheck.PhysicalFile (index + 1)) then + if + tcInfo.stateContainsNodes + |> Set.contains (NodeToTypeCheck.PhysicalFile(index + 1)) + then failwith $"Oops!!!" let parsedInput, _parseErrors, _ = parsedInputs[index] @@ -1165,7 +1208,7 @@ type internal TransparentCompiler let! graph, dependencyFiles = ComputeDependencyGraphForFile bootstrapInfo.TcConfig priorSnapshot (parsedInputs |> Array.map p13) - //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) + //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) let! results, tcInfo = processTypeCheckingGraph @@ -1624,7 +1667,11 @@ type internal TransparentCompiler |> NodeCode.AwaitAsync ignore parseResults - let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) |> NodeCode.AwaitAsync + + let! _, result = + this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + |> NodeCode.AwaitAsync + return result } @@ -1643,7 +1690,11 @@ type internal TransparentCompiler |> NodeCode.AwaitAsync ignore parseResults - let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) |> NodeCode.AwaitAsync + + let! _, result = + this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + |> NodeCode.AwaitAsync + return Some result } @@ -1670,12 +1721,17 @@ type internal TransparentCompiler node { ignore canInvalidateProject let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! this.FindReferencesInFile(fileName, snapshot, symbol, userOpName) |> NodeCode.AwaitAsync + + return! + this.FindReferencesInFile(fileName, snapshot, symbol, userOpName) + |> NodeCode.AwaitAsync } member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = node { - return! this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) |> NodeCode.AwaitAsync + return! + this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) + |> NodeCode.AwaitAsync } member _.FrameworkImportsCache: FrameworkImportsCache = @@ -1698,9 +1754,11 @@ type internal TransparentCompiler ) : NodeCode = node { let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - - match! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) |> NodeCode.AwaitAsync with + match! + this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + |> NodeCode.AwaitAsync + with | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return parseResult, checkResult | parseResult, FSharpCheckFileAnswer.Aborted -> return parseResult, FSharpCheckFileResults.MakeEmpty(fileName, [||], true) } @@ -1730,8 +1788,10 @@ type internal TransparentCompiler FSharpProjectSnapshot.FromOptions(options, fileName, 1, sourceText) |> NodeCode.AwaitAsync - - match! this.ParseAndCheckFileInProject(fileName, snapshot, "GetCachedCheckFileResult") |> NodeCode.AwaitAsync with + match! + this.ParseAndCheckFileInProject(fileName, snapshot, "GetCachedCheckFileResult") + |> NodeCode.AwaitAsync + with | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return Some(parseResult, checkResult) | _, FSharpCheckFileAnswer.Aborted -> return None } @@ -1804,7 +1864,9 @@ type internal TransparentCompiler FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync - return! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) |> NodeCode.AwaitAsync + return! + this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + |> NodeCode.AwaitAsync } member this.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = @@ -1824,9 +1886,7 @@ type internal TransparentCompiler } member this.ParseFile(fileName, projectSnapshot, userOpName) = - node { - return! this.ParseFile(fileName, projectSnapshot, userOpName) |> NodeCode.AwaitAsync - } + node { return! this.ParseFile(fileName, projectSnapshot, userOpName) |> NodeCode.AwaitAsync } member this.ParseFile ( diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index d71b45475b9..e8fc38b54a6 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -272,13 +272,12 @@ type FSharpChecker member _.UsesTransparentCompiler = useTransparentCompiler = Some true - member _.TransparentCompiler = + member _.TransparentCompiler = match useTransparentCompiler with - | Some true -> backgroundCompiler :?> TransparentCompiler + | Some true -> backgroundCompiler :?> TransparentCompiler | _ -> failwith "Transparent Compiler is not enabled." - member this.Caches = - this.TransparentCompiler.Caches + member this.Caches = this.TransparentCompiler.Caches member _.ReferenceResolver = legacyReferenceResolver diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index 925173ec5cb..b05f0c4ba54 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -1,9 +1,7 @@ - -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. namespace FSharp.Compiler.Diagnostics - open System /// For activities following the dotnet distributed tracing concept diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 12674fa0db9..8eab7e3c074 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -222,7 +222,6 @@ - diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs deleted file mode 100644 index 8344be03309..00000000000 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/Graph/GraphOperations.fs +++ /dev/null @@ -1,84 +0,0 @@ -module FSharp.Compiler.ComponentTests.TypeChecks.Graph.GraphOperations - -open System.IO -open Xunit -open FSharp.Compiler.GraphChecking -open FSharp.Test.ProjectGeneration -open FSharp.Compiler.Text -open FSharp.Compiler -open FSharp.Compiler.ParseAndCheckInputs - - -[] -let ``Transform graph to layers of leaves`` () = - - let g = Graph.make [ - 'B', [|'A'|] - 'C', [|'A'|] - 'E', [|'A'; 'B'; 'C'|] - 'F', [|'C'; 'D'|] - ] - - let expected = [ - set [ 'A'; 'D' ] - set [ 'B'; 'C' ] - set [ 'E'; 'F' ] - ] - - let result = Graph.leafSequence g |> Seq.toList - - Assert.Equal list>(expected, result) - - -[] -let ``See what this does`` () = - - SyntheticProject.Create( - sourceFile "A" [] |> addSignatureFile, - sourceFile "B" ["A"] |> addSignatureFile, - sourceFile "C" ["A"] |> addSignatureFile, - sourceFile "D" [] |> addSignatureFile, - sourceFile "E" ["A"; "B"; "C"] |> addSignatureFile, - sourceFile "F" ["C"; "D"] |> addSignatureFile - ).Workflow { - withProject (fun project checker -> - async { - let options = project.GetProjectOptions checker - let options, _ = checker.GetParsingOptionsFromProjectOptions options - let! inputs = - project.SourceFilePaths - |> Seq.map (fun path -> path, File.ReadAllText path |> SourceText.ofString) - |> Seq.map (fun (path, text) -> checker.ParseFile(path, text, options)) - |> Async.Parallel - - let sourceFiles: FileInProject array = - inputs - |> Seq.map (fun x -> x.ParseTree) - |> Seq.toArray - |> Array.mapi (fun idx (input: Syntax.ParsedInput) -> - { - Idx = idx - FileName = input.FileName - ParsedInput = input - }) - - let filePairs = FilePairMap(sourceFiles) - - let fullGraph = - DependencyResolution.mkGraph filePairs sourceFiles - |> fst - //|> Graph.map (fun idx -> project.SourceFilePaths[idx] |> Path.GetFileName) - - let subGraph = fullGraph |> Graph.subGraphFor 9 //"FileF.fs" - - let layers = Graph.leafSequence subGraph |> Seq.toList - - let transformed = TransformDependencyGraph (subGraph, filePairs) - - ignore (layers, transformed) - - return () - } - ) - } - From ee7d940e3eb552a83442cdf417408870111cac03 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 5 Sep 2023 13:59:01 +0200 Subject: [PATCH 079/222] more experiments --- src/Compiler/Service/FSharpCheckerResults.fs | 3 ++ src/Compiler/Service/FSharpCheckerResults.fsi | 2 + src/Compiler/Service/TransparentCompiler.fs | 38 ++++++++++++------- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 14562f22a51..b4fd8d494b4 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -272,6 +272,9 @@ type FSharpProjectSnapshot = .WithoutImplFilesThatHaveSignaturesExceptLastOne.Key.GetVersion() } + member this.GetLastModifiedTimeOnDisk() = + DateTime.Now + interface ICacheKey with member this.GetLabel() = this.ToString() member this.GetKey() = this.ProjectFileName diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 4dc6b84d801..42a75d26d62 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -136,6 +136,8 @@ type FSharpProjectSnapshot = /// A snapshot of the same project with file versions removed. member WithoutFileVersions: FSharpProjectSnapshot + member GetLastModifiedTimeOnDisk: unit -> DateTime + member internal Key: ICacheKey member internal FileKey: fileName: string -> ICacheKey<(string * string), string> diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 81601d1cd33..79a023d38d4 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -501,7 +501,7 @@ type internal TransparentCompiler node { Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) - return! self.GetAssemblyData(projectSnapshot, userOpName + ".CheckReferencedProject(" + nm + ")") + return! self.GetAssemblyData(projectSnapshot, nm, userOpName + ".CheckReferencedProject(" + nm + ")") } member x.TryGetLogicalTimeStamp(cache) = @@ -1458,15 +1458,27 @@ type internal TransparentCompiler } ) - let ComputeAssemblyData (projectSnapshot: FSharpProjectSnapshot) = + let ComputeAssemblyData (projectSnapshot: FSharpProjectSnapshot) fileName = caches.AssemblyData.Get( projectSnapshot.WithoutImplFilesThatHaveSignatures.Key, async { - match! ComputeBootstrapInfo projectSnapshot with - | None, _ -> return ProjectAssemblyDataResult.Unavailable true - | Some bootstrapInfo, _creationDiags -> - let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot - return assemblyDataResult + let availableOnDiskModifiedTime = + if FileSystem.FileExistsShim fileName then + Some <| FileSystem.GetLastWriteTimeShim fileName + else None + + let shouldUseOnDisk = + availableOnDiskModifiedTime + |> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) + + if shouldUseOnDisk then + return ProjectAssemblyDataResult.Unavailable true + else + match! ComputeBootstrapInfo projectSnapshot with + | None, _ -> return ProjectAssemblyDataResult.Unavailable true + | Some bootstrapInfo, _creationDiags -> + let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot + return assemblyDataResult } ) @@ -1642,8 +1654,8 @@ type internal TransparentCompiler | Some itemKeyStore -> return itemKeyStore.FindAll symbol.Item } - member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, _userOpName) = - ComputeAssemblyData projectSnapshot |> NodeCode.AwaitAsync + member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, fileName, _userOpName) = + ComputeAssemblyData projectSnapshot fileName |> NodeCode.AwaitAsync member _.Caches = caches @@ -1737,14 +1749,14 @@ type internal TransparentCompiler member _.FrameworkImportsCache: FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache - member this.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = + member this.GetAssemblyData(options: FSharpProjectOptions, fileName, userOpName: string) : NodeCode = node { let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! this.GetAssemblyData(snapshot, userOpName) + return! this.GetAssemblyData(snapshot, fileName, userOpName) } - member this.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = - this.GetAssemblyData(projectSnapshot, userOpName) + member this.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, fileName, userOpName: string) : NodeCode = + this.GetAssemblyData(projectSnapshot, fileName, userOpName) member this.GetBackgroundCheckResultsForFileInProject ( From d0bbd78200e4805f4543a7a49a7f685b2b08953a Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 5 Sep 2023 13:59:01 +0200 Subject: [PATCH 080/222] more experiments --- src/Compiler/Service/BackgroundCompiler.fs | 8 ++-- src/Compiler/Service/FSharpCheckerResults.fs | 3 ++ src/Compiler/Service/FSharpCheckerResults.fsi | 2 + src/Compiler/Service/TransparentCompiler.fs | 38 ++++++++++++------- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index dfb6ce72ffc..7aef64e54fa 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -85,10 +85,10 @@ type internal IBackgroundCompiler = NodeCode> abstract member GetAssemblyData: - options: FSharpProjectOptions * userOpName: string -> NodeCode + options: FSharpProjectOptions * outputFileName: string * userOpName: string -> NodeCode abstract member GetAssemblyData: - projectSnapshot: FSharpProjectSnapshot * userOpName: string -> NodeCode + projectSnapshot: FSharpProjectSnapshot * outputFileName: string * userOpName: string -> NodeCode /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) abstract member GetBackgroundCheckResultsForFileInProject: @@ -1493,10 +1493,10 @@ type internal BackgroundCompiler member _.FrameworkImportsCache: FrameworkImportsCache = self.FrameworkImportsCache - member _.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = + member _.GetAssemblyData(options: FSharpProjectOptions, _fileName: string, userOpName: string) : NodeCode = self.GetAssemblyData(options, userOpName) - member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = + member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, _fileName: string, userOpName: string) : NodeCode = self.GetAssemblyData(projectSnapshot.ToOptions(), userOpName) member _.GetBackgroundCheckResultsForFileInProject diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index e5278f668bf..98cf78250c1 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -272,6 +272,9 @@ type FSharpProjectSnapshot = .WithoutImplFilesThatHaveSignaturesExceptLastOne.Key.GetVersion() } + member this.GetLastModifiedTimeOnDisk() = + DateTime.Now + interface ICacheKey with member this.GetLabel() = this.ToString() member this.GetKey() = this.ProjectFileName diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 4dc6b84d801..42a75d26d62 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -136,6 +136,8 @@ type FSharpProjectSnapshot = /// A snapshot of the same project with file versions removed. member WithoutFileVersions: FSharpProjectSnapshot + member GetLastModifiedTimeOnDisk: unit -> DateTime + member internal Key: ICacheKey member internal FileKey: fileName: string -> ICacheKey<(string * string), string> diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 81601d1cd33..79a023d38d4 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -501,7 +501,7 @@ type internal TransparentCompiler node { Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) - return! self.GetAssemblyData(projectSnapshot, userOpName + ".CheckReferencedProject(" + nm + ")") + return! self.GetAssemblyData(projectSnapshot, nm, userOpName + ".CheckReferencedProject(" + nm + ")") } member x.TryGetLogicalTimeStamp(cache) = @@ -1458,15 +1458,27 @@ type internal TransparentCompiler } ) - let ComputeAssemblyData (projectSnapshot: FSharpProjectSnapshot) = + let ComputeAssemblyData (projectSnapshot: FSharpProjectSnapshot) fileName = caches.AssemblyData.Get( projectSnapshot.WithoutImplFilesThatHaveSignatures.Key, async { - match! ComputeBootstrapInfo projectSnapshot with - | None, _ -> return ProjectAssemblyDataResult.Unavailable true - | Some bootstrapInfo, _creationDiags -> - let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot - return assemblyDataResult + let availableOnDiskModifiedTime = + if FileSystem.FileExistsShim fileName then + Some <| FileSystem.GetLastWriteTimeShim fileName + else None + + let shouldUseOnDisk = + availableOnDiskModifiedTime + |> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) + + if shouldUseOnDisk then + return ProjectAssemblyDataResult.Unavailable true + else + match! ComputeBootstrapInfo projectSnapshot with + | None, _ -> return ProjectAssemblyDataResult.Unavailable true + | Some bootstrapInfo, _creationDiags -> + let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot + return assemblyDataResult } ) @@ -1642,8 +1654,8 @@ type internal TransparentCompiler | Some itemKeyStore -> return itemKeyStore.FindAll symbol.Item } - member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, _userOpName) = - ComputeAssemblyData projectSnapshot |> NodeCode.AwaitAsync + member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, fileName, _userOpName) = + ComputeAssemblyData projectSnapshot fileName |> NodeCode.AwaitAsync member _.Caches = caches @@ -1737,14 +1749,14 @@ type internal TransparentCompiler member _.FrameworkImportsCache: FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache - member this.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = + member this.GetAssemblyData(options: FSharpProjectOptions, fileName, userOpName: string) : NodeCode = node { let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! this.GetAssemblyData(snapshot, userOpName) + return! this.GetAssemblyData(snapshot, fileName, userOpName) } - member this.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = - this.GetAssemblyData(projectSnapshot, userOpName) + member this.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, fileName, userOpName: string) : NodeCode = + this.GetAssemblyData(projectSnapshot, fileName, userOpName) member this.GetBackgroundCheckResultsForFileInProject ( From 113d8dbe756bbbedd654d8f8b6435a5194e76bc7 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 5 Sep 2023 16:27:28 +0200 Subject: [PATCH 081/222] debug output update --- src/Compiler/Service/TransparentCompiler.fs | 13 ++++++++++--- src/Compiler/Service/service.fs | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 79a023d38d4..316f5f000da 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -916,7 +916,7 @@ type internal TransparentCompiler | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF [{i}] : {fileNames[i]}") |> Graph.serialiseToMermaid - Trace.TraceInformation("\n" + debugGraph) + //Trace.TraceInformation("\n" + debugGraph) if Activity.Current <> null then Activity.Current.AddTag("graph", debugGraph) |> ignore @@ -1012,7 +1012,7 @@ type internal TransparentCompiler "ComputeTcIntermediate" [| Activity.Tags.fileName, fileName |> Path.GetFileName - "key", key.GetKey() |> sprintf "%A" + "key", key.GetLabel() "version", key.GetVersion() |] @@ -1471,13 +1471,20 @@ type internal TransparentCompiler availableOnDiskModifiedTime |> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) + let name = projectSnapshot.ProjectFileName |> Path.GetFileNameWithoutExtension + if shouldUseOnDisk then + Trace.TraceInformation($"Using assembly on disk: {name}") return ProjectAssemblyDataResult.Unavailable true else match! ComputeBootstrapInfo projectSnapshot with - | None, _ -> return ProjectAssemblyDataResult.Unavailable true + | None, _ -> + Trace.TraceInformation($"Using assembly on disk (unintentionally): {name}") + return ProjectAssemblyDataResult.Unavailable true | Some bootstrapInfo, _creationDiags -> let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot + Trace.TraceInformation($"Using in-memory project reference: {name}") + return assemblyDataResult } ) diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index e8fc38b54a6..d8682c3e934 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -284,8 +284,8 @@ type FSharpChecker member _.MatchBraces(fileName, sourceText: ISourceText, options: FSharpParsingOptions, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" - use _ = - Activity.start "FSharpChecker.MatchBraces" [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, userOpName |] + //use _ = + // Activity.start "FSharpChecker.MatchBraces" [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, userOpName |] let hash = sourceText.GetHashCode() |> int64 From 8b56248012c7185ed44a9fdd394db50ecb802330 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 6 Sep 2023 17:04:55 +0200 Subject: [PATCH 082/222] fix merge --- tests/FSharp.Test.Utilities/ProjectGeneration.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 6adef3eb1d1..f92e08f5254 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -1069,6 +1069,7 @@ type ProjectWorkflowBuilder let project, file = ctx.Project.FindInAllProjects fileId let fileName = project.ProjectDir ++ file.FileName let source = renderSourceFile project file + let options = project.GetProjectOptions checker let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot ctx.Project) return! getSymbolUse fileName source symbolName snapshot checker } From 21020d92fbdc27bf9083afc2b1f5bc763c99a776 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 5 Sep 2023 13:59:01 +0200 Subject: [PATCH 083/222] more experiments --- src/Compiler/Service/FSharpCheckerResults.fs | 3 ++ src/Compiler/Service/FSharpCheckerResults.fsi | 2 + src/Compiler/Service/TransparentCompiler.fs | 38 ++++++++++++------- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index e5278f668bf..98cf78250c1 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -272,6 +272,9 @@ type FSharpProjectSnapshot = .WithoutImplFilesThatHaveSignaturesExceptLastOne.Key.GetVersion() } + member this.GetLastModifiedTimeOnDisk() = + DateTime.Now + interface ICacheKey with member this.GetLabel() = this.ToString() member this.GetKey() = this.ProjectFileName diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 4dc6b84d801..42a75d26d62 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -136,6 +136,8 @@ type FSharpProjectSnapshot = /// A snapshot of the same project with file versions removed. member WithoutFileVersions: FSharpProjectSnapshot + member GetLastModifiedTimeOnDisk: unit -> DateTime + member internal Key: ICacheKey member internal FileKey: fileName: string -> ICacheKey<(string * string), string> diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 81601d1cd33..79a023d38d4 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -501,7 +501,7 @@ type internal TransparentCompiler node { Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) - return! self.GetAssemblyData(projectSnapshot, userOpName + ".CheckReferencedProject(" + nm + ")") + return! self.GetAssemblyData(projectSnapshot, nm, userOpName + ".CheckReferencedProject(" + nm + ")") } member x.TryGetLogicalTimeStamp(cache) = @@ -1458,15 +1458,27 @@ type internal TransparentCompiler } ) - let ComputeAssemblyData (projectSnapshot: FSharpProjectSnapshot) = + let ComputeAssemblyData (projectSnapshot: FSharpProjectSnapshot) fileName = caches.AssemblyData.Get( projectSnapshot.WithoutImplFilesThatHaveSignatures.Key, async { - match! ComputeBootstrapInfo projectSnapshot with - | None, _ -> return ProjectAssemblyDataResult.Unavailable true - | Some bootstrapInfo, _creationDiags -> - let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot - return assemblyDataResult + let availableOnDiskModifiedTime = + if FileSystem.FileExistsShim fileName then + Some <| FileSystem.GetLastWriteTimeShim fileName + else None + + let shouldUseOnDisk = + availableOnDiskModifiedTime + |> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) + + if shouldUseOnDisk then + return ProjectAssemblyDataResult.Unavailable true + else + match! ComputeBootstrapInfo projectSnapshot with + | None, _ -> return ProjectAssemblyDataResult.Unavailable true + | Some bootstrapInfo, _creationDiags -> + let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot + return assemblyDataResult } ) @@ -1642,8 +1654,8 @@ type internal TransparentCompiler | Some itemKeyStore -> return itemKeyStore.FindAll symbol.Item } - member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, _userOpName) = - ComputeAssemblyData projectSnapshot |> NodeCode.AwaitAsync + member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, fileName, _userOpName) = + ComputeAssemblyData projectSnapshot fileName |> NodeCode.AwaitAsync member _.Caches = caches @@ -1737,14 +1749,14 @@ type internal TransparentCompiler member _.FrameworkImportsCache: FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache - member this.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = + member this.GetAssemblyData(options: FSharpProjectOptions, fileName, userOpName: string) : NodeCode = node { let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! this.GetAssemblyData(snapshot, userOpName) + return! this.GetAssemblyData(snapshot, fileName, userOpName) } - member this.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = - this.GetAssemblyData(projectSnapshot, userOpName) + member this.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, fileName, userOpName: string) : NodeCode = + this.GetAssemblyData(projectSnapshot, fileName, userOpName) member this.GetBackgroundCheckResultsForFileInProject ( From 9ed6b12b92019a43fe0e159816699e113d51c5ef Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 5 Sep 2023 13:59:01 +0200 Subject: [PATCH 084/222] more experiments --- src/Compiler/Service/BackgroundCompiler.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index dfb6ce72ffc..7aef64e54fa 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -85,10 +85,10 @@ type internal IBackgroundCompiler = NodeCode> abstract member GetAssemblyData: - options: FSharpProjectOptions * userOpName: string -> NodeCode + options: FSharpProjectOptions * outputFileName: string * userOpName: string -> NodeCode abstract member GetAssemblyData: - projectSnapshot: FSharpProjectSnapshot * userOpName: string -> NodeCode + projectSnapshot: FSharpProjectSnapshot * outputFileName: string * userOpName: string -> NodeCode /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) abstract member GetBackgroundCheckResultsForFileInProject: @@ -1493,10 +1493,10 @@ type internal BackgroundCompiler member _.FrameworkImportsCache: FrameworkImportsCache = self.FrameworkImportsCache - member _.GetAssemblyData(options: FSharpProjectOptions, userOpName: string) : NodeCode = + member _.GetAssemblyData(options: FSharpProjectOptions, _fileName: string, userOpName: string) : NodeCode = self.GetAssemblyData(options, userOpName) - member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = + member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, _fileName: string, userOpName: string) : NodeCode = self.GetAssemblyData(projectSnapshot.ToOptions(), userOpName) member _.GetBackgroundCheckResultsForFileInProject From 8511a6f15608ae7f713f10b2103481880fdf02a8 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 5 Sep 2023 16:27:28 +0200 Subject: [PATCH 085/222] debug output update --- src/Compiler/Service/TransparentCompiler.fs | 13 ++++++++++--- src/Compiler/Service/service.fs | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 79a023d38d4..316f5f000da 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -916,7 +916,7 @@ type internal TransparentCompiler | NodeToTypeCheck.ArtificialImplFile i -> -(i + 1), $"AIF [{i}] : {fileNames[i]}") |> Graph.serialiseToMermaid - Trace.TraceInformation("\n" + debugGraph) + //Trace.TraceInformation("\n" + debugGraph) if Activity.Current <> null then Activity.Current.AddTag("graph", debugGraph) |> ignore @@ -1012,7 +1012,7 @@ type internal TransparentCompiler "ComputeTcIntermediate" [| Activity.Tags.fileName, fileName |> Path.GetFileName - "key", key.GetKey() |> sprintf "%A" + "key", key.GetLabel() "version", key.GetVersion() |] @@ -1471,13 +1471,20 @@ type internal TransparentCompiler availableOnDiskModifiedTime |> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) + let name = projectSnapshot.ProjectFileName |> Path.GetFileNameWithoutExtension + if shouldUseOnDisk then + Trace.TraceInformation($"Using assembly on disk: {name}") return ProjectAssemblyDataResult.Unavailable true else match! ComputeBootstrapInfo projectSnapshot with - | None, _ -> return ProjectAssemblyDataResult.Unavailable true + | None, _ -> + Trace.TraceInformation($"Using assembly on disk (unintentionally): {name}") + return ProjectAssemblyDataResult.Unavailable true | Some bootstrapInfo, _creationDiags -> let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot + Trace.TraceInformation($"Using in-memory project reference: {name}") + return assemblyDataResult } ) diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index e8fc38b54a6..d8682c3e934 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -284,8 +284,8 @@ type FSharpChecker member _.MatchBraces(fileName, sourceText: ISourceText, options: FSharpParsingOptions, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" - use _ = - Activity.start "FSharpChecker.MatchBraces" [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, userOpName |] + //use _ = + // Activity.start "FSharpChecker.MatchBraces" [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, userOpName |] let hash = sourceText.GetHashCode() |> int64 From 1db5b382f2218c65270332ddf0e0a37398cb0288 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 7 Sep 2023 19:10:55 +0200 Subject: [PATCH 086/222] more debug info --- src/Compiler/Facilities/AsyncMemoize.fs | 2 ++ vsintegration/src/FSharp.Editor/Common/Logging.fs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 8f785b459d4..3ccb97cce25 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -582,6 +582,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T member _.Locked = lock.Semaphore.CurrentCount < 1 + member _.Running = cache.GetValues() |> Seq.filter (function Running _ -> true | _ -> false) |> Seq.toArray + member this.DebuggerDisplay = let locked = if this.Locked then " [LOCKED]" else "" diff --git a/vsintegration/src/FSharp.Editor/Common/Logging.fs b/vsintegration/src/FSharp.Editor/Common/Logging.fs index db961a883ae..3cc088c8615 100644 --- a/vsintegration/src/FSharp.Editor/Common/Logging.fs +++ b/vsintegration/src/FSharp.Editor/Common/Logging.fs @@ -93,7 +93,9 @@ module Logging = let inline debug msg = Printf.kprintf Debug.WriteLine msg let private logger = lazy Logger(Logger.GlobalServiceProvider) - let private log logType msg = logger.Value.Log(logType, msg) + let private log logType msg = + logger.Value.Log(logType, msg) + System.Diagnostics.Trace.TraceInformation(msg) let logMsg msg = log LogType.Message msg let logInfo msg = log LogType.Info msg From f1778eb54d593094604f8375287d8c6acf279ffe Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Fri, 8 Sep 2023 16:43:55 +0200 Subject: [PATCH 087/222] wip --- .../FSharp.Editor/Common/CancellableTasks.fs | 7 +- .../src/FSharp.Editor/Common/Extensions.fs | 80 ++- .../src/FSharp.Editor/Common/Pervasive.fs | 2 +- .../LanguageService/WorkspaceExtensions.fs | 6 +- .../Navigation/FindDefinitionService.fs | 10 +- .../Navigation/GoToDefinition.fs | 556 ++++++++++-------- .../Navigation/GoToDefinitionService.fs | 11 +- .../Navigation/NavigateToSearchService.fs | 71 ++- .../Refactor/AddExplicitTypeToParameter.fs | 164 +++--- .../BreakpointResolutionServiceTests.fs | 4 +- .../FindReferencesTests.fs | 8 +- 11 files changed, 555 insertions(+), 364 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs index 6e703fc2c59..7d5ed3205d2 100644 --- a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs +++ b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs @@ -451,13 +451,13 @@ module CancellableTasks = /// /// Builds a cancellableTask using computation expression syntax. - /// Default behaviour when binding (v)options is to return a cacnelled task. + /// Default behaviour when binding (v)options is to return a cancelled task. /// let foregroundCancellableTask = CancellableTaskBuilder(false) /// /// Builds a cancellableTask using computation expression syntax which switches to execute on a background thread if not already doing so. - /// Default behaviour when binding (v)options is to return a cacnelled task. + /// Default behaviour when binding (v)options is to return a cancelled task. /// let cancellableTask = CancellableTaskBuilder(true) @@ -1105,8 +1105,7 @@ module CancellableTasks = return! Task.WhenAll (seq { for task in tasks do yield startTask ct task }) } - let inline ignore (ctask: CancellableTask<_>) = - ctask |> toUnit + let inline ignore ([] ctask: CancellableTask<_>) = toUnit ctask /// [] diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index 3c9d54f4261..6cb1246d21d 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -299,9 +299,65 @@ module ValueOption = | _ -> None [] +module IEnumerator = + let chooseV f (e: IEnumerator<'T>) = + let mutable started = false + let mutable curr = None + + let get () = + if not started then + raise(InvalidOperationException("Not started")) + + match curr with + | None -> + raise(InvalidOperationException("Already finished")) + | Some x -> x + + + { new IEnumerator<'U> with + member _.Current = get () + interface System.Collections.IEnumerator with + member _.Current = box (get ()) + + member _.MoveNext() = + if not started then + started <- true + + curr <- None + + while (curr.IsNone && e.MoveNext()) do + curr <- f e.Current + + Option.isSome curr + + member _.Reset() = + raise(NotSupportedException("Reset is not supported")) + interface System.IDisposable with + member _.Dispose() = + e.Dispose() + } +[] module Seq = - let toImmutableArray (xs: seq<'a>) : ImmutableArray<'a> = xs.ToImmutableArray() + let mkSeq f = + { new IEnumerable<'U> with + member _.GetEnumerator() = f() + + interface System.Collections.IEnumerable with + member _.GetEnumerator() = (f() :> System.Collections.IEnumerator) } + + let inline revamp f (ie: seq<_>) = + mkSeq (fun () -> f (ie.GetEnumerator())) + + let inline toImmutableArray (xs: seq<'a>) : ImmutableArray<'a> = xs.ToImmutableArray() + + let inline tryHeadV (source: seq<_>) = + use e = source.GetEnumerator() + + if (e.MoveNext()) then + ValueSome e.Current + else + ValueNone let inline tryFindV ([] predicate) (source: seq<'T>) = use e = source.GetEnumerator() @@ -326,6 +382,18 @@ module Seq = loop 0 + let inline tryPickV ([] chooser) (source: seq<'T>) = + use e = source.GetEnumerator() + let mutable res = ValueNone + + while (ValueOption.isNone res && e.MoveNext()) do + res <- chooser e.Current + + res + + let chooseV chooser source = + revamp (IEnumerator.chooseV chooser) source + [] module Array = let inline foldi ([] folder: 'State -> int -> 'T -> 'State) (state: 'State) (xs: 'T[]) = @@ -340,6 +408,12 @@ module Array = let toImmutableArray (xs: 'T[]) = xs.ToImmutableArray() + let inline tryHeadV (array: _[]) = + if array.Length = 0 then + ValueNone + else + ValueSome array[0] + let inline tryFindV ([] predicate) (array: _[]) = let rec loop i = @@ -420,6 +494,10 @@ module ImmutableArray = else ValueSome xs[0] + let inline empty<'T> = ImmutableArray<'T>.Empty + + let inline create<'T> (x: 'T) = ImmutableArray.Create<'T>(x) + [] module List = let rec tryFindV predicate list = diff --git a/vsintegration/src/FSharp.Editor/Common/Pervasive.fs b/vsintegration/src/FSharp.Editor/Common/Pervasive.fs index 57f78491e9c..0ad2b8ce8c7 100644 --- a/vsintegration/src/FSharp.Editor/Common/Pervasive.fs +++ b/vsintegration/src/FSharp.Editor/Common/Pervasive.fs @@ -6,7 +6,7 @@ open System.IO open System.Diagnostics /// Checks if the filePath ends with ".fsi" -let isSignatureFile (filePath: string) = +let inline isSignatureFile (filePath: string) = String.Equals(Path.GetExtension filePath, ".fsi", StringComparison.OrdinalIgnoreCase) /// Returns the corresponding signature file path for given implementation file path or vice versa diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index cb93bfe7d4c..77d89a7d7a0 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -258,10 +258,10 @@ type Document with /// Try to find a F# lexer/token symbol of the given F# document and position. member this.TryFindFSharpLexerSymbolAsync(position, lookupKind, wholeActivePattern, allowStringToken, userOpName) = - async { + cancellableTask { let! defines, langVersion, strictIndentation = this.GetFsharpParsingOptionsAsync(userOpName) - let! ct = Async.CancellationToken - let! sourceText = this.GetTextAsync(ct) |> Async.AwaitTask + let! ct = CancellableTask.getCancellationToken () + let! sourceText = this.GetTextAsync(ct) return Tokenizer.getSymbolAtPosition ( diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs index 1864e99c508..0013da2d154 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs @@ -10,15 +10,15 @@ open FSharp.Compiler.Text.Range open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.ExternalAccess.FSharp.GoToDefinition -open System.Collections.Immutable -open System.Threading.Tasks +open CancellableTasks [)>] [)>] type internal FSharpFindDefinitionService [] (metadataAsSource: FSharpMetadataAsSourceService) = interface IFSharpFindDefinitionService with member _.FindDefinitionsAsync(document: Document, position: int, cancellationToken: CancellationToken) = - let navigation = FSharpNavigation(metadataAsSource, document, rangeStartup) + cancellableTask { + let navigation = FSharpNavigation(metadataAsSource, document, rangeStartup) + return! navigation.FindDefinitionsAsync(position) + } |> CancellableTask.start cancellationToken - let definitions = navigation.FindDefinitions(position, cancellationToken) // TODO: probably will need to be async all the way down - ImmutableArray.CreateRange(definitions) |> Task.FromResult diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 2545b367849..74429229bdd 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -185,54 +185,79 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = /// Helper function that is used to determine the navigation strategy to apply, can be tuned towards signatures or implementation files. member private _.FindSymbolHelper(originDocument: Document, originRange: range, sourceText: SourceText, preferSignature: bool) = - asyncMaybe { - let userOpName = "FindSymbolHelper" - let! originTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, originRange) - let position = originTextSpan.Start - let! lexerSymbol = originDocument.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, userOpName) - let textLinePos = sourceText.Lines.GetLinePosition position - let fcsTextLineNumber = Line.fromZ textLinePos.Line - let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - let idRange = lexerSymbol.Ident.idRange + let originTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, originRange) + match originTextSpan with + | ValueNone -> + CancellableTask.singleton None + | ValueSome originTextSpan -> + cancellableTask { + let userOpName = "FindSymbolHelper" - let! ct = Async.CancellationToken |> liftAsync + let position = originTextSpan.Start + let! lexerSymbol = originDocument.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, userOpName) - let! _, checkFileResults = - originDocument.GetFSharpParseAndCheckResultsAsync(nameof (GoToDefinition)) - |> CancellableTask.start ct - |> Async.AwaitTask - |> liftAsync + match lexerSymbol with + | None -> return None + | Some lexerSymbol -> + let textLinePos = sourceText.Lines.GetLinePosition position + let fcsTextLineNumber = Line.fromZ textLinePos.Line + let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() + let idRange = lexerSymbol.Ident.idRange - let! fsSymbolUse = - checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) + let! ct = CancellableTask.getCancellationToken () - let symbol = fsSymbolUse.Symbol - // if the tooltip was spawned in an implementation file and we have a range targeting - // a signature file, try to find the corresponding implementation file and target the - // desired symbol - if isSignatureFile fsSymbolUse.FileName && preferSignature = false then - let fsfilePath = Path.ChangeExtension(originRange.FileName, "fs") + let! _, checkFileResults = + originDocument.GetFSharpParseAndCheckResultsAsync(nameof (GoToDefinition)) - if not (File.Exists fsfilePath) then - return! None - else - let! implDoc = originDocument.Project.Solution.TryGetDocumentFromPath fsfilePath - let! implSourceText = implDoc.GetTextAsync() + let fsSymbolUse = + checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) - let! _, checkFileResults = - implDoc.GetFSharpParseAndCheckResultsAsync(userOpName) - |> CancellableTask.start ct - |> Async.AwaitTask - |> liftAsync - - let symbolUses = checkFileResults.GetUsesOfSymbolInFile symbol - let! implSymbol = symbolUses |> Array.tryHead - let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(implSourceText, implSymbol.Range) - return FSharpGoToDefinitionNavigableItem(implDoc, implTextSpan) - else - let! targetDocument = originDocument.Project.Solution.TryGetDocumentFromFSharpRange fsSymbolUse.Range - return! rangeToNavigableItem (fsSymbolUse.Range, targetDocument) - } + match fsSymbolUse with + | None -> return None + | Some fsSymbolUse -> + + let symbol = fsSymbolUse.Symbol + // if the tooltip was spawned in an implementation file and we have a range targeting + // a signature file, try to find the corresponding implementation file and target the + // desired symbol + if isSignatureFile fsSymbolUse.FileName && preferSignature = false then + let fsfilePath = Path.ChangeExtension(originRange.FileName, "fs") + + if not (File.Exists fsfilePath) then + return None + else + let implDoc = originDocument.Project.Solution.TryGetDocumentFromPath fsfilePath + + match implDoc with + | ValueNone -> return None + | ValueSome implDoc -> + let! implSourceText = implDoc.GetTextAsync(ct) + + let! _, checkFileResults = + implDoc.GetFSharpParseAndCheckResultsAsync(userOpName) + + let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbol, ct) + + let implSymbol = Array.tryHeadV symbolUses + + match implSymbol with + | ValueNone -> return None + | ValueSome implSymbol -> + let implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(implSourceText, implSymbol.Range) + + match implTextSpan with + | ValueNone -> return None + | ValueSome implTextSpan -> + return Some(FSharpGoToDefinitionNavigableItem(implDoc, implTextSpan)) + else + let targetDocument = originDocument.Project.Solution.TryGetDocumentFromFSharpRange fsSymbolUse.Range + + match targetDocument with + | None -> return None + | Some targetDocument -> + let! navItem = rangeToNavigableItem (fsSymbolUse.Range, targetDocument) + return navItem + } /// if the symbol is defined in the given file, return its declaration location, otherwise use the targetSymbol to find the first /// instance of its presence in the provided source file. The first case is needed to return proper declaration location for @@ -256,9 +281,10 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = return implSymbol.Range } - member private this.FindDefinitionAtPosition(originDocument: Document, position: int, cancellationToken: CancellationToken) = - asyncMaybe { + member internal this.FindDefinitionAtPosition(originDocument: Document, position: int) = + cancellableTask { let userOpName = "FindDefinitionAtPosition" + let! cancellationToken = CancellableTask.getCancellationToken () let! sourceText = originDocument.GetTextAsync(cancellationToken) let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position @@ -266,131 +292,179 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - let! ct = Async.CancellationToken |> liftAsync + let! cancellationToken = CancellableTask.getCancellationToken () let preferSignature = isSignatureFile originDocument.FilePath let! lexerSymbol = originDocument.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, userOpName) - let idRange = lexerSymbol.Ident.idRange - - let! _, checkFileResults = - originDocument.GetFSharpParseAndCheckResultsAsync(userOpName) - |> CancellableTask.start ct - |> Async.AwaitTask - |> liftAsync - - let declarations = - checkFileResults.GetDeclarationLocation( - fcsTextLineNumber, - lexerSymbol.Ident.idRange.EndColumn, - textLineString, - lexerSymbol.FullIsland, - preferSignature - ) + + match lexerSymbol with + | None -> return ValueNone + | Some lexerSymbol -> + + let idRange = lexerSymbol.Ident.idRange - let! targetSymbolUse = - checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) - - match declarations with - | FindDeclResult.ExternalDecl (assembly, targetExternalSym) -> - let projectOpt = - originDocument.Project.Solution.Projects - |> Seq.tryFindV (fun p -> p.AssemblyName.Equals(assembly, StringComparison.OrdinalIgnoreCase)) - - match projectOpt with - | ValueSome project -> - let! symbols = SymbolFinder.FindSourceDeclarationsAsync(project, (fun _ -> true)) - - let roslynSymbols = - symbols |> Seq.collect ExternalSymbol.ofRoslynSymbol |> Array.ofSeq - - let! symbol = - roslynSymbols - |> Seq.tryPick (fun (sym, externalSym) -> if externalSym = targetExternalSym then Some sym else None) - - let! location = symbol.Locations |> Seq.tryHead - - return - (FSharpGoToDefinitionResult.NavigableItem( - FSharpGoToDefinitionNavigableItem(project.GetDocument(location.SourceTree), location.SourceSpan) - ), - idRange) - | _ -> - let metadataReferences = originDocument.Project.MetadataReferences - return (FSharpGoToDefinitionResult.ExternalAssembly(targetSymbolUse, metadataReferences), idRange) - - | FindDeclResult.DeclFound targetRange -> - // If the file is not associated with a document, it's considered external. - if not (originDocument.Project.Solution.ContainsDocumentWithFilePath(targetRange.FileName)) then - let metadataReferences = originDocument.Project.MetadataReferences - return (FSharpGoToDefinitionResult.ExternalAssembly(targetSymbolUse, metadataReferences), idRange) - else - // if goto definition is called at we are alread at the declaration location of a symbol in - // either a signature or an implementation file then we jump to it's respective postion in thethe - if - lexerSymbol.Range = targetRange - then - // jump from signature to the corresponding implementation - if isSignatureFile originDocument.FilePath then - let implFilePath = Path.ChangeExtension(originDocument.FilePath, "fs") - - if not (File.Exists implFilePath) then - return! None - else - let! implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath - - let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument) - let! implSourceText = implDocument.GetTextAsync(cancellationToken) |> liftTaskAsync - let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(implSourceText, targetRange) - let navItem = FSharpGoToDefinitionNavigableItem(implDocument, implTextSpan) - return (FSharpGoToDefinitionResult.NavigableItem(navItem), idRange) - else // jump from implementation to the corresponding signature - let declarations = - checkFileResults.GetDeclarationLocation( - fcsTextLineNumber, - lexerSymbol.Ident.idRange.EndColumn, - textLineString, - lexerSymbol.FullIsland, - true - ) - - match declarations with - | FindDeclResult.DeclFound targetRange -> - let! sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName - let! sigSourceText = sigDocument.GetTextAsync(cancellationToken) |> liftTaskAsync - let! sigTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sigSourceText, targetRange) - let navItem = FSharpGoToDefinitionNavigableItem(sigDocument, sigTextSpan) - return (FSharpGoToDefinitionResult.NavigableItem(navItem), idRange) - | _ -> return! None - // when the target range is different follow the navigation convention of - // - gotoDefn origin = signature , gotoDefn destination = signature - // - gotoDefn origin = implementation, gotoDefn destination = implementation - else - let! sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName - let! sigSourceText = sigDocument.GetTextAsync(cancellationToken) |> liftTaskAsync - let! sigTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sigSourceText, targetRange) - // if the gotodef call originated from a signature and the returned target is a signature, navigate there - if isSignatureFile targetRange.FileName && preferSignature then - let navItem = FSharpGoToDefinitionNavigableItem(sigDocument, sigTextSpan) - return (FSharpGoToDefinitionResult.NavigableItem(navItem), idRange) - else // we need to get an FSharpSymbol from the targetRange found in the signature - // that symbol will be used to find the destination in the corresponding implementation file - let implFilePath = - // Bugfix: apparently sigDocument not always is a signature file - if isSignatureFile sigDocument.FilePath then - Path.ChangeExtension(sigDocument.FilePath, "fs") - else - sigDocument.FilePath - - let! implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath - - let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument) + let! _, checkFileResults = + originDocument.GetFSharpParseAndCheckResultsAsync(userOpName) + + let declarations = + checkFileResults.GetDeclarationLocation( + fcsTextLineNumber, + idRange.EndColumn, + textLineString, + lexerSymbol.FullIsland, + preferSignature + ) - let! implSourceText = implDocument.GetTextAsync() |> liftTaskAsync - let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(implSourceText, targetRange) - let navItem = FSharpGoToDefinitionNavigableItem(implDocument, implTextSpan) - return (FSharpGoToDefinitionResult.NavigableItem(navItem), idRange) - | _ -> return! None + let targetSymbolUse = + checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) + + match targetSymbolUse with + | None -> return ValueNone + | Some targetSymbolUse -> + + match declarations with + | FindDeclResult.ExternalDecl (assembly, targetExternalSym) -> + let projectOpt = + originDocument.Project.Solution.Projects + |> Seq.tryFindV (fun p -> p.AssemblyName.Equals(assembly, StringComparison.OrdinalIgnoreCase)) + + match projectOpt with + | ValueSome project -> + let! symbols = SymbolFinder.FindSourceDeclarationsAsync(project, (fun _ -> true), cancellationToken) + + let roslynSymbols = + Seq.collect ExternalSymbol.ofRoslynSymbol symbols + + let symbol = + Seq.tryPickV (fun (sym, externalSym) -> if externalSym = targetExternalSym then ValueSome sym else ValueNone) roslynSymbols + + let location = + symbol + |> ValueOption.map _.Locations + |> ValueOption.bind Seq.tryHeadV + + match location with + | ValueNone -> return ValueNone + | ValueSome location -> + + return + ValueSome ( + FSharpGoToDefinitionResult.NavigableItem( + FSharpGoToDefinitionNavigableItem(project.GetDocument(location.SourceTree), location.SourceSpan) + ), idRange) + | _ -> + let metadataReferences = originDocument.Project.MetadataReferences + return ValueSome (FSharpGoToDefinitionResult.ExternalAssembly(targetSymbolUse, metadataReferences), idRange) + + | FindDeclResult.DeclFound targetRange -> + // If the file is not associated with a document, it's considered external. + if not (originDocument.Project.Solution.ContainsDocumentWithFilePath(targetRange.FileName)) then + let metadataReferences = originDocument.Project.MetadataReferences + return ValueSome (FSharpGoToDefinitionResult.ExternalAssembly(targetSymbolUse, metadataReferences), idRange) + else + // if goto definition is called at we are alread at the declaration location of a symbol in + // either a signature or an implementation file then we jump to it's respective postion in thethe + if + lexerSymbol.Range = targetRange + then + // jump from signature to the corresponding implementation + if isSignatureFile originDocument.FilePath then + let implFilePath = Path.ChangeExtension(originDocument.FilePath, "fs") + + if not (File.Exists implFilePath) then + return ValueNone + else + let implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath + + match implDocument with + | ValueNone -> return ValueNone + | ValueSome implDocument -> + let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument) + + match targetRange with + | None -> return ValueNone + | Some targetRange -> + let! implSourceText = implDocument.GetTextAsync(cancellationToken) + let implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(implSourceText, targetRange) + + match implTextSpan with + | ValueNone -> return ValueNone + | ValueSome implTextSpan -> + let navItem = FSharpGoToDefinitionNavigableItem(implDocument, implTextSpan) + return ValueSome(FSharpGoToDefinitionResult.NavigableItem(navItem), idRange) + + else // jump from implementation to the corresponding signature + let declarations = + checkFileResults.GetDeclarationLocation( + fcsTextLineNumber, + idRange.EndColumn, + textLineString, + lexerSymbol.FullIsland, + true + ) + + match declarations with + | FindDeclResult.DeclFound targetRange -> + let sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName + + match sigDocument with + | ValueNone -> return ValueNone + | ValueSome sigDocument -> + let! sigSourceText = sigDocument.GetTextAsync(cancellationToken) + + let sigTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sigSourceText, targetRange) + + match sigTextSpan with + | ValueNone -> return ValueNone + | ValueSome sigTextSpan -> + let navItem = FSharpGoToDefinitionNavigableItem(sigDocument, sigTextSpan) + return ValueSome(FSharpGoToDefinitionResult.NavigableItem(navItem), idRange) + | _ -> return ValueNone + // when the target range is different follow the navigation convention of + // - gotoDefn origin = signature , gotoDefn destination = signature + // - gotoDefn origin = implementation, gotoDefn destination = implementation + else + let sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName + match sigDocument with + | ValueNone -> return ValueNone + | ValueSome sigDocument -> + let! sigSourceText = sigDocument.GetTextAsync(cancellationToken) + let sigTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sigSourceText, targetRange) + match sigTextSpan with + | ValueNone -> return ValueNone + | ValueSome sigTextSpan -> + // if the gotodef call originated from a signature and the returned target is a signature, navigate there + if isSignatureFile targetRange.FileName && preferSignature then + let navItem = FSharpGoToDefinitionNavigableItem(sigDocument, sigTextSpan) + return ValueSome(FSharpGoToDefinitionResult.NavigableItem(navItem), idRange) + else // we need to get an FSharpSymbol from the targetRange found in the signature + // that symbol will be used to find the destination in the corresponding implementation file + let implFilePath = + // Bugfix: apparently sigDocument not always is a signature file + if isSignatureFile sigDocument.FilePath then + Path.ChangeExtension(sigDocument.FilePath, "fs") + else + sigDocument.FilePath + + let implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath + + match implDocument with + | ValueNone -> return ValueNone + | ValueSome implDocument -> + let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument) + + match targetRange with + | None -> return ValueNone + | Some targetRange -> + let! implSourceText = implDocument.GetTextAsync(cancellationToken) + let implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(implSourceText, targetRange) + match implTextSpan with + | ValueNone -> return ValueNone + | ValueSome implTextSpan -> + let navItem = FSharpGoToDefinitionNavigableItem(implDocument, implTextSpan) + return ValueSome(FSharpGoToDefinitionResult.NavigableItem(navItem), idRange) + | _ -> return ValueNone } /// find the declaration location (signature file/.fsi) of the target symbol if possible, fall back to definition @@ -401,16 +475,11 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = member this.FindDefinitionOfSymbolAtRange(targetDocument: Document, symbolRange: range, targetSourceText: SourceText) = this.FindSymbolHelper(targetDocument, symbolRange, targetSourceText, preferSignature = false) - member this.FindDefinitionsForPeekTask(originDocument: Document, position: int, cancellationToken: CancellationToken) = - this.FindDefinitionAtPosition(originDocument, position, cancellationToken) - |> Async.map (Option.toArray >> Array.toSeq) - |> RoslynHelpers.StartAsyncAsTask cancellationToken - /// Construct a task that will return a navigation target for the implementation definition of the symbol /// at the provided position in the document. member this.FindDefinitionTask(originDocument: Document, position: int, cancellationToken: CancellationToken) = - this.FindDefinitionAtPosition(originDocument, position, cancellationToken) - |> RoslynHelpers.StartAsyncAsTask cancellationToken + this.FindDefinitionAtPosition(originDocument, position) + |> CancellableTask.start cancellationToken /// Navigate to the positon of the textSpan in the provided document /// used by quickinfo link navigation when the tooltip contains the correct destination range. @@ -452,17 +521,24 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = else statusBar.TempMessage(SR.CannotNavigateUnknown()) + result + /// Find the declaration location (signature file/.fsi) of the target symbol if possible, fall back to definition member this.NavigateToSymbolDeclarationAsync ( targetDocument: Document, targetSourceText: SourceText, - symbolRange: range, - cancellationToken: CancellationToken + symbolRange: range ) = - asyncMaybe { + cancellableTask { let! item = this.FindDeclarationOfSymbolAtRange(targetDocument, symbolRange, targetSourceText) - return this.NavigateToItem(item, cancellationToken) + + match item with + | None -> + return false + | Some item -> + let! cancellationToken = CancellableTask.getCancellationToken () + return this.NavigateToItem(item, cancellationToken) } /// Find the definition location (implementation file/.fs) of the target symbol @@ -470,12 +546,16 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = ( targetDocument: Document, targetSourceText: SourceText, - symbolRange: range, - cancellationToken: CancellationToken + symbolRange: range ) = - asyncMaybe { + cancellableTask { let! item = this.FindDefinitionOfSymbolAtRange(targetDocument, symbolRange, targetSourceText) - return this.NavigateToItem(item, cancellationToken) + match item with + | None -> + return false + | Some item -> + let! cancellationToken = CancellableTask.getCancellationToken () + return this.NavigateToItem(item, cancellationToken) } member this.NavigateToExternalDeclaration @@ -623,33 +703,40 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, ThreadHelper.JoinableTaskFactory.Run( SR.NavigatingTo(), (fun _progress cancellationToken -> - Async.StartImmediateAsTask( - asyncMaybe { - let! targetDoc = solution.TryGetDocumentFromFSharpRange(range, initialDoc.Project.Id) + cancellableTask { + let targetDoc = solution.TryGetDocumentFromFSharpRange(range, initialDoc.Project.Id) + + match targetDoc with + | None -> () + | Some targetDoc -> + + let! cancellationToken = CancellableTask.getCancellationToken () + let! targetSource = targetDoc.GetTextAsync(cancellationToken) - let! targetTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(targetSource, range) - let gtd = GoToDefinition(metadataAsSource) - - // Whenever possible: - // - signature files (.fsi) should navigate to other signature files - // - implementation files (.fs) should navigate to other implementation files - if isSignatureFile initialDoc.FilePath then - // Target range will point to .fsi file if only there is one so we can just use Roslyn navigation service. - return gtd.TryNavigateToTextSpan(targetDoc, targetTextSpan, cancellationToken) - else - // Navigation request was made in a .fs file, so we try to find the implmentation of the symbol at target range. - // This is the part that may take some time, because of type checks involved. - let! result = - gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, range, cancellationToken) - |> liftAsync - - if result.IsNone then - // In case the above fails, we just navigate to target range. - return gtd.TryNavigateToTextSpan(targetDoc, targetTextSpan, cancellationToken) - } - |> Async.Ignore, - cancellationToken - )), + let targetTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(targetSource, range) + match targetTextSpan with + | ValueNone -> () + | ValueSome targetTextSpan -> + + let gtd = GoToDefinition(metadataAsSource) + + // Whenever possible: + // - signature files (.fsi) should navigate to other signature files + // - implementation files (.fs) should navigate to other implementation files + if isSignatureFile initialDoc.FilePath then + // Target range will point to .fsi file if only there is one so we can just use Roslyn navigation service. + do gtd.TryNavigateToTextSpan(targetDoc, targetTextSpan, cancellationToken) + else + // Navigation request was made in a .fs file, so we try to find the implmentation of the symbol at target range. + // This is the part that may take some time, because of type checks involved. + let! result = + gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, range) + + if not result then + // In case the above fails, we just navigate to target range. + do gtd.TryNavigateToTextSpan(targetDoc, targetTextSpan, cancellationToken) + } |> CancellableTask.start cancellationToken + ), // Default wait time before VS shows the dialog allowing to cancel the long running task is 2 seconds. // This seems a bit too long to leave the user without any feedback, so we shorten it to 1 second. // Note: it seems anything less than 1 second will get rounded down to zero, resulting in flashing dialog @@ -659,17 +746,16 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, with :? OperationCanceledException -> () - member _.FindDefinitions(position, cancellationToken) = - let gtd = GoToDefinition(metadataAsSource) - let task = gtd.FindDefinitionsForPeekTask(initialDoc, position, cancellationToken) - task.Wait(cancellationToken) - let results = task.Result + member _.FindDefinitionsAsync(position) = + cancellableTask { + let gtd = GoToDefinition(metadataAsSource) + let! result = gtd.FindDefinitionAtPosition(initialDoc, position) - results - |> Seq.choose (fun (result, _) -> - match result with - | FSharpGoToDefinitionResult.NavigableItem (navItem) -> Some navItem - | _ -> None) + return + match result with + | ValueSome (FSharpGoToDefinitionResult.NavigableItem (navItem), _) -> ImmutableArray.create navItem + | _ -> ImmutableArray.empty + } member _.TryGoToDefinition(position, cancellationToken) = let gtd = GoToDefinition(metadataAsSource) @@ -739,24 +825,32 @@ type internal DocCommentId = type FSharpNavigableLocation(metadataAsSource: FSharpMetadataAsSourceService, symbolRange: range, project: Project) = interface IFSharpNavigableLocation with member _.NavigateToAsync(_options: FSharpNavigationOptions2, cancellationToken: CancellationToken) : Task = - asyncMaybe { + cancellableTask { let targetPath = symbolRange.FileName - let! targetDoc = project.Solution.TryGetDocumentFromFSharpRange(symbolRange, project.Id) - let! targetSource = targetDoc.GetTextAsync(cancellationToken) - let gtd = GoToDefinition(metadataAsSource) - - let (|Signature|Implementation|) filepath = - if isSignatureFile filepath then - Signature - else - Implementation - - match targetPath with - | Signature -> return! gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, symbolRange, cancellationToken) - | Implementation -> return! gtd.NavigateToSymbolDeclarationAsync(targetDoc, targetSource, symbolRange, cancellationToken) - } - |> Async.map Option.isSome - |> RoslynHelpers.StartAsyncAsTask cancellationToken + + let! cancellationToken = CancellableTask.getCancellationToken () + + let targetDoc = project.Solution.TryGetDocumentFromFSharpRange(symbolRange, project.Id) + + match targetDoc with + | None -> + return false + | Some targetDoc -> + let! targetSource = targetDoc.GetTextAsync(cancellationToken) + let gtd = GoToDefinition(metadataAsSource) + + let (|Signature|Implementation|) filepath = + if isSignatureFile filepath then + Signature + else + Implementation + + match targetPath with + | Signature -> + return! gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, symbolRange) + | Implementation -> + return! gtd.NavigateToSymbolDeclarationAsync(targetDoc, targetSource, symbolRange) + } |> CancellableTask.start cancellationToken [)>] [)>] diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index a3a800c9ef6..1cea931c17d 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -4,12 +4,13 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Composition open System.Threading -open System.Threading.Tasks open FSharp.Compiler.Text.Range open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor +open CancellableTasks +open System.Collections.Generic [)>] [)>] @@ -18,9 +19,11 @@ type internal FSharpGoToDefinitionService [] (metadataAsSo interface IFSharpGoToDefinitionService with /// Invoked with Peek Definition. member _.FindDefinitionsAsync(document: Document, position: int, cancellationToken: CancellationToken) = - let navigation = FSharpNavigation(metadataAsSource, document, rangeStartup) - - navigation.FindDefinitions(position, cancellationToken) |> Task.FromResult + cancellableTask { + let navigation = FSharpNavigation(metadataAsSource, document, rangeStartup) + let! res = navigation.FindDefinitionsAsync(position) + return (res :> IEnumerable<_>) + } |> CancellableTask.start cancellationToken /// Invoked with Go to Definition. /// Try to navigate to the definiton of the symbol at the symbolRange in the originDocument diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index b0bb007f161..07a3e52f8bc 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -135,46 +135,50 @@ type internal FSharpNavigateToSearchService [] if item.NeedsBackticks then match name.IndexOf(searchPattern, StringComparison.CurrentCultureIgnoreCase) with - | i when i > 0 -> PatternMatch(PatternMatchKind.Substring, false, false) |> Some - | 0 when name.Length = searchPattern.Length -> PatternMatch(PatternMatchKind.Exact, false, false) |> Some - | 0 -> PatternMatch(PatternMatchKind.Prefix, false, false) |> Some - | _ -> None + | i when i > 0 -> ValueSome(PatternMatch(PatternMatchKind.Substring, false, false)) + | 0 when name.Length = searchPattern.Length -> ValueSome(PatternMatch(PatternMatchKind.Exact, false, false)) + | 0 -> ValueSome(PatternMatch(PatternMatchKind.Prefix, false, false)) + | _ -> ValueNone else // full name with dots allows for path matching, e.g. // "f.c.so.elseif" will match "Fantomas.Core.SyntaxOak.ElseIfNode" - patternMatcher.TryMatch $"{item.Container.FullName}.{name}" |> Option.ofNullable + patternMatcher.TryMatch $"{item.Container.FullName}.{name}" |> ValueOption.ofNullable - let processDocument (tryMatch: NavigableItem -> PatternMatch option) (kinds: IImmutableSet) (document: Document) = + let processDocument (tryMatch: NavigableItem -> PatternMatch voption) (kinds: IImmutableSet) (document: Document) = cancellableTask { let! ct = CancellableTask.getCancellationToken () let! sourceText = document.GetTextAsync ct - let processItem (item: NavigableItem) = - asyncMaybe { // TODO: make a flat cancellable task - - do! Option.guard (kinds.Contains(navigateToItemKindToRoslynKind item.Kind)) - - let! m = tryMatch item - - let! sourceSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, item.Range) - let glyph = navigateToItemKindToGlyph item.Kind - let kind = navigateToItemKindToRoslynKind item.Kind - let additionalInfo = formatInfo item.Container document - - return - FSharpNavigateToSearchResult( - additionalInfo, - kind, - patternMatchKindToNavigateToMatchKind m.Kind, - item.Name, - FSharpNavigableItem(glyph, ImmutableArray.Create(TaggedText(TextTags.Text, item.Name)), document, sourceSpan) - ) - } - let! items = getNavigableItems document - let! processed = items |> Seq.map processItem |> Async.Parallel - return processed |> Array.choose id + + let processed = + [| + for item in items do + let contains = kinds.Contains (navigateToItemKindToRoslynKind item.Kind) + let patternMatch = tryMatch item + match contains, patternMatch with + | true, ValueSome m -> + let sourceSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, item.Range) + match sourceSpan with + | ValueNone -> () + | ValueSome sourceSpan -> + let glyph = navigateToItemKindToGlyph item.Kind + let kind = navigateToItemKindToRoslynKind item.Kind + let additionalInfo = formatInfo item.Container document + + yield + FSharpNavigateToSearchResult( + additionalInfo, + kind, + patternMatchKindToNavigateToMatchKind m.Kind, + item.Name, + FSharpNavigableItem(glyph, ImmutableArray.Create(TaggedText(TextTags.Text, item.Name)), document, sourceSpan) + ) + | _ -> () + |] + + return processed } interface IFSharpNavigateToSearchService with @@ -190,7 +194,10 @@ type internal FSharpNavigateToSearchService [] let tryMatch = createMatcherFor searchPattern let tasks = - Seq.map (fun doc -> processDocument tryMatch kinds doc) project.Documents + [| + for doc in project.Documents do + yield processDocument tryMatch kinds doc + |] let! results = CancellableTask.whenAll tasks @@ -214,7 +221,7 @@ type internal FSharpNavigateToSearchService [] ) : Task> = cancellableTask { let! result = processDocument (createMatcherFor searchPattern) kinds document - return result |> Array.toImmutableArray + return Array.toImmutableArray result } |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs index a018bb52558..527da6f83e7 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs @@ -6,7 +6,6 @@ open System open System.Composition open System.Threading -open FSharp.Compiler open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols open FSharp.Compiler.Text @@ -22,16 +21,18 @@ type internal FSharpAddExplicitTypeToParameterRefactoring [ liftTaskAsync + + let! ct = CancellableTask.getCancellationToken () + + let! sourceText = document.GetTextAsync(ct) + let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! ct = Async.CancellationToken |> liftAsync - let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync( position, @@ -41,79 +42,88 @@ type internal FSharpAddExplicitTypeToParameterRefactoring [ CancellableTask.start ct - |> Async.AwaitTask - |> liftAsync - - let! symbolUse = - checkFileResults.GetSymbolUseAtLocation( - fcsTextLineNumber, - lexerSymbol.Ident.idRange.EndColumn, - textLine.ToString(), - lexerSymbol.FullIsland - ) + match lexerSymbol with + | None -> return () + | Some lexerSymbol -> + + let! parseFileResults, checkFileResults = + document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpAddExplicitTypeToParameterRefactoring)) - let isValidParameterWithoutTypeAnnotation (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = - let isLambdaIfFunction = - funcOrValue.IsFunction - && parseFileResults.IsBindingALambdaAtPosition symbolUse.Range.Start - - (funcOrValue.IsValue || isLambdaIfFunction) - && parseFileResults.IsPositionContainedInACurriedParameter symbolUse.Range.Start - && not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start) - && not funcOrValue.IsMember - && not funcOrValue.IsMemberThisValue - && not funcOrValue.IsConstructorThisValue - && not (PrettyNaming.IsOperatorDisplayName funcOrValue.DisplayName) - - match symbolUse.Symbol with - | :? FSharpMemberOrFunctionOrValue as v when isValidParameterWithoutTypeAnnotation v symbolUse -> - let typeString = v.FullType.FormatWithConstraints symbolUse.DisplayContext - let title = SR.AddTypeAnnotation() - - let! symbolSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) - - let alreadyWrappedInParens = - let rec leftLoop ch pos = - if not (Char.IsWhiteSpace(ch)) then - ch = '(' - else - leftLoop sourceText.[pos - 1] (pos - 1) - - let rec rightLoop ch pos = - if not (Char.IsWhiteSpace(ch)) then - ch = ')' - else - rightLoop sourceText.[pos + 1] (pos + 1) - - let hasLeftParen = leftLoop sourceText.[symbolSpan.Start - 1] (symbolSpan.Start - 1) - let hasRightParen = rightLoop sourceText.[symbolSpan.End] symbolSpan.End - hasLeftParen && hasRightParen - - let getChangedText (sourceText: SourceText) = - if alreadyWrappedInParens then - sourceText.WithChanges(TextChange(TextSpan(symbolSpan.End, 0), ": " + typeString)) - else - sourceText - .WithChanges(TextChange(TextSpan(symbolSpan.Start, 0), "(")) - .WithChanges(TextChange(TextSpan(symbolSpan.End + 1, 0), ": " + typeString + ")")) - - let codeAction = - CodeAction.Create( - title, - (fun (cancellationToken: CancellationToken) -> - async { - let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask - return context.Document.WithText(getChangedText sourceText) - } - |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), - title + let symbolUse = + checkFileResults.GetSymbolUseAtLocation( + fcsTextLineNumber, + lexerSymbol.Ident.idRange.EndColumn, + textLine.ToString(), + lexerSymbol.FullIsland ) - context.RegisterRefactoring(codeAction) - | _ -> () + let isValidParameterWithoutTypeAnnotation (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = + let isLambdaIfFunction = + funcOrValue.IsFunction + && parseFileResults.IsBindingALambdaAtPosition symbolUse.Range.Start + + (funcOrValue.IsValue || isLambdaIfFunction) + && parseFileResults.IsPositionContainedInACurriedParameter symbolUse.Range.Start + && not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start) + && not funcOrValue.IsMember + && not funcOrValue.IsMemberThisValue + && not funcOrValue.IsConstructorThisValue + && not (PrettyNaming.IsOperatorDisplayName funcOrValue.DisplayName) + + match symbolUse with + | None -> return () + | Some symbolUse -> + + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue as v when isValidParameterWithoutTypeAnnotation v symbolUse -> + let typeString = v.FullType.FormatWithConstraints symbolUse.DisplayContext + let title = SR.AddTypeAnnotation() + + let symbolSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) + + match symbolSpan with + | ValueNone -> return () + | ValueSome symbolSpan -> + + let alreadyWrappedInParens = + let rec leftLoop ch pos = + if not (Char.IsWhiteSpace(ch)) then + ch = '(' + else + leftLoop sourceText.[pos - 1] (pos - 1) + + let rec rightLoop ch pos = + if not (Char.IsWhiteSpace(ch)) then + ch = ')' + else + rightLoop sourceText.[pos + 1] (pos + 1) + + let hasLeftParen = leftLoop sourceText.[symbolSpan.Start - 1] (symbolSpan.Start - 1) + let hasRightParen = rightLoop sourceText.[symbolSpan.End] symbolSpan.End + hasLeftParen && hasRightParen + + let getChangedText (sourceText: SourceText) = + if alreadyWrappedInParens then + sourceText.WithChanges(TextChange(TextSpan(symbolSpan.End, 0), ": " + typeString)) + else + sourceText + .WithChanges(TextChange(TextSpan(symbolSpan.Start, 0), "(")) + .WithChanges(TextChange(TextSpan(symbolSpan.End + 1, 0), ": " + typeString + ")")) + + let codeAction = + CodeAction.Create( + title, + (fun (cancellationToken: CancellationToken) -> + cancellableTask { + let! ct = CancellableTask.getCancellationToken () + let! sourceText = context.Document.GetTextAsync(ct) + return context.Document.WithText(getChangedText sourceText) + } + |> CancellableTask.start cancellationToken), + title + ) + + context.RegisterRefactoring(codeAction) + | _ -> () } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + |> CancellableTask.startAsTask context.CancellationToken diff --git a/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs index 08a73dc2816..cd01e6434d7 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs @@ -66,8 +66,8 @@ let main argv = task.Result match actualResolutionOption with - | None -> Assert.True(expectedResolution.IsNone, "BreakpointResolutionService failed to resolve breakpoint position") - | Some (actualResolutionRange) -> + | ValueNone -> Assert.True(expectedResolution.IsNone, "BreakpointResolutionService failed to resolve breakpoint position") + | ValueSome (actualResolutionRange) -> let actualResolution = sourceText .GetSubText(RoslynHelpers.FSharpRangeToTextSpan(sourceText, actualResolutionRange)) diff --git a/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs index 7f3d4d0e804..4a10dacc6c6 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/FindReferencesTests.fs @@ -71,7 +71,7 @@ module FindReferences = let document = solution.TryGetDocumentFromPath documentPath - |> Option.defaultWith (fun _ -> failwith "Document not found") + |> ValueOption.defaultWith (fun _ -> failwith "Document not found") findUsagesService .FindReferencesAsync(document, getPositionOf "funcParam" documentPath, context) @@ -94,7 +94,7 @@ module FindReferences = let document = solution.TryGetDocumentFromPath documentPath - |> Option.defaultWith (fun _ -> failwith "Document not found") + |> ValueOption.defaultWith (fun _ -> failwith "Document not found") findUsagesService .FindReferencesAsync(document, getPositionOf "funcParam" documentPath, context) @@ -116,7 +116,7 @@ module FindReferences = let document = solution.TryGetDocumentFromPath documentPath - |> Option.defaultWith (fun _ -> failwith "Document not found") + |> ValueOption.defaultWith (fun _ -> failwith "Document not found") findUsagesService .FindReferencesAsync(document, getPositionOf "sharedFunc" documentPath, context) @@ -157,7 +157,7 @@ module FindReferences = let document = solution2.TryGetDocumentFromPath documentPath - |> Option.defaultWith (fun _ -> failwith "Document not found") + |> ValueOption.defaultWith (fun _ -> failwith "Document not found") findUsagesService .FindReferencesAsync(document, getPositionOf operator documentPath, context) From a94bb5fbe611bf72852fabb53c179bd58d820c08 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Fri, 8 Sep 2023 16:58:06 +0200 Subject: [PATCH 088/222] wip --- .../src/FSharp.Editor/FSharp.Editor.fsproj | 2 +- .../Navigation/GoToDefinition.fs | 23 +++++++++---------- .../Navigation/NavigableSymbolsService.fs | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index b73a33b690d..70d9291c092 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -18,7 +18,7 @@ - + true Microsoft.VisualStudio.FSharp.Editor.SR diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 74429229bdd..018ee7e1859 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -297,11 +297,11 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let preferSignature = isSignatureFile originDocument.FilePath let! lexerSymbol = originDocument.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, userOpName) - + match lexerSymbol with | None -> return ValueNone | Some lexerSymbol -> - + let idRange = lexerSymbol.Ident.idRange let! _, checkFileResults = @@ -340,8 +340,8 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = Seq.tryPickV (fun (sym, externalSym) -> if externalSym = targetExternalSym then ValueSome sym else ValueNone) roslynSymbols let location = - symbol - |> ValueOption.map _.Locations + symbol + |> ValueOption.map (fun s -> s.Locations) |> ValueOption.bind Seq.tryHeadV match location with @@ -407,7 +407,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = match declarations with | FindDeclResult.DeclFound targetRange -> let sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName - + match sigDocument with | ValueNone -> return ValueNone | ValueSome sigDocument -> @@ -532,7 +532,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = ) = cancellableTask { let! item = this.FindDeclarationOfSymbolAtRange(targetDocument, symbolRange, targetSourceText) - + match item with | None -> return false @@ -678,7 +678,6 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let navItem = FSharpGoToDefinitionNavigableItem(tmpShownDoc, span) this.NavigateToItem(navItem, cancellationToken) - true | _ -> false | _ -> false @@ -705,7 +704,7 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, (fun _progress cancellationToken -> cancellableTask { let targetDoc = solution.TryGetDocumentFromFSharpRange(range, initialDoc.Project.Id) - + match targetDoc with | None -> () | Some targetDoc -> @@ -716,7 +715,7 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, let targetTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(targetSource, range) match targetTextSpan with | ValueNone -> () - | ValueSome targetTextSpan -> + | ValueSome targetTextSpan -> let gtd = GoToDefinition(metadataAsSource) @@ -774,7 +773,7 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, if gtdTask.Status = TaskStatus.RanToCompletion && gtdTask.Result.IsSome then match gtdTask.Result.Value with | FSharpGoToDefinitionResult.NavigableItem (navItem), _ -> - gtd.NavigateToItem(navItem, cancellationToken) + gtd.NavigateToItem(navItem, cancellationToken) |> ignore // 'true' means do it, like Sheev Palpatine would want us to. true | FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences), _ -> @@ -831,9 +830,9 @@ type FSharpNavigableLocation(metadataAsSource: FSharpMetadataAsSourceService, sy let! cancellationToken = CancellableTask.getCancellationToken () let targetDoc = project.Solution.TryGetDocumentFromFSharpRange(symbolRange, project.Id) - + match targetDoc with - | None -> + | None -> return false | Some targetDoc -> let! targetSource = targetDoc.GetTextAsync(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs index ab7a4b6a29a..32a4d8a9bab 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs @@ -21,7 +21,7 @@ open Microsoft.VisualStudio.Telemetry type internal FSharpNavigableSymbol(item: FSharpNavigableItem, span: SnapshotSpan, gtd: GoToDefinition) = interface INavigableSymbol with member _.Navigate(_: INavigableRelationship) = - gtd.NavigateToItem(item, CancellationToken.None) + gtd.NavigateToItem(item, CancellationToken.None) |> ignore member _.Relationships = seq { yield PredefinedNavigableRelationships.Definition } From c24a1a368dd2b8d1bffe415b94c5de9d291dba2b Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Fri, 8 Sep 2023 18:06:29 +0200 Subject: [PATCH 089/222] wip --- .../src/FSharp.Editor/Common/Extensions.fs | 2 +- .../Refactor/AddExplicitTypeToParameter.fs | 165 ++++++++---------- .../Refactor/ChangeDerefToValueRefactoring.fs | 63 +++---- .../ChangeTypeofWithNameToNameofExpression.fs | 70 ++++---- 4 files changed, 140 insertions(+), 160 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index 6cb1246d21d..1cd8f5cc36e 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -349,7 +349,7 @@ module Seq = let inline revamp f (ie: seq<_>) = mkSeq (fun () -> f (ie.GetEnumerator())) - let inline toImmutableArray (xs: seq<'a>) : ImmutableArray<'a> = xs.ToImmutableArray() + let toImmutableArray (xs: seq<'a>) : ImmutableArray<'a> = xs.ToImmutableArray() let inline tryHeadV (source: seq<_>) = use e = source.GetEnumerator() diff --git a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs index 527da6f83e7..a6f9dbb8ecb 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs @@ -21,18 +21,16 @@ type internal FSharpAddExplicitTypeToParameterRefactoring [ liftTaskAsync let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line + let! ct = Async.CancellationToken |> liftAsync + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync( position, @@ -40,90 +38,81 @@ type internal FSharpAddExplicitTypeToParameterRefactoring [ CancellableTask.start ct |> Async.AwaitTask + + let! parseFileResults, checkFileResults = + document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpAddExplicitTypeToParameterRefactoring)) + |> CancellableTask.start ct + |> Async.AwaitTask + |> liftAsync + + let! symbolUse = + checkFileResults.GetSymbolUseAtLocation( + fcsTextLineNumber, + lexerSymbol.Ident.idRange.EndColumn, + textLine.ToString(), + lexerSymbol.FullIsland ) - match lexerSymbol with - | None -> return () - | Some lexerSymbol -> - - let! parseFileResults, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpAddExplicitTypeToParameterRefactoring)) - - let symbolUse = - checkFileResults.GetSymbolUseAtLocation( - fcsTextLineNumber, - lexerSymbol.Ident.idRange.EndColumn, - textLine.ToString(), - lexerSymbol.FullIsland + let isValidParameterWithoutTypeAnnotation (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = + let isLambdaIfFunction = + funcOrValue.IsFunction + && parseFileResults.IsBindingALambdaAtPosition symbolUse.Range.Start + + (funcOrValue.IsValue || isLambdaIfFunction) + && parseFileResults.IsPositionContainedInACurriedParameter symbolUse.Range.Start + && not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start) + && not funcOrValue.IsMember + && not funcOrValue.IsMemberThisValue + && not funcOrValue.IsConstructorThisValue + && not (PrettyNaming.IsOperatorDisplayName funcOrValue.DisplayName) + + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue as v when isValidParameterWithoutTypeAnnotation v symbolUse -> + let typeString = v.FullType.FormatWithConstraints symbolUse.DisplayContext + let title = SR.AddTypeAnnotation() + + let! symbolSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) + + let alreadyWrappedInParens = + let rec leftLoop ch pos = + if not (Char.IsWhiteSpace(ch)) then + ch = '(' + else + leftLoop sourceText.[pos - 1] (pos - 1) + + let rec rightLoop ch pos = + if not (Char.IsWhiteSpace(ch)) then + ch = ')' + else + rightLoop sourceText.[pos + 1] (pos + 1) + + let hasLeftParen = leftLoop sourceText.[symbolSpan.Start - 1] (symbolSpan.Start - 1) + let hasRightParen = rightLoop sourceText.[symbolSpan.End] symbolSpan.End + hasLeftParen && hasRightParen + + let getChangedText (sourceText: SourceText) = + if alreadyWrappedInParens then + sourceText.WithChanges(TextChange(TextSpan(symbolSpan.End, 0), ": " + typeString)) + else + sourceText + .WithChanges(TextChange(TextSpan(symbolSpan.Start, 0), "(")) + .WithChanges(TextChange(TextSpan(symbolSpan.End + 1, 0), ": " + typeString + ")")) + + let codeAction = + CodeAction.Create( + title, + (fun (cancellationToken: CancellationToken) -> + async { + let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask + return context.Document.WithText(getChangedText sourceText) + } + |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), + title ) - let isValidParameterWithoutTypeAnnotation (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = - let isLambdaIfFunction = - funcOrValue.IsFunction - && parseFileResults.IsBindingALambdaAtPosition symbolUse.Range.Start - - (funcOrValue.IsValue || isLambdaIfFunction) - && parseFileResults.IsPositionContainedInACurriedParameter symbolUse.Range.Start - && not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start) - && not funcOrValue.IsMember - && not funcOrValue.IsMemberThisValue - && not funcOrValue.IsConstructorThisValue - && not (PrettyNaming.IsOperatorDisplayName funcOrValue.DisplayName) - - match symbolUse with - | None -> return () - | Some symbolUse -> - - match symbolUse.Symbol with - | :? FSharpMemberOrFunctionOrValue as v when isValidParameterWithoutTypeAnnotation v symbolUse -> - let typeString = v.FullType.FormatWithConstraints symbolUse.DisplayContext - let title = SR.AddTypeAnnotation() - - let symbolSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) - - match symbolSpan with - | ValueNone -> return () - | ValueSome symbolSpan -> - - let alreadyWrappedInParens = - let rec leftLoop ch pos = - if not (Char.IsWhiteSpace(ch)) then - ch = '(' - else - leftLoop sourceText.[pos - 1] (pos - 1) - - let rec rightLoop ch pos = - if not (Char.IsWhiteSpace(ch)) then - ch = ')' - else - rightLoop sourceText.[pos + 1] (pos + 1) - - let hasLeftParen = leftLoop sourceText.[symbolSpan.Start - 1] (symbolSpan.Start - 1) - let hasRightParen = rightLoop sourceText.[symbolSpan.End] symbolSpan.End - hasLeftParen && hasRightParen - - let getChangedText (sourceText: SourceText) = - if alreadyWrappedInParens then - sourceText.WithChanges(TextChange(TextSpan(symbolSpan.End, 0), ": " + typeString)) - else - sourceText - .WithChanges(TextChange(TextSpan(symbolSpan.Start, 0), "(")) - .WithChanges(TextChange(TextSpan(symbolSpan.End + 1, 0), ": " + typeString + ")")) - - let codeAction = - CodeAction.Create( - title, - (fun (cancellationToken: CancellationToken) -> - cancellableTask { - let! ct = CancellableTask.getCancellationToken () - let! sourceText = context.Document.GetTextAsync(ct) - return context.Document.WithText(getChangedText sourceText) - } - |> CancellableTask.start cancellationToken), - title - ) - - context.RegisterRefactoring(codeAction) - | _ -> () + context.RegisterRefactoring(codeAction) + | _ -> () } - |> CancellableTask.startAsTask context.CancellationToken + |> Async.Ignore + |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs b/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs index 86607965acf..07408c049b1 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs @@ -22,52 +22,47 @@ type internal FSharpChangeDerefToValueRefactoring [] () = inherit CodeRefactoringProvider() override _.ComputeRefactoringsAsync context = - cancellableTask { + asyncMaybe { let document = context.Document - let! ct = CancellableTask.getCancellationToken () - let! sourceText = context.Document.GetTextAsync(ct) + let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpChangeDerefToValueRefactoring)) + |> CancellableTask.start context.CancellationToken + |> Async.AwaitTask + |> liftAsync let selectionRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) - let derefRange = parseResults.TryRangeOfRefCellDereferenceContainingPos selectionRange.Start - let exprRange = parseResults.TryRangeOfExpressionBeingDereferencedContainingPos selectionRange.Start + let! derefRange = parseResults.TryRangeOfRefCellDereferenceContainingPos selectionRange.Start + let! exprRange = parseResults.TryRangeOfExpressionBeingDereferencedContainingPos selectionRange.Start - match derefRange, exprRange with - | Some derefRange, Some exprRange -> + let combinedRange = Range.unionRanges derefRange exprRange + let! combinedSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, combinedRange) - let combinedRange = Range.unionRanges derefRange exprRange + let replacementString = + // Trim off the `!` + sourceText.GetSubText(combinedSpan).ToString().[1..] + ".Value" - let combinedSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, combinedRange) + let title = SR.UseValueInsteadOfDeref() - match combinedSpan with - | ValueNone -> () - | ValueSome combinedSpan -> - let replacementString = - // Trim off the `!` - sourceText.GetSubText(combinedSpan).ToString().[1..] + ".Value" + let getChangedText (sourceText: SourceText) = + sourceText.WithChanges(TextChange(combinedSpan, replacementString)) - let title = SR.UseValueInsteadOfDeref() + let codeAction = + CodeAction.Create( + title, + (fun (cancellationToken: CancellationToken) -> + async { + let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask + return context.Document.WithText(getChangedText sourceText) + } + |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), + title + ) - let getChangedText (sourceText: SourceText) = - sourceText.WithChanges(TextChange(combinedSpan, replacementString)) - - let codeAction = - CodeAction.Create( - title, - (fun (cancellationToken: CancellationToken) -> - cancellableTask { - let! sourceText = context.Document.GetTextAsync(cancellationToken) - return context.Document.WithText(getChangedText sourceText) - } - |> CancellableTask.start cancellationToken), - title - ) - - context.RegisterRefactoring(codeAction) - | _ -> () + context.RegisterRefactoring(codeAction) } - |> CancellableTask.startAsTask context.CancellationToken + |> Async.Ignore + |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs b/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs index 2efeb8bf6e7..6cc93b06d87 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs @@ -22,48 +22,44 @@ type internal FSharpChangeTypeofWithNameToNameofExpressionRefactoring [ CancellableTask.start context.CancellationToken + |> Async.AwaitTask + |> liftAsync let selectionRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) - let namedTypeOfResults = parseResults.TryRangeOfTypeofWithNameAndTypeExpr(selectionRange.Start) - - match namedTypeOfResults with - | None -> () - | Some namedTypeOfResults -> - - let namedTypeSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, namedTypeOfResults.NamedIdentRange) - let typeofAndNameSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, namedTypeOfResults.FullExpressionRange) - - match namedTypeSpan, typeofAndNameSpan with - | ValueSome namedTypeSpan, ValueSome typeofAndNameSpan -> - - let namedTypeName = sourceText.GetSubText(namedTypeSpan) - let replacementString = $"nameof({namedTypeName})" - - let title = SR.UseNameof() - - let getChangedText (sourceText: SourceText) = - sourceText.WithChanges(TextChange(typeofAndNameSpan, replacementString)) - - let codeAction = - CodeAction.Create( - title, - (fun (cancellationToken: CancellationToken) -> - cancellableTask { - let! sourceText = context.Document.GetTextAsync(cancellationToken) - return context.Document.WithText(getChangedText sourceText) - } |> CancellableTask.start cancellationToken), - title - ) - - context.RegisterRefactoring(codeAction) - | _ -> () - } |> CancellableTask.startAsTask context.CancellationToken + let! namedTypeOfResults = parseResults.TryRangeOfTypeofWithNameAndTypeExpr(selectionRange.Start) + + let! namedTypeSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, namedTypeOfResults.NamedIdentRange) + let! typeofAndNameSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, namedTypeOfResults.FullExpressionRange) + let namedTypeName = sourceText.GetSubText(namedTypeSpan) + let replacementString = $"nameof({namedTypeName})" + + let title = SR.UseNameof() + + let getChangedText (sourceText: SourceText) = + sourceText.WithChanges(TextChange(typeofAndNameSpan, replacementString)) + + let codeAction = + CodeAction.Create( + title, + (fun (cancellationToken: CancellationToken) -> + async { + let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask + return context.Document.WithText(getChangedText sourceText) + } + |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), + title + ) + + context.RegisterRefactoring(codeAction) + } + |> Async.Ignore + |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) From cc5f92bf2877c42fa3eeb3d720cd68b27099eb5c Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 11 Sep 2023 14:59:49 +0200 Subject: [PATCH 090/222] wip --- .../Commands/XmlDocCommandService.fs | 118 +++++++++--------- .../src/FSharp.Editor/Common/Extensions.fs | 2 +- 2 files changed, 58 insertions(+), 62 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs index 4efd4e51f38..b8547654c5b 100644 --- a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs @@ -60,80 +60,76 @@ type internal XmlDocCommandFilter(wpfTextView: IWpfTextView, filePath: string, w match XmlDocComment.IsBlank lineWithLastCharInserted with | Some i when i = indexOfCaret -> - cancellableTask { + asyncMaybe { try // XmlDocable line #1 are 1-based, editor is 0-based let curEditorLineNum = wpfTextView.Caret.Position.BufferPosition.GetContainingLine().LineNumber - let document = getLastDocument () - - match document with - | None -> () - | Some document -> - let! cancellationToken = CancellableTask.getCancellationToken () - let! sourceText = document.GetTextAsync(cancellationToken) - let! parseResults = document.GetFSharpParseResultsAsync(nameof (XmlDocCommandFilter)) - - let xmlDocables = - XmlDocParser.GetXmlDocables(sourceText.ToFSharpSourceText(), parseResults.ParseTree) + let! document = getLastDocument () + let! cancellationToken = Async.CancellationToken |> liftAsync + let! sourceText = document.GetTextAsync(cancellationToken) + let! parseResults = document.GetFSharpParseResultsAsync(nameof (XmlDocCommandFilter)) |> CancellableTask.start cancellationToken |> Async.AwaitTask |> liftAsync + + let xmlDocables = + XmlDocParser.GetXmlDocables(sourceText.ToFSharpSourceText(), parseResults.ParseTree) + + let xmlDocablesBelowThisLine = + // +1 because looking below current line for e.g. a 'member' or 'let' + xmlDocables + |> List.filter (fun (XmlDocable (line, _indent, _paramNames)) -> line = curEditorLineNum + 1) + + match xmlDocablesBelowThisLine with + | [] -> () + | XmlDocable (_line, indent, paramNames) :: _xs -> + // delete the slashes the user typed (they may be indented wrong) + let editorLineToDelete = + wpfTextView.TextBuffer.CurrentSnapshot.GetLineFromLineNumber( + wpfTextView.Caret.Position.BufferPosition.GetContainingLine().LineNumber + ) + + wpfTextView.TextBuffer.Delete(editorLineToDelete.Extent.Span) |> ignore + // add the new xmldoc comment + let toInsert = new Text.StringBuilder() + + toInsert + .Append(' ', indent) + .AppendLine("/// ") + .Append(' ', indent) + .AppendLine("/// ") + .Append(' ', indent) + .Append("/// ") + |> ignore + + paramNames + |> List.iter (fun p -> + toInsert + .AppendLine() + .Append(' ', indent) + .Append(sprintf "/// " p) + |> ignore) - let xmlDocablesBelowThisLine = - // +1 because looking below current line for e.g. a 'member' or 'let' - xmlDocables - |> List.filter (fun (XmlDocable (line, _indent, _paramNames)) -> line = curEditorLineNum + 1) + let _newSS = + wpfTextView.TextBuffer.Insert( + wpfTextView.Caret.Position.BufferPosition.Position, + toInsert.ToString() + ) + // move the caret to between the summary tags + let lastLine = wpfTextView.Caret.Position.BufferPosition.GetContainingLine() - match xmlDocablesBelowThisLine with - | [] -> () - | XmlDocable (_line, indent, paramNames) :: _xs -> - // delete the slashes the user typed (they may be indented wrong) - let editorLineToDelete = - wpfTextView.TextBuffer.CurrentSnapshot.GetLineFromLineNumber( - wpfTextView.Caret.Position.BufferPosition.GetContainingLine().LineNumber - ) + let middleSummaryLine = + wpfTextView.TextSnapshot.GetLineFromLineNumber(lastLine.LineNumber - 1 - paramNames.Length) - wpfTextView.TextBuffer.Delete(editorLineToDelete.Extent.Span) |> ignore - // add the new xmldoc comment - let toInsert = new Text.StringBuilder() + wpfTextView.Caret.MoveTo(wpfTextView.GetTextViewLineContainingBufferPosition(middleSummaryLine.Start)) + |> ignore - toInsert - .Append(' ', indent) - .AppendLine("/// ") - .Append(' ', indent) - .AppendLine("/// ") - .Append(' ', indent) - .Append("/// ") - |> ignore - - paramNames - |> List.iter (fun p -> - toInsert - .AppendLine() - .Append(' ', indent) - .Append(sprintf "/// " p) - |> ignore) - - let _newSS = - wpfTextView.TextBuffer.Insert( - wpfTextView.Caret.Position.BufferPosition.Position, - toInsert.ToString() - ) - // move the caret to between the summary tags - let lastLine = wpfTextView.Caret.Position.BufferPosition.GetContainingLine() - - let middleSummaryLine = - wpfTextView.TextSnapshot.GetLineFromLineNumber(lastLine.LineNumber - 1 - paramNames.Length) - - wpfTextView.Caret.MoveTo(wpfTextView.GetTextViewLineContainingBufferPosition(middleSummaryLine.Start)) - |> ignore - - shouldCommitCharacter <- false + shouldCommitCharacter <- false with ex -> Assert.Exception ex () } - |> CancellableTask.startAsTaskWithoutCancellation // We don't have a cancellation token here at the moment, maybe there's a better way of handling it in modern VS? - |> ignore + |> Async.Ignore + |> Async.StartImmediate | Some _ | None -> () | _ -> () diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index 1cd8f5cc36e..4328952339c 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -529,7 +529,7 @@ module Exception = |> String.concat " ---> " type Async with - + static member RunImmediateExceptOnUI(computation: Async<'T>, ?cancellationToken) = match SynchronizationContext.Current with | null -> From 38ea5406acd1bd63262389a52735a1fb7ef4ce93 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 11 Sep 2023 15:00:51 +0200 Subject: [PATCH 091/222] Fantomas --- .../Commands/XmlDocCommandService.fs | 7 +- .../src/FSharp.Editor/Common/Extensions.fs | 45 +++---- .../Debugging/BreakpointResolutionService.fs | 6 +- .../Navigation/FindDefinitionService.fs | 4 +- .../Navigation/GoToDefinition.fs | 112 +++++++++--------- .../Navigation/GoToDefinitionService.fs | 3 +- .../Navigation/NavigateToSearchService.fs | 14 ++- .../Navigation/NavigationBarItemService.fs | 6 +- .../Refactor/AddExplicitTypeToParameter.fs | 4 +- 9 files changed, 104 insertions(+), 97 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs index b8547654c5b..5b762414c59 100644 --- a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs @@ -69,7 +69,12 @@ type internal XmlDocCommandFilter(wpfTextView: IWpfTextView, filePath: string, w let! document = getLastDocument () let! cancellationToken = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) - let! parseResults = document.GetFSharpParseResultsAsync(nameof (XmlDocCommandFilter)) |> CancellableTask.start cancellationToken |> Async.AwaitTask |> liftAsync + + let! parseResults = + document.GetFSharpParseResultsAsync(nameof (XmlDocCommandFilter)) + |> CancellableTask.start cancellationToken + |> Async.AwaitTask + |> liftAsync let xmlDocables = XmlDocParser.GetXmlDocables(sourceText.ToFSharpSourceText(), parseResults.ParseTree) diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index 4328952339c..87d0007be82 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -306,14 +306,12 @@ module IEnumerator = let get () = if not started then - raise(InvalidOperationException("Not started")) + raise (InvalidOperationException("Not started")) match curr with - | None -> - raise(InvalidOperationException("Already finished")) + | None -> raise (InvalidOperationException("Already finished")) | Some x -> x - { new IEnumerator<'U> with member _.Current = get () interface System.Collections.IEnumerator with @@ -331,20 +329,21 @@ module IEnumerator = Option.isSome curr member _.Reset() = - raise(NotSupportedException("Reset is not supported")) + raise (NotSupportedException("Reset is not supported")) interface System.IDisposable with - member _.Dispose() = - e.Dispose() + member _.Dispose() = e.Dispose() } + [] module Seq = let mkSeq f = { new IEnumerable<'U> with - member _.GetEnumerator() = f() - + member _.GetEnumerator() = f () interface System.Collections.IEnumerable with - member _.GetEnumerator() = (f() :> System.Collections.IEnumerator) } + member _.GetEnumerator() = + (f () :> System.Collections.IEnumerator) + } let inline revamp f (ie: seq<_>) = mkSeq (fun () -> f (ie.GetEnumerator())) @@ -354,10 +353,7 @@ module Seq = let inline tryHeadV (source: seq<_>) = use e = source.GetEnumerator() - if (e.MoveNext()) then - ValueSome e.Current - else - ValueNone + if (e.MoveNext()) then ValueSome e.Current else ValueNone let inline tryFindV ([] predicate) (source: seq<'T>) = use e = source.GetEnumerator() @@ -409,10 +405,7 @@ module Array = let toImmutableArray (xs: 'T[]) = xs.ToImmutableArray() let inline tryHeadV (array: _[]) = - if array.Length = 0 then - ValueNone - else - ValueSome array[0] + if array.Length = 0 then ValueNone else ValueSome array[0] let inline tryFindV ([] predicate) (array: _[]) = @@ -440,8 +433,7 @@ module Array = if i <> array.Length then - let chunk1: 'U[] = - Array.zeroCreate ((array.Length >>> 2) + 1) + let chunk1: 'U[] = Array.zeroCreate ((array.Length >>> 2) + 1) chunk1.[0] <- first let mutable count = 1 @@ -459,8 +451,7 @@ module Array = i <- i + 1 if i < array.Length then - let chunk2: 'U[] = - Array.zeroCreate (array.Length - i) + let chunk2: 'U[] = Array.zeroCreate (array.Length - i) count <- 0 @@ -475,8 +466,7 @@ module Array = i <- i + 1 - let res: 'U[] = - Array.zeroCreate (chunk1.Length + count) + let res: 'U[] = Array.zeroCreate (chunk1.Length + count) Array.Copy(chunk1, res, chunk1.Length) Array.Copy(chunk2, 0, res, chunk1.Length, count) @@ -489,10 +479,7 @@ module Array = [] module ImmutableArray = let inline tryHeadV (xs: ImmutableArray<'T>) : 'T voption = - if xs.Length = 0 then - ValueNone - else - ValueSome xs[0] + if xs.Length = 0 then ValueNone else ValueSome xs[0] let inline empty<'T> = ImmutableArray<'T>.Empty @@ -529,7 +516,7 @@ module Exception = |> String.concat " ---> " type Async with - + static member RunImmediateExceptOnUI(computation: Async<'T>, ?cancellationToken) = match SynchronizationContext.Current with | null -> diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index a7e33fbfcb3..56ad5a26518 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -38,10 +38,10 @@ type internal FSharpBreakpointResolutionService [] () = 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 = - document.GetFSharpParseResultsAsync(nameof (FSharpBreakpointResolutionService)) + let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpBreakpointResolutionService)) - let location = parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) + let location = + parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) return ValueOption.ofOption location } diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs index 0013da2d154..bc408c01741 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindDefinitionService.fs @@ -20,5 +20,5 @@ type internal FSharpFindDefinitionService [] (metadataAsSo cancellableTask { let navigation = FSharpNavigation(metadataAsSource, document, rangeStartup) return! navigation.FindDefinitionsAsync(position) - } |> CancellableTask.start cancellationToken - + } + |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 018ee7e1859..08591a6915b 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -186,9 +186,9 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = /// Helper function that is used to determine the navigation strategy to apply, can be tuned towards signatures or implementation files. member private _.FindSymbolHelper(originDocument: Document, originRange: range, sourceText: SourceText, preferSignature: bool) = let originTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, originRange) + match originTextSpan with - | ValueNone -> - CancellableTask.singleton None + | ValueNone -> CancellableTask.singleton None | ValueSome originTextSpan -> cancellableTask { let userOpName = "FindSymbolHelper" @@ -206,8 +206,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let! ct = CancellableTask.getCancellationToken () - let! _, checkFileResults = - originDocument.GetFSharpParseAndCheckResultsAsync(nameof (GoToDefinition)) + let! _, checkFileResults = originDocument.GetFSharpParseAndCheckResultsAsync(nameof (GoToDefinition)) let fsSymbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) @@ -233,24 +232,24 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = | ValueSome implDoc -> let! implSourceText = implDoc.GetTextAsync(ct) - let! _, checkFileResults = - implDoc.GetFSharpParseAndCheckResultsAsync(userOpName) + let! _, checkFileResults = implDoc.GetFSharpParseAndCheckResultsAsync(userOpName) let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbol, ct) - let implSymbol = Array.tryHeadV symbolUses + let implSymbol = Array.tryHeadV symbolUses match implSymbol with | ValueNone -> return None | ValueSome implSymbol -> - let implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(implSourceText, implSymbol.Range) + let implTextSpan = + RoslynHelpers.TryFSharpRangeToTextSpan(implSourceText, implSymbol.Range) match implTextSpan with | ValueNone -> return None - | ValueSome implTextSpan -> - return Some(FSharpGoToDefinitionNavigableItem(implDoc, implTextSpan)) + | ValueSome implTextSpan -> return Some(FSharpGoToDefinitionNavigableItem(implDoc, implTextSpan)) else - let targetDocument = originDocument.Project.Solution.TryGetDocumentFromFSharpRange fsSymbolUse.Range + let targetDocument = + originDocument.Project.Solution.TryGetDocumentFromFSharpRange fsSymbolUse.Range match targetDocument with | None -> return None @@ -304,8 +303,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let idRange = lexerSymbol.Ident.idRange - let! _, checkFileResults = - originDocument.GetFSharpParseAndCheckResultsAsync(userOpName) + let! _, checkFileResults = originDocument.GetFSharpParseAndCheckResultsAsync(userOpName) let declarations = checkFileResults.GetDeclarationLocation( @@ -333,11 +331,16 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = | ValueSome project -> let! symbols = SymbolFinder.FindSourceDeclarationsAsync(project, (fun _ -> true), cancellationToken) - let roslynSymbols = - Seq.collect ExternalSymbol.ofRoslynSymbol symbols + let roslynSymbols = Seq.collect ExternalSymbol.ofRoslynSymbol symbols let symbol = - Seq.tryPickV (fun (sym, externalSym) -> if externalSym = targetExternalSym then ValueSome sym else ValueNone) roslynSymbols + Seq.tryPickV + (fun (sym, externalSym) -> + if externalSym = targetExternalSym then + ValueSome sym + else + ValueNone) + roslynSymbols let location = symbol @@ -349,19 +352,21 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = | ValueSome location -> return - ValueSome ( + ValueSome( FSharpGoToDefinitionResult.NavigableItem( FSharpGoToDefinitionNavigableItem(project.GetDocument(location.SourceTree), location.SourceSpan) - ), idRange) + ), + idRange + ) | _ -> let metadataReferences = originDocument.Project.MetadataReferences - return ValueSome (FSharpGoToDefinitionResult.ExternalAssembly(targetSymbolUse, metadataReferences), idRange) + return ValueSome(FSharpGoToDefinitionResult.ExternalAssembly(targetSymbolUse, metadataReferences), idRange) | FindDeclResult.DeclFound targetRange -> // If the file is not associated with a document, it's considered external. if not (originDocument.Project.Solution.ContainsDocumentWithFilePath(targetRange.FileName)) then let metadataReferences = originDocument.Project.MetadataReferences - return ValueSome (FSharpGoToDefinitionResult.ExternalAssembly(targetSymbolUse, metadataReferences), idRange) + return ValueSome(FSharpGoToDefinitionResult.ExternalAssembly(targetSymbolUse, metadataReferences), idRange) else // if goto definition is called at we are alread at the declaration location of a symbol in // either a signature or an implementation file then we jump to it's respective postion in thethe @@ -375,7 +380,8 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = if not (File.Exists implFilePath) then return ValueNone else - let implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath + let implDocument = + originDocument.Project.Solution.TryGetDocumentFromPath implFilePath match implDocument with | ValueNone -> return ValueNone @@ -386,7 +392,9 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = | None -> return ValueNone | Some targetRange -> let! implSourceText = implDocument.GetTextAsync(cancellationToken) - let implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(implSourceText, targetRange) + + let implTextSpan = + RoslynHelpers.TryFSharpRangeToTextSpan(implSourceText, targetRange) match implTextSpan with | ValueNone -> return ValueNone @@ -406,7 +414,8 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = match declarations with | FindDeclResult.DeclFound targetRange -> - let sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName + let sigDocument = + originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName match sigDocument with | ValueNone -> return ValueNone @@ -425,12 +434,15 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = // - gotoDefn origin = signature , gotoDefn destination = signature // - gotoDefn origin = implementation, gotoDefn destination = implementation else - let sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName + let sigDocument = + originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName + match sigDocument with | ValueNone -> return ValueNone | ValueSome sigDocument -> let! sigSourceText = sigDocument.GetTextAsync(cancellationToken) let sigTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sigSourceText, targetRange) + match sigTextSpan with | ValueNone -> return ValueNone | ValueSome sigTextSpan -> @@ -447,7 +459,8 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = else sigDocument.FilePath - let implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath + let implDocument = + originDocument.Project.Solution.TryGetDocumentFromPath implFilePath match implDocument with | ValueNone -> return ValueNone @@ -458,7 +471,10 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = | None -> return ValueNone | Some targetRange -> let! implSourceText = implDocument.GetTextAsync(cancellationToken) - let implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(implSourceText, targetRange) + + let implTextSpan = + RoslynHelpers.TryFSharpRangeToTextSpan(implSourceText, targetRange) + match implTextSpan with | ValueNone -> return ValueNone | ValueSome implTextSpan -> @@ -524,35 +540,24 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = result /// Find the declaration location (signature file/.fsi) of the target symbol if possible, fall back to definition - member this.NavigateToSymbolDeclarationAsync - ( - targetDocument: Document, - targetSourceText: SourceText, - symbolRange: range - ) = + member this.NavigateToSymbolDeclarationAsync(targetDocument: Document, targetSourceText: SourceText, symbolRange: range) = cancellableTask { let! item = this.FindDeclarationOfSymbolAtRange(targetDocument, symbolRange, targetSourceText) match item with - | None -> - return false + | None -> return false | Some item -> let! cancellationToken = CancellableTask.getCancellationToken () return this.NavigateToItem(item, cancellationToken) } /// Find the definition location (implementation file/.fs) of the target symbol - member this.NavigateToSymbolDefinitionAsync - ( - targetDocument: Document, - targetSourceText: SourceText, - symbolRange: range - ) = + member this.NavigateToSymbolDefinitionAsync(targetDocument: Document, targetSourceText: SourceText, symbolRange: range) = cancellableTask { let! item = this.FindDefinitionOfSymbolAtRange(targetDocument, symbolRange, targetSourceText) + match item with - | None -> - return false + | None -> return false | Some item -> let! cancellationToken = CancellableTask.getCancellationToken () return this.NavigateToItem(item, cancellationToken) @@ -713,6 +718,7 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, let! targetSource = targetDoc.GetTextAsync(cancellationToken) let targetTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan(targetSource, range) + match targetTextSpan with | ValueNone -> () | ValueSome targetTextSpan -> @@ -728,14 +734,13 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, else // Navigation request was made in a .fs file, so we try to find the implmentation of the symbol at target range. // This is the part that may take some time, because of type checks involved. - let! result = - gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, range) + let! result = gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, range) if not result then // In case the above fails, we just navigate to target range. do gtd.TryNavigateToTextSpan(targetDoc, targetTextSpan, cancellationToken) - } |> CancellableTask.start cancellationToken - ), + } + |> CancellableTask.start cancellationToken), // Default wait time before VS shows the dialog allowing to cancel the long running task is 2 seconds. // This seems a bit too long to leave the user without any feedback, so we shorten it to 1 second. // Note: it seems anything less than 1 second will get rounded down to zero, resulting in flashing dialog @@ -829,11 +834,11 @@ type FSharpNavigableLocation(metadataAsSource: FSharpMetadataAsSourceService, sy let! cancellationToken = CancellableTask.getCancellationToken () - let targetDoc = project.Solution.TryGetDocumentFromFSharpRange(symbolRange, project.Id) + let targetDoc = + project.Solution.TryGetDocumentFromFSharpRange(symbolRange, project.Id) match targetDoc with - | None -> - return false + | None -> return false | Some targetDoc -> let! targetSource = targetDoc.GetTextAsync(cancellationToken) let gtd = GoToDefinition(metadataAsSource) @@ -845,11 +850,10 @@ type FSharpNavigableLocation(metadataAsSource: FSharpMetadataAsSourceService, sy Implementation match targetPath with - | Signature -> - return! gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, symbolRange) - | Implementation -> - return! gtd.NavigateToSymbolDeclarationAsync(targetDoc, targetSource, symbolRange) - } |> CancellableTask.start cancellationToken + | Signature -> return! gtd.NavigateToSymbolDefinitionAsync(targetDoc, targetSource, symbolRange) + | Implementation -> return! gtd.NavigateToSymbolDeclarationAsync(targetDoc, targetSource, symbolRange) + } + |> CancellableTask.start cancellationToken [)>] [)>] diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index 1cea931c17d..9e822e0e26f 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -23,7 +23,8 @@ type internal FSharpGoToDefinitionService [] (metadataAsSo let navigation = FSharpNavigation(metadataAsSource, document, rangeStartup) let! res = navigation.FindDefinitionsAsync(position) return (res :> IEnumerable<_>) - } |> CancellableTask.start cancellationToken + } + |> CancellableTask.start cancellationToken /// Invoked with Go to Definition. /// Try to navigate to the definiton of the symbol at the symbolRange in the originDocument diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index 07a3e52f8bc..e901e0c0113 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -142,7 +142,8 @@ type internal FSharpNavigateToSearchService [] else // full name with dots allows for path matching, e.g. // "f.c.so.elseif" will match "Fantomas.Core.SyntaxOak.ElseIfNode" - patternMatcher.TryMatch $"{item.Container.FullName}.{name}" |> ValueOption.ofNullable + patternMatcher.TryMatch $"{item.Container.FullName}.{name}" + |> ValueOption.ofNullable let processDocument (tryMatch: NavigableItem -> PatternMatch voption) (kinds: IImmutableSet) (document: Document) = cancellableTask { @@ -155,11 +156,13 @@ type internal FSharpNavigateToSearchService [] let processed = [| for item in items do - let contains = kinds.Contains (navigateToItemKindToRoslynKind item.Kind) + let contains = kinds.Contains(navigateToItemKindToRoslynKind item.Kind) let patternMatch = tryMatch item + match contains, patternMatch with | true, ValueSome m -> let sourceSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, item.Range) + match sourceSpan with | ValueNone -> () | ValueSome sourceSpan -> @@ -173,7 +176,12 @@ type internal FSharpNavigateToSearchService [] kind, patternMatchKindToNavigateToMatchKind m.Kind, item.Name, - FSharpNavigableItem(glyph, ImmutableArray.Create(TaggedText(TextTags.Text, item.Name)), document, sourceSpan) + FSharpNavigableItem( + glyph, + ImmutableArray.Create(TaggedText(TextTags.Text, item.Name)), + document, + sourceSpan + ) ) | _ -> () |] diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs index d40a18e9485..b31b42c9822 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs @@ -25,8 +25,7 @@ type internal FSharpNavigationBarItemService [] () = let! cancellationToken = CancellableTask.getCancellationToken () - let! parseResults = - document.GetFSharpParseResultsAsync(nameof (FSharpNavigationBarItemService)) + let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpNavigationBarItemService)) let navItems = Navigation.getNavigation parseResults.ParseTree let! sourceText = document.GetTextAsync(cancellationToken) @@ -59,4 +58,5 @@ type internal FSharpNavigationBarItemService [] () = ) :> FSharpNavigationBarItem)) :> IList<_> - } |> CancellableTask.start cancellationToken + } + |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs index a6f9dbb8ecb..bcea39dd8ec 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs @@ -38,7 +38,9 @@ type internal FSharpAddExplicitTypeToParameterRefactoring [ CancellableTask.start ct |> Async.AwaitTask + ) + |> CancellableTask.start ct + |> Async.AwaitTask let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpAddExplicitTypeToParameterRefactoring)) From 709d6d5a5674d3c3421c3cc63369650d4ba9fb41 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 11 Sep 2023 16:06:43 +0200 Subject: [PATCH 092/222] more wip --- .../Navigation/GoToDefinition.fs | 294 +++++++----------- .../Navigation/GoToDefinitionService.fs | 5 - .../Navigation/NavigableSymbolsService.fs | 82 +++-- 3 files changed, 147 insertions(+), 234 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 08591a6915b..3f242c63b68 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -110,45 +110,6 @@ module private ExternalSymbol = | _ -> [] -// TODO: Uncomment code when VS has a fix for updating the status bar. -type StatusBar() = - let statusBar = - ServiceProvider.GlobalProvider.GetService() - - let mutable _searchIcon = - int16 Microsoft.VisualStudio.Shell.Interop.Constants.SBAI_Find :> obj - - let _clear () = - // unfreeze the statusbar - statusBar.FreezeOutput 0 |> ignore - statusBar.Clear() |> ignore - - member _.Message(_msg: string) = () - //let _, frozen = statusBar.IsFrozen() - //// unfreeze the status bar - //if frozen <> 0 then statusBar.FreezeOutput 0 |> ignore - //statusBar.SetText msg |> ignore - //// freeze the status bar - //statusBar.FreezeOutput 1 |> ignore - - member this.TempMessage(_msg: string) = () - //this.Message msg - //async { - // do! Async.Sleep 4000 - // match statusBar.GetText() with - // | 0, currentText when currentText <> msg -> () - // | _ -> clear() - //}|> Async.Start - - member _.Clear() = () //clear() - - /// Animated magnifying glass that displays on the status bar while a symbol search is in progress. - member _.Animate() : IDisposable = - //statusBar.Animation (1, &searchIcon) |> ignore - { new IDisposable with - member _.Dispose() = () - } //statusBar.Animation(0, &searchIcon) |> ignore } - type internal FSharpGoToDefinitionNavigableItem(document, sourceSpan) = inherit FSharpNavigableItem(Glyph.BasicFile, ImmutableArray.Empty, document, sourceSpan) @@ -161,7 +122,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = /// Use an origin document to provide the solution & workspace used to /// find the corresponding textSpan and INavigableItem for the range let rangeToNavigableItem (range: range, document: Document) = - async { + cancellableTask { let fileName = try System.IO.Path.GetFullPath range.FileName @@ -493,16 +454,15 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = /// Construct a task that will return a navigation target for the implementation definition of the symbol /// at the provided position in the document. - member this.FindDefinitionTask(originDocument: Document, position: int, cancellationToken: CancellationToken) = + member this.FindDefinitionAsync(originDocument: Document, position: int) = this.FindDefinitionAtPosition(originDocument, position) - |> CancellableTask.start cancellationToken /// Navigate to the positon of the textSpan in the provided document /// used by quickinfo link navigation when the tooltip contains the correct destination range. member _.TryNavigateToTextSpan ( document: Document, - textSpan: Microsoft.CodeAnalysis.Text.TextSpan, + textSpan: TextSpan, cancellationToken: CancellationToken ) = let navigableItem = FSharpGoToDefinitionNavigableItem(document, textSpan) @@ -511,17 +471,9 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let navigationService = workspace.Services.GetService() - let navigationSucceeded = - navigationService.TryNavigateToSpan(workspace, navigableItem.Document.Id, navigableItem.SourceSpan, cancellationToken) - - if not navigationSucceeded then - StatusBar().TempMessage(SR.CannotNavigateUnknown()) + navigationService.TryNavigateToSpan(workspace, navigableItem.Document.Id, navigableItem.SourceSpan, cancellationToken) |> ignore member _.NavigateToItem(navigableItem: FSharpNavigableItem, cancellationToken: CancellationToken) = - let statusBar = StatusBar() - use __ = statusBar.Animate() - - statusBar.Message(SR.NavigatingTo()) let workspace = navigableItem.Document.Project.Solution.Workspace @@ -532,11 +484,6 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let result = navigationService.TryNavigateToSpan(workspace, navigableItem.Document.Id, navigableItem.SourceSpan, cancellationToken) - if result then - statusBar.Clear() - else - statusBar.TempMessage(SR.CannotNavigateUnknown()) - result /// Find the declaration location (signature file/.fsi) of the target symbol if possible, fall back to definition @@ -567,11 +514,8 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = ( targetSymbolUse: FSharpSymbolUse, metadataReferences: seq, - cancellationToken: CancellationToken, - statusBar: StatusBar + cancellationToken: CancellationToken ) = - use __ = statusBar.Animate() - statusBar.Message(SR.NavigatingTo()) let textOpt = match targetSymbolUse.Symbol with @@ -593,103 +537,97 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = |> Option.map (fun text -> text, symbol.DisplayName) | _ -> None - let result = - match textOpt with - | Some (text, fileName) -> - let tmpProjInfo, tmpDocInfo = - MetadataAsSource.generateTemporaryDocument ( - AssemblyIdentity(targetSymbolUse.Symbol.Assembly.QualifiedName), - fileName, - metadataReferences - ) + match textOpt with + | Some (text, fileName) -> + let tmpProjInfo, tmpDocInfo = + MetadataAsSource.generateTemporaryDocument ( + AssemblyIdentity(targetSymbolUse.Symbol.Assembly.QualifiedName), + fileName, + metadataReferences + ) - let tmpShownDocOpt = - metadataAsSource.ShowDocument(tmpProjInfo, tmpDocInfo.FilePath, SourceText.From(text.ToString())) + let tmpShownDocOpt = + metadataAsSource.ShowDocument(tmpProjInfo, tmpDocInfo.FilePath, SourceText.From(text.ToString())) - match tmpShownDocOpt with - | ValueSome tmpShownDoc -> - let goToAsync = - asyncMaybe { + match tmpShownDocOpt with + | ValueSome tmpShownDoc -> + let goToAsync = + asyncMaybe { - let! ct = Async.CancellationToken |> liftAsync + let! ct = Async.CancellationToken |> liftAsync - let! _, checkResults = - tmpShownDoc.GetFSharpParseAndCheckResultsAsync("NavigateToExternalDeclaration") - |> CancellableTask.start ct - |> Async.AwaitTask - |> liftAsync + let! _, checkResults = + tmpShownDoc.GetFSharpParseAndCheckResultsAsync("NavigateToExternalDeclaration") + |> CancellableTask.start ct + |> Async.AwaitTask + |> liftAsync - let! r = - let rec areTypesEqual (ty1: FSharpType) (ty2: FSharpType) = - let ty1 = ty1.StripAbbreviations() - let ty2 = ty2.StripAbbreviations() + let! r = + let rec areTypesEqual (ty1: FSharpType) (ty2: FSharpType) = + let ty1 = ty1.StripAbbreviations() + let ty2 = ty2.StripAbbreviations() - let generic = - ty1.IsGenericParameter && ty2.IsGenericParameter - || (ty1.GenericArguments.Count = ty2.GenericArguments.Count - && (ty1.GenericArguments, ty2.GenericArguments) ||> Seq.forall2 areTypesEqual) + let generic = + ty1.IsGenericParameter && ty2.IsGenericParameter + || (ty1.GenericArguments.Count = ty2.GenericArguments.Count + && (ty1.GenericArguments, ty2.GenericArguments) ||> Seq.forall2 areTypesEqual) - if generic then - true - else - let namesEqual = ty1.TypeDefinition.DisplayName = ty2.TypeDefinition.DisplayName - let accessPathsEqual = ty1.TypeDefinition.AccessPath = ty2.TypeDefinition.AccessPath - namesEqual && accessPathsEqual - - // This tries to find the best possible location of the target symbol's location in the metadata source. - // We really should rely on symbol equality within FCS instead of doing it here, - // but the generated metadata as source isn't perfect for symbol equality. - checkResults.GetAllUsesOfAllSymbolsInFile(cancellationToken) - |> Seq.tryFindV (fun x -> - match x.Symbol, targetSymbolUse.Symbol with - | (:? FSharpEntity as symbol1), (:? FSharpEntity as symbol2) when x.IsFromDefinition -> - symbol1.DisplayName = symbol2.DisplayName - | (:? FSharpMemberOrFunctionOrValue as symbol1), (:? FSharpMemberOrFunctionOrValue as symbol2) -> - symbol1.DisplayName = symbol2.DisplayName - && (match symbol1.DeclaringEntity, symbol2.DeclaringEntity with - | Some e1, Some e2 -> e1.CompiledName = e2.CompiledName - | _ -> false) - && symbol1.GenericParameters.Count = symbol2.GenericParameters.Count - && symbol1.CurriedParameterGroups.Count = symbol2.CurriedParameterGroups.Count - && ((symbol1.CurriedParameterGroups, symbol2.CurriedParameterGroups) - ||> Seq.forall2 (fun pg1 pg2 -> - pg1.Count = pg2.Count - && ((pg1, pg2) ||> Seq.forall2 (fun p1 p2 -> areTypesEqual p1.Type p2.Type)))) - && areTypesEqual symbol1.ReturnParameter.Type symbol2.ReturnParameter.Type - | (:? FSharpField as symbol1), (:? FSharpField as symbol2) when x.IsFromDefinition -> - symbol1.DisplayName = symbol2.DisplayName - && (match symbol1.DeclaringEntity, symbol2.DeclaringEntity with - | Some e1, Some e2 -> e1.CompiledName = e2.CompiledName - | _ -> false) - | (:? FSharpUnionCase as symbol1), (:? FSharpUnionCase as symbol2) -> - symbol1.DisplayName = symbol2.DisplayName - && symbol1.DeclaringEntity.CompiledName = symbol2.DeclaringEntity.CompiledName - | _ -> false) - |> ValueOption.map (fun x -> x.Range) - |> ValueOption.toOption - - let span = - match RoslynHelpers.TryFSharpRangeToTextSpan(tmpShownDoc.GetTextAsync(cancellationToken).Result, r) with - | ValueSome span -> span - | _ -> TextSpan() - - return span - } - - let span = - match Async.RunImmediateExceptOnUI(goToAsync, cancellationToken = cancellationToken) with - | Some span -> span - | _ -> TextSpan() - - let navItem = FSharpGoToDefinitionNavigableItem(tmpShownDoc, span) - this.NavigateToItem(navItem, cancellationToken) - | _ -> false - | _ -> false + if generic then + true + else + let namesEqual = ty1.TypeDefinition.DisplayName = ty2.TypeDefinition.DisplayName + let accessPathsEqual = ty1.TypeDefinition.AccessPath = ty2.TypeDefinition.AccessPath + namesEqual && accessPathsEqual + + // This tries to find the best possible location of the target symbol's location in the metadata source. + // We really should rely on symbol equality within FCS instead of doing it here, + // but the generated metadata as source isn't perfect for symbol equality. + checkResults.GetAllUsesOfAllSymbolsInFile(cancellationToken) + |> Seq.tryFindV (fun x -> + match x.Symbol, targetSymbolUse.Symbol with + | (:? FSharpEntity as symbol1), (:? FSharpEntity as symbol2) when x.IsFromDefinition -> + symbol1.DisplayName = symbol2.DisplayName + | (:? FSharpMemberOrFunctionOrValue as symbol1), (:? FSharpMemberOrFunctionOrValue as symbol2) -> + symbol1.DisplayName = symbol2.DisplayName + && (match symbol1.DeclaringEntity, symbol2.DeclaringEntity with + | Some e1, Some e2 -> e1.CompiledName = e2.CompiledName + | _ -> false) + && symbol1.GenericParameters.Count = symbol2.GenericParameters.Count + && symbol1.CurriedParameterGroups.Count = symbol2.CurriedParameterGroups.Count + && ((symbol1.CurriedParameterGroups, symbol2.CurriedParameterGroups) + ||> Seq.forall2 (fun pg1 pg2 -> + pg1.Count = pg2.Count + && ((pg1, pg2) ||> Seq.forall2 (fun p1 p2 -> areTypesEqual p1.Type p2.Type)))) + && areTypesEqual symbol1.ReturnParameter.Type symbol2.ReturnParameter.Type + | (:? FSharpField as symbol1), (:? FSharpField as symbol2) when x.IsFromDefinition -> + symbol1.DisplayName = symbol2.DisplayName + && (match symbol1.DeclaringEntity, symbol2.DeclaringEntity with + | Some e1, Some e2 -> e1.CompiledName = e2.CompiledName + | _ -> false) + | (:? FSharpUnionCase as symbol1), (:? FSharpUnionCase as symbol2) -> + symbol1.DisplayName = symbol2.DisplayName + && symbol1.DeclaringEntity.CompiledName = symbol2.DeclaringEntity.CompiledName + | _ -> false) + |> ValueOption.map (fun x -> x.Range) + |> ValueOption.toOption + + let span = + match RoslynHelpers.TryFSharpRangeToTextSpan(tmpShownDoc.GetTextAsync(cancellationToken).Result, r) with + | ValueSome span -> span + | _ -> TextSpan() + + return span + } - if result then - statusBar.Clear() - else - statusBar.TempMessage(SR.CannotNavigateUnknown()) + let span = + match Async.RunImmediateExceptOnUI(goToAsync, cancellationToken = cancellationToken) with + | Some span -> span + | _ -> TextSpan() + + let navItem = FSharpGoToDefinitionNavigableItem(tmpShownDoc, span) + this.NavigateToItem(navItem, cancellationToken) + | _ -> false + | _ -> false type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, initialDoc: Document, thisSymbolUseRange: range) = @@ -762,39 +700,31 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, } member _.TryGoToDefinition(position, cancellationToken) = - let gtd = GoToDefinition(metadataAsSource) - let statusBar = StatusBar() - let gtdTask = gtd.FindDefinitionTask(initialDoc, position, cancellationToken) - - // Wrap this in a try/with as if the user clicks "Cancel" on the thread dialog, we'll be cancelled. - // Task.Wait throws an exception if the task is cancelled, so be sure to catch it. try - // This call to Wait() is fine because we want to be able to provide the error message in the status bar. - use _ = - TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.GoToDefinition, [||]) - - gtdTask.Wait(cancellationToken) - - if gtdTask.Status = TaskStatus.RanToCompletion && gtdTask.Result.IsSome then - match gtdTask.Result.Value with - | FSharpGoToDefinitionResult.NavigableItem (navItem), _ -> - gtd.NavigateToItem(navItem, cancellationToken) |> ignore - // 'true' means do it, like Sheev Palpatine would want us to. - true - | FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences), _ -> - gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, cancellationToken, statusBar) - // 'true' means do it, like Sheev Palpatine would want us to. - true - else - statusBar.TempMessage(SR.CannotDetermineSymbol()) - false - with exc -> - TelemetryReporter.ReportFault(TelemetryEvents.GoToDefinition, FaultSeverity.General, exc) - statusBar.TempMessage(String.Format(SR.NavigateToFailed(), Exception.flattenMessage exc)) - - // Don't show the dialog box as it's most likely that the user cancelled. - // Don't make them click twice. - true + let gtd = GoToDefinition(metadataAsSource) + let gtdTask = + cancellableTask { + use _ = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.GoToDefinition, [||]) + let! definition = gtd.FindDefinitionAsync(initialDoc, position) + let! cancellationToken = CancellableTask.getCancellationToken () + + match definition with + | ValueSome (FSharpGoToDefinitionResult.NavigableItem (navItem), _) -> + gtd.NavigateToItem(navItem, cancellationToken) |> ignore + return true + | ValueSome (FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences), _) -> + gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, cancellationToken) |> ignore + return true + | _ -> return false + } + ThreadHelper.JoinableTaskFactory.Run( + SR.NavigatingTo(), + (fun _ ct -> + let cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, ct) + CancellableTask.start cts.Token gtdTask), + TimeSpan.FromSeconds 1) + with :? OperationCanceledException -> + false [] type internal SymbolMemberType = diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index 9e822e0e26f..3130163b192 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -29,10 +29,5 @@ type internal FSharpGoToDefinitionService [] (metadataAsSo /// Invoked with Go to Definition. /// Try to navigate to the definiton of the symbol at the symbolRange in the originDocument member _.TryGoToDefinition(document: Document, position: int, cancellationToken: CancellationToken) = - let statusBar = StatusBar() - statusBar.Message(SR.LocatingSymbol()) - use __ = statusBar.Animate() - let navigation = FSharpNavigation(metadataAsSource, document, rangeStartup) - navigation.TryGoToDefinition(position, cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs index 32a4d8a9bab..a5e354a74cf 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs @@ -16,6 +16,8 @@ open Microsoft.VisualStudio.Text.Editor open Microsoft.VisualStudio.Utilities open Microsoft.VisualStudio.FSharp.Editor.Telemetry open Microsoft.VisualStudio.Telemetry +open CancellableTasks.CancellableTaskBuilder +open CancellableTasks [] type internal FSharpNavigableSymbol(item: FSharpNavigableItem, span: SnapshotSpan, gtd: GoToDefinition) = @@ -31,7 +33,6 @@ type internal FSharpNavigableSymbolSource(metadataAsSource) = let mutable disposed = false let gtd = GoToDefinition(metadataAsSource) - let statusBar = StatusBar() interface INavigableSymbolSource with member _.GetNavigableSymbolAsync(triggerSpan: SnapshotSpan, cancellationToken: CancellationToken) = @@ -39,69 +40,56 @@ type internal FSharpNavigableSymbolSource(metadataAsSource) = if disposed then null else - asyncMaybe { + cancellableTask { + use _ = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.GoToDefinitionGetSymbol, [||]) + let snapshot = triggerSpan.Snapshot let position = triggerSpan.Start.Position let document = snapshot.GetOpenDocumentInCurrentContextWithChanges() - let! sourceText = document.GetTextAsync(cancellationToken) |> liftTaskAsync - - statusBar.Message(SR.LocatingSymbol()) - use _ = statusBar.Animate() - - let gtdTask = gtd.FindDefinitionTask(document, position, cancellationToken) + let! cancellationToken = CancellableTask.getCancellationToken () + let! sourceText = document.GetTextAsync(cancellationToken) - // Wrap this in a try/with as if the user clicks "Cancel" on the thread dialog, we'll be cancelled. - // Task.Wait throws an exception if the task is cancelled, so be sure to catch it. try - // This call to Wait() is fine because we want to be able to provide the error message in the status bar. - use _ = - TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.GoToDefinitionGetSymbol, [||]) + let! definition = gtd.FindDefinitionAsync(document, position) - gtdTask.Wait(cancellationToken) - statusBar.Clear() - - if gtdTask.Status = TaskStatus.RanToCompletion && gtdTask.Result.IsSome then - let result, range = gtdTask.Result.Value + match definition with + | ValueNone -> return null + | ValueSome (result, range) -> + let declarationTextSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range) let declarationSpan = Span(declarationTextSpan.Start, declarationTextSpan.Length) let symbolSpan = SnapshotSpan(snapshot, declarationSpan) match result with - | FSharpGoToDefinitionResult.NavigableItem (navItem) -> - return FSharpNavigableSymbol(navItem, symbolSpan, gtd) :> INavigableSymbol - - | FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences) -> - let nav = - { new INavigableSymbol with - member _.Navigate(_: INavigableRelationship) = - // Need to new up a CTS here instead of re-using the other one, since VS - // will navigate disconnected from the outer routine, leading to an - // OperationCancelledException if you use the one defined outside. - use ct = new CancellationTokenSource() - gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, ct.Token, statusBar) - - member _.Relationships = seq { yield PredefinedNavigableRelationships.Definition } - - member _.SymbolSpan = symbolSpan - } - - return nav - else - statusBar.TempMessage(SR.CannotDetermineSymbol()) - - // The NavigableSymbols API accepts 'null' when there's nothing to navigate to. - return null - with exc -> - TelemetryReporter.ReportFault(TelemetryEvents.GoToDefinitionGetSymbol, FaultSeverity.General, exc) + | FSharpGoToDefinitionResult.NavigableItem (navItem) -> + return FSharpNavigableSymbol(navItem, symbolSpan, gtd) :> INavigableSymbol - statusBar.TempMessage(String.Format(SR.NavigateToFailed(), Exception.flattenMessage exc)) + | FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences) -> + let nav = + { new INavigableSymbol with + member _.Navigate(_: INavigableRelationship) = + // Need to new up a CTS here instead of re-using the other one, since VS + // will navigate disconnected from the outer routine, leading to an + // OperationCancelledException if you use the one defined outside. + // TODO: review if it's a proper way of doing it + use ct = new CancellationTokenSource() + do gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, ct.Token) |> ignore + member _.Relationships = seq { yield PredefinedNavigableRelationships.Definition } + + member _.SymbolSpan = symbolSpan + } + + return nav + + with exc -> + TelemetryReporter.ReportFault(TelemetryEvents.GoToDefinitionGetSymbol, FaultSeverity.General, exc) // The NavigableSymbols API accepts 'null' when there's nothing to navigate to. return null } - |> Async.map Option.toObj - |> RoslynHelpers.StartAsyncAsTask cancellationToken + |> CancellableTask.start cancellationToken member _.Dispose() = disposed <- true From 18b07a52b00295e2992e98c3345ba10ee7ce2097 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 11 Sep 2023 17:12:15 +0200 Subject: [PATCH 093/222] wip: cache semantic highlightings for opened docs --- .../Classification/ClassificationService.fs | 81 ++++++++++++++----- .../src/FSharp.Editor/Common/DocumentCache.fs | 3 + 2 files changed, 63 insertions(+), 21 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 8900417cdc0..31dcd549109 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -111,7 +111,7 @@ type internal FSharpClassificationService [] () = | true, items -> items | _ -> let items = ResizeArray() - lookup.[dataItem.Range.StartLine] <- items + lookup[dataItem.Range.StartLine] <- items items items.Add dataItem @@ -120,8 +120,25 @@ type internal FSharpClassificationService [] () = lookup :> IReadOnlyDictionary<_, _> - let semanticClassificationCache = - new DocumentCache("fsharp-semantic-classification-cache") + static let itemTosemanticClassificationLookup (d: SemanticClassificationItem array) = + let lookup = Dictionary>() + for item in d do + let items = + let startLine = item.Range.StartLine + match lookup.TryGetValue startLine with + | true, items -> items + | _ -> + let items = ResizeArray() + lookup[startLine] <- items + items + items.Add item + lookup :> IReadOnlyDictionary<_, _> + + static let unopenedDocumentsSemanticClassificationCache = + new DocumentCache("fsharp-unopened-documents-semantic-classification-cache", 5.) + + static let openedDocumentsSemanticClassificationCache = + new DocumentCache("fsharp-opened-documents-semantic-classification-cache", 2.) interface IFSharpClassificationService with // Do not perform classification if we don't have project options (#defines matter) @@ -197,7 +214,7 @@ type internal FSharpClassificationService [] () = let isOpenDocument = document.Project.Solution.Workspace.IsDocumentOpen document.Id if not isOpenDocument then - match! semanticClassificationCache.TryGetValueAsync document with + match! unopenedDocumentsSemanticClassificationCache.TryGetValueAsync document with | ValueSome classificationDataLookup -> let eventProps: (string * obj) array = [| @@ -212,7 +229,7 @@ type internal FSharpClassificationService [] () = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result - | _ -> + | ValueNone -> let eventProps: (string * obj) array = [| "context.document.project.id", document.Project.Id.Id.ToString() @@ -227,28 +244,50 @@ type internal FSharpClassificationService [] () = let! classificationData = document.GetFSharpSemanticClassificationAsync(nameof (FSharpClassificationService)) let classificationDataLookup = toSemanticClassificationLookup classificationData - do! semanticClassificationCache.SetAsync(document, classificationDataLookup) + do! unopenedDocumentsSemanticClassificationCache.SetAsync(document, classificationDataLookup) addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result else - let eventProps: (string * obj) array = - [| - "context.document.project.id", document.Project.Id.Id.ToString() - "context.document.id", document.Id.Id.ToString() - "isOpenDocument", isOpenDocument - "textSpanLength", textSpan.Length - "cacheHit", false - |] - use _eventDuration = - TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) + match! openedDocumentsSemanticClassificationCache.TryGetValueAsync document with + | ValueSome classificationDataLookup -> + let eventProps: (string * obj) array = + [| + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + "isOpenDocument", isOpenDocument + "textSpanLength", textSpan.Length + "cacheHit", true + |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) + + addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result + | ValueNone -> + + let eventProps: (string * obj) array = + [| + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + "isOpenDocument", isOpenDocument + "textSpanLength", textSpan.Length + "cacheHit", false + |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) + + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof (IFSharpClassificationService)) + + let targetRange = + RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) - let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof (IFSharpClassificationService)) + let classificationData = checkResults.GetSemanticClassification(Some targetRange) - let targetRange = - RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) + let classificationDataLookup = itemTosemanticClassificationLookup classificationData + do! unopenedDocumentsSemanticClassificationCache.SetAsync(document, classificationDataLookup) - let classificationData = checkResults.GetSemanticClassification(Some targetRange) - addSemanticClassification sourceText textSpan classificationData result + addSemanticClassification sourceText textSpan classificationData result } |> CancellableTask.startAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs b/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs index f812e5f8b0f..6c88bc0f912 100644 --- a/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs +++ b/vsintegration/src/FSharp.Editor/Common/DocumentCache.fs @@ -16,6 +16,9 @@ type DocumentCache<'Value when 'Value: not struct>(name: string, ?cacheItemPolic let policy = defaultArg cacheItemPolicy (CacheItemPolicy(SlidingExpiration = (TimeSpan.FromSeconds defaultSlidingExpiration))) + new(name: string, slidingExpirationSeconds: float) = + new DocumentCache<'Value>(name, CacheItemPolicy(SlidingExpiration = (TimeSpan.FromSeconds slidingExpirationSeconds))) + member _.TryGetValueAsync(doc: Document) = cancellableTask { let! ct = CancellableTask.getCancellationToken () From 98c2a5abfea4a465ac385f4c6ba2dfdcaf64475e Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 11 Sep 2023 19:28:44 +0200 Subject: [PATCH 094/222] more wip --- .../Diagnostics/DocumentDiagnosticAnalyzer.fs | 6 +- .../FSharpAnalysisSaveFileCommandHandler.fs | 73 ++++++++-------- .../FSharpProjectOptionsManager.fs | 85 ++++++++++--------- .../LanguageService/WorkspaceExtensions.fs | 8 +- 4 files changed, 87 insertions(+), 85 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 1e547e86335..e1ff85c4ee6 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -16,7 +16,7 @@ open FSharp.Compiler.Diagnostics open CancellableTasks open Microsoft.VisualStudio.FSharp.Editor.Telemetry -[] +[] type internal DiagnosticsType = | Syntax | Semantic @@ -125,12 +125,12 @@ type internal FSharpDocumentDiagnosticAnalyzer [] () = if document.Project.IsFSharpMetadata then Task.FromResult ImmutableArray.Empty else - cancellableTask { return! FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) } + FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) |> CancellableTask.start cancellationToken member _.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken) : Task> = if document.Project.IsFSharpMiscellaneousOrMetadata && not document.IsFSharpScript then Task.FromResult ImmutableArray.Empty else - cancellableTask { return! FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) } + FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) |> CancellableTask.start cancellationToken diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpAnalysisSaveFileCommandHandler.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpAnalysisSaveFileCommandHandler.fs index 30baf56eb3e..f6388c590ca 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpAnalysisSaveFileCommandHandler.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpAnalysisSaveFileCommandHandler.fs @@ -12,6 +12,7 @@ open Microsoft.VisualStudio.FSharp.Editor.Logging open Microsoft.VisualStudio.Text.Editor.Commanding.Commands open Microsoft.VisualStudio.Commanding open Microsoft.VisualStudio.Utilities +open CancellableTasks // This causes re-analysis to happen when a F# document is saved. // We do this because FCS relies on the file system and existing open documents @@ -45,43 +46,41 @@ type internal FSharpAnalysisSaveFileCommandHandler [] (ana | null -> () | _ -> let document = solution.GetDocument(documentId) - - async { - try - if document.Project.Language = LanguageNames.FSharp then - let openDocIds = workspace.GetOpenDocumentIds() - - let docIdsToReanalyze = - if document.IsFSharpScript then - openDocIds - |> Seq.filter (fun x -> - x <> document.Id - && (let doc = solution.GetDocument(x) - - match doc with - | null -> false - | _ -> doc.IsFSharpScript)) - |> Array.ofSeq - else - let depProjIds = document.Project.GetDependentProjectIds().Add(document.Project.Id) - - openDocIds - |> Seq.filter (fun x -> - depProjIds.Contains(x.ProjectId) - && x <> document.Id - && (let doc = solution.GetDocument(x) - - match box doc with - | null -> false - | _ -> doc.Project.Language = LanguageNames.FSharp)) - |> Array.ofSeq - - if docIdsToReanalyze.Length > 0 then - analyzerService.Reanalyze(workspace, documentIds = docIdsToReanalyze) - with ex -> - logException ex - } - |> Async.Start // fire and forget + if document.Project.Language <> LanguageNames.FSharp then + () + else + cancellableTask { + try + if document.Project.Language = LanguageNames.FSharp then + let openDocIds = workspace.GetOpenDocumentIds() + + let docIdsToReanalyze = + if document.IsFSharpScript then + [| for x in openDocIds do + if x <> document.Id + && (let doc = solution.GetDocument(x) + match doc with + | null -> false + | _ -> doc.IsFSharpScript) then + yield x |] + else + let depProjIds = document.Project.GetDependentProjectIds().Add(document.Project.Id) + + [| for x in openDocIds do + if depProjIds.Contains(x.ProjectId) + && x <> document.Id + && (let doc = solution.GetDocument(x) + match box doc with + | null -> false + | _ -> doc.Project.Language = LanguageNames.FSharp) then + yield x |] + + if docIdsToReanalyze.Length > 0 then + analyzerService.Reanalyze(workspace, documentIds = docIdsToReanalyze) + with ex -> + Telemetry.TelemetryReporter.ReportFault("FSharpAnalysisSaveFileCommandHandler.ExecuteCommand", e = ex) + logException ex + } |> CancellableTask.startWithoutCancellation |> ignore // fire and forget nextCommandHandler.Invoke() diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 345bd3d707f..7834be87c52 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -15,6 +15,7 @@ open Microsoft.VisualStudio.FSharp.Editor open System.Threading open Microsoft.VisualStudio.FSharp.Interactive.Session open System.Runtime.CompilerServices +open CancellableTasks [] module private FSharpProjectOptionsHelpers = @@ -55,7 +56,7 @@ module private FSharpProjectOptionsHelpers = and set (v) = errorReporter <- v } - let hasProjectVersionChanged (oldProject: Project) (newProject: Project) = + let inline hasProjectVersionChanged (oldProject: Project) (newProject: Project) = oldProject.Version <> newProject.Version let hasDependentVersionChanged (oldProject: Project) (newProject: Project) (ct: CancellationToken) = @@ -97,10 +98,10 @@ module private FSharpProjectOptionsHelpers = type private FSharpProjectOptionsMessage = | TryGetOptionsByDocument of Document * - AsyncReplyChannel<(FSharpParsingOptions * FSharpProjectOptions) option> * + AsyncReplyChannel<(FSharpParsingOptions * FSharpProjectOptions) voption> * CancellationToken * userOpName: string - | TryGetOptionsByProject of Project * AsyncReplyChannel<(FSharpParsingOptions * FSharpProjectOptions) option> * CancellationToken + | TryGetOptionsByProject of Project * AsyncReplyChannel<(FSharpParsingOptions * FSharpProjectOptions) voption> * CancellationToken | ClearOptions of ProjectId | ClearSingleFileOptionsCache of DocumentId @@ -188,13 +189,14 @@ type private FSharpProjectOptionsReactor(checker: FSharpChecker) = weakPEReferences.Add(comp, fsRefProj) fsRefProj - let rec tryComputeOptionsBySingleScriptOrFile (document: Document) (ct: CancellationToken) userOpName = - async { - let! fileStamp = document.GetTextVersionAsync(ct) |> Async.AwaitTask + let rec tryComputeOptionsBySingleScriptOrFile (document: Document) userOpName = + cancellableTask { + let! ct = CancellableTask.getCancellationToken () + let! fileStamp = document.GetTextVersionAsync(ct) match singleFileCache.TryGetValue(document.Id) with | false, _ -> - let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask + let! sourceText = document.GetTextAsync(ct) let! scriptProjectOptions, _ = checker.GetProjectOptionsFromScript( @@ -243,29 +245,30 @@ type private FSharpProjectOptionsReactor(checker: FSharpChecker) = singleFileCache.[document.Id] <- (document.Project, fileStamp, parsingOptions, projectOptions) - return Some(parsingOptions, projectOptions) + return ValueSome(parsingOptions, projectOptions) | true, (oldProject, oldFileStamp, parsingOptions, projectOptions) -> if fileStamp <> oldFileStamp || isProjectInvalidated document.Project oldProject ct then singleFileCache.TryRemove(document.Id) |> ignore - return! tryComputeOptionsBySingleScriptOrFile document ct userOpName + return! tryComputeOptionsBySingleScriptOrFile document userOpName else - return Some(parsingOptions, projectOptions) + return ValueSome(parsingOptions, projectOptions) } let tryGetProjectSite (project: Project) = // Cps if commandLineOptions.ContainsKey project.Id then - Some(mapCpsProjectToSite (project, commandLineOptions)) + ValueSome(mapCpsProjectToSite (project, commandLineOptions)) else // Legacy match legacyProjectSites.TryGetValue project.Id with - | true, site -> Some site - | _ -> None + | true, site -> ValueSome site + | _ -> ValueNone - let rec tryComputeOptions (project: Project) ct = - async { + let rec tryComputeOptions (project: Project) = + cancellableTask { let projectId = project.Id + let! ct = CancellableTask.getCancellationToken () match cache.TryGetValue(projectId) with | false, _ -> @@ -276,29 +279,29 @@ type private FSharpProjectOptionsReactor(checker: FSharpChecker) = let referencedProjects = ResizeArray() + if project.AreFSharpInMemoryCrossProjectReferencesEnabled then for projectReference in project.ProjectReferences do let referencedProject = project.Solution.GetProject(projectReference.ProjectId) if referencedProject.Language = FSharpConstants.FSharpLanguageName then - match! tryComputeOptions referencedProject ct with - | None -> canBail <- true - | Some (_, projectOptions) -> + match! tryComputeOptions referencedProject with + | ValueNone -> canBail <- true + | ValueSome (_, projectOptions) -> referencedProjects.Add( FSharpReferencedProject.FSharpReference(referencedProject.OutputFilePath, projectOptions) ) elif referencedProject.SupportsCompilation then - let! comp = referencedProject.GetCompilationAsync(ct) |> Async.AwaitTask + let! comp = referencedProject.GetCompilationAsync(ct) let peRef = createPEReference referencedProject comp referencedProjects.Add(peRef) if canBail then - return None + return ValueNone else - match tryGetProjectSite project with - | None -> return None - | Some projectSite -> + | ValueNone -> return ValueNone + | ValueSome projectSite -> let otherOptions = [| @@ -321,7 +324,7 @@ type private FSharpProjectOptionsReactor(checker: FSharpChecker) = "--ignorelinedirectives" |] - let! ver = project.GetDependentVersionAsync(ct) |> Async.AwaitTask + let! ver = project.GetDependentVersionAsync(ct) let projectOptions = { @@ -340,7 +343,7 @@ type private FSharpProjectOptionsReactor(checker: FSharpChecker) = // This can happen if we didn't receive the callback from HandleCommandLineChanges yet. if Array.isEmpty projectOptions.SourceFiles then - return None + return ValueNone else // Clear any caches that need clearing and invalidate the project. let currentSolution = project.Solution.Workspace.CurrentSolution @@ -371,14 +374,14 @@ type private FSharpProjectOptionsReactor(checker: FSharpChecker) = cache.[projectId] <- (project, parsingOptions, projectOptions) - return Some(parsingOptions, projectOptions) + return ValueSome(parsingOptions, projectOptions) | true, (oldProject, parsingOptions, projectOptions) -> if isProjectInvalidated oldProject project ct then cache.TryRemove(projectId) |> ignore return! tryComputeOptions project ct else - return Some(parsingOptions, projectOptions) + return ValueSome(parsingOptions, projectOptions) } let loop (agent: MailboxProcessor) = @@ -387,17 +390,17 @@ type private FSharpProjectOptionsReactor(checker: FSharpChecker) = match! agent.Receive() with | FSharpProjectOptionsMessage.TryGetOptionsByDocument (document, reply, ct, userOpName) -> if ct.IsCancellationRequested then - reply.Reply None + reply.Reply ValueNone else try // For now, disallow miscellaneous workspace since we are using the hacky F# miscellaneous files project. if document.Project.Solution.Workspace.Kind = WorkspaceKind.MiscellaneousFiles then - reply.Reply None + reply.Reply ValueNone elif document.Project.IsFSharpMiscellaneousOrMetadata then - let! options = tryComputeOptionsBySingleScriptOrFile document ct userOpName + let! options = tryComputeOptionsBySingleScriptOrFile document userOpName |> CancellableTask.start ct |> Async.AwaitTask if ct.IsCancellationRequested then - reply.Reply None + reply.Reply ValueNone else reply.Reply options else @@ -407,43 +410,43 @@ type private FSharpProjectOptionsReactor(checker: FSharpChecker) = document.Project.Solution.Workspace.CurrentSolution.GetProject(document.Project.Id) if not (isNull project) then - let! options = tryComputeOptions project ct + let! options = tryComputeOptions project |> CancellableTask.start ct |> Async.AwaitTask if ct.IsCancellationRequested then - reply.Reply None + reply.Reply ValueNone else reply.Reply options else - reply.Reply None + reply.Reply ValueNone with _ -> - reply.Reply None + reply.Reply ValueNone | FSharpProjectOptionsMessage.TryGetOptionsByProject (project, reply, ct) -> if ct.IsCancellationRequested then - reply.Reply None + reply.Reply ValueNone else try if project.Solution.Workspace.Kind = WorkspaceKind.MiscellaneousFiles || project.IsFSharpMiscellaneousOrMetadata then - reply.Reply None + reply.Reply ValueNone else // We only care about the latest project in the workspace's solution. // We do this to prevent any possible cache thrashing in FCS. let project = project.Solution.Workspace.CurrentSolution.GetProject(project.Id) if not (isNull project) then - let! options = tryComputeOptions project ct + let! options = tryComputeOptions project |> CancellableTask.start ct |> Async.AwaitTask if ct.IsCancellationRequested then - reply.Reply None + reply.Reply ValueNone else reply.Reply options else - reply.Reply None + reply.Reply ValueNone with _ -> - reply.Reply None + reply.Reply ValueNone | FSharpProjectOptionsMessage.ClearOptions (projectId) -> match cache.TryRemove(projectId) with diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 77d89a7d7a0..a13b116c4ab 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -152,8 +152,8 @@ type Document with let! ct = CancellableTask.getCancellationToken () match! projectOptionsManager.TryGetOptionsForDocumentOrProject(this, ct, userOpName) with - | None -> return raise (OperationCanceledException("FSharp project options not found.")) - | Some (parsingOptions, projectOptions) -> + | ValueNone -> return raise (OperationCanceledException("FSharp project options not found.")) + | ValueSome (parsingOptions, projectOptions) -> let result = (service.Checker, projectOptionsManager, parsingOptions, projectOptions) @@ -344,8 +344,8 @@ type Project with let projectOptionsManager = service.FSharpProjectOptionsManager match! projectOptionsManager.TryGetOptionsByProject(this, ct) with - | None -> return raise (OperationCanceledException("FSharp project options not found.")) - | Some (parsingOptions, projectOptions) -> + | ValueNone -> return raise (OperationCanceledException("FSharp project options not found.")) + | ValueSome (parsingOptions, projectOptions) -> let result = (service.Checker, projectOptionsManager, parsingOptions, projectOptions) From 73877dc643591cddc42b2500db32e6ba12cf9a69 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 12 Sep 2023 14:27:30 +0200 Subject: [PATCH 095/222] wip --- .../Classification/ClassificationService.fs | 8 +++- .../FSharpAnalysisSaveFileCommandHandler.fs | 46 ++++++++++++------- .../FSharpProjectOptionsManager.fs | 8 ++-- .../Navigation/GoToDefinition.fs | 25 +++++----- .../Navigation/NavigableSymbolsService.fs | 44 +++++++++--------- .../Navigation/NavigateToSearchService.fs | 8 +--- 6 files changed, 78 insertions(+), 61 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 31dcd549109..35eefa24828 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -120,18 +120,22 @@ type internal FSharpClassificationService [] () = lookup :> IReadOnlyDictionary<_, _> - static let itemTosemanticClassificationLookup (d: SemanticClassificationItem array) = + static let itemToSemanticClassificationLookup (d: SemanticClassificationItem array) = let lookup = Dictionary>() + for item in d do let items = let startLine = item.Range.StartLine + match lookup.TryGetValue startLine with | true, items -> items | _ -> let items = ResizeArray() lookup[startLine] <- items items + items.Add item + lookup :> IReadOnlyDictionary<_, _> static let unopenedDocumentsSemanticClassificationCache = @@ -284,7 +288,7 @@ type internal FSharpClassificationService [] () = let classificationData = checkResults.GetSemanticClassification(Some targetRange) - let classificationDataLookup = itemTosemanticClassificationLookup classificationData + let classificationDataLookup = itemToSemanticClassificationLookup classificationData do! unopenedDocumentsSemanticClassificationCache.SetAsync(document, classificationDataLookup) addSemanticClassification sourceText textSpan classificationData result diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpAnalysisSaveFileCommandHandler.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpAnalysisSaveFileCommandHandler.fs index f6388c590ca..1b44c731e7a 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpAnalysisSaveFileCommandHandler.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpAnalysisSaveFileCommandHandler.fs @@ -46,41 +46,53 @@ type internal FSharpAnalysisSaveFileCommandHandler [] (ana | null -> () | _ -> let document = solution.GetDocument(documentId) + if document.Project.Language <> LanguageNames.FSharp then () else cancellableTask { try - if document.Project.Language = LanguageNames.FSharp then - let openDocIds = workspace.GetOpenDocumentIds() + let openDocIds = workspace.GetOpenDocumentIds() - let docIdsToReanalyze = - if document.IsFSharpScript then - [| for x in openDocIds do - if x <> document.Id + let docIdsToReanalyze = + if document.IsFSharpScript then + [| + for x in openDocIds do + if + x <> document.Id && (let doc = solution.GetDocument(x) + match doc with | null -> false - | _ -> doc.IsFSharpScript) then - yield x |] - else - let depProjIds = document.Project.GetDependentProjectIds().Add(document.Project.Id) + | _ -> doc.IsFSharpScript) + then + yield x + |] + else + let depProjIds = document.Project.GetDependentProjectIds().Add(document.Project.Id) - [| for x in openDocIds do - if depProjIds.Contains(x.ProjectId) + [| + for x in openDocIds do + if + depProjIds.Contains(x.ProjectId) && x <> document.Id && (let doc = solution.GetDocument(x) + match box doc with | null -> false - | _ -> doc.Project.Language = LanguageNames.FSharp) then - yield x |] + | _ -> doc.Project.Language = LanguageNames.FSharp) + then + yield x + |] - if docIdsToReanalyze.Length > 0 then - analyzerService.Reanalyze(workspace, documentIds = docIdsToReanalyze) + if docIdsToReanalyze.Length > 0 then + analyzerService.Reanalyze(workspace, documentIds = docIdsToReanalyze) with ex -> Telemetry.TelemetryReporter.ReportFault("FSharpAnalysisSaveFileCommandHandler.ExecuteCommand", e = ex) logException ex - } |> CancellableTask.startWithoutCancellation |> ignore // fire and forget + } + |> CancellableTask.startWithoutCancellation + |> ignore // fire and forget nextCommandHandler.Invoke() diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 7834be87c52..4fcc180201b 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -279,7 +279,6 @@ type private FSharpProjectOptionsReactor(checker: FSharpChecker) = let referencedProjects = ResizeArray() - if project.AreFSharpInMemoryCrossProjectReferencesEnabled then for projectReference in project.ProjectReferences do let referencedProject = project.Solution.GetProject(projectReference.ProjectId) @@ -324,7 +323,7 @@ type private FSharpProjectOptionsReactor(checker: FSharpChecker) = "--ignorelinedirectives" |] - let! ver = project.GetDependentVersionAsync(ct) + let! ver = project.GetDependentVersionAsync(ct) let projectOptions = { @@ -397,7 +396,10 @@ type private FSharpProjectOptionsReactor(checker: FSharpChecker) = if document.Project.Solution.Workspace.Kind = WorkspaceKind.MiscellaneousFiles then reply.Reply ValueNone elif document.Project.IsFSharpMiscellaneousOrMetadata then - let! options = tryComputeOptionsBySingleScriptOrFile document userOpName |> CancellableTask.start ct |> Async.AwaitTask + let! options = + tryComputeOptionsBySingleScriptOrFile document userOpName + |> CancellableTask.start ct + |> Async.AwaitTask if ct.IsCancellationRequested then reply.Reply ValueNone diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 3f242c63b68..3301faa30ce 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -459,19 +459,15 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = /// Navigate to the positon of the textSpan in the provided document /// used by quickinfo link navigation when the tooltip contains the correct destination range. - member _.TryNavigateToTextSpan - ( - document: Document, - textSpan: TextSpan, - cancellationToken: CancellationToken - ) = + member _.TryNavigateToTextSpan(document: Document, textSpan: TextSpan, cancellationToken: CancellationToken) = let navigableItem = FSharpGoToDefinitionNavigableItem(document, textSpan) let workspace = document.Project.Solution.Workspace let navigationService = workspace.Services.GetService() - navigationService.TryNavigateToSpan(workspace, navigableItem.Document.Id, navigableItem.SourceSpan, cancellationToken) |> ignore + navigationService.TryNavigateToSpan(workspace, navigableItem.Document.Id, navigableItem.SourceSpan, cancellationToken) + |> ignore member _.NavigateToItem(navigableItem: FSharpNavigableItem, cancellationToken: CancellationToken) = @@ -702,27 +698,34 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, member _.TryGoToDefinition(position, cancellationToken) = try let gtd = GoToDefinition(metadataAsSource) + let gtdTask = cancellableTask { - use _ = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.GoToDefinition, [||]) + use _ = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.GoToDefinition, [||]) + let! definition = gtd.FindDefinitionAsync(initialDoc, position) let! cancellationToken = CancellableTask.getCancellationToken () - + match definition with | ValueSome (FSharpGoToDefinitionResult.NavigableItem (navItem), _) -> gtd.NavigateToItem(navItem, cancellationToken) |> ignore return true | ValueSome (FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences), _) -> - gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, cancellationToken) |> ignore + gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, cancellationToken) + |> ignore + return true | _ -> return false } + ThreadHelper.JoinableTaskFactory.Run( SR.NavigatingTo(), (fun _ ct -> let cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, ct) CancellableTask.start cts.Token gtdTask), - TimeSpan.FromSeconds 1) + TimeSpan.FromSeconds 1 + ) with :? OperationCanceledException -> false diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs index a5e354a74cf..6d918913fc5 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs @@ -56,33 +56,35 @@ type internal FSharpNavigableSymbolSource(metadataAsSource) = match definition with | ValueNone -> return null | ValueSome (result, range) -> - let declarationTextSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range) let declarationSpan = Span(declarationTextSpan.Start, declarationTextSpan.Length) let symbolSpan = SnapshotSpan(snapshot, declarationSpan) match result with - | FSharpGoToDefinitionResult.NavigableItem (navItem) -> - return FSharpNavigableSymbol(navItem, symbolSpan, gtd) :> INavigableSymbol - - | FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences) -> - let nav = - { new INavigableSymbol with - member _.Navigate(_: INavigableRelationship) = - // Need to new up a CTS here instead of re-using the other one, since VS - // will navigate disconnected from the outer routine, leading to an - // OperationCancelledException if you use the one defined outside. - // TODO: review if it's a proper way of doing it - use ct = new CancellationTokenSource() - do gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, ct.Token) |> ignore - - member _.Relationships = seq { yield PredefinedNavigableRelationships.Definition } - - member _.SymbolSpan = symbolSpan - } - - return nav + | FSharpGoToDefinitionResult.NavigableItem (navItem) -> + return FSharpNavigableSymbol(navItem, symbolSpan, gtd) :> INavigableSymbol + + | FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences) -> + let nav = + { new INavigableSymbol with + member _.Navigate(_: INavigableRelationship) = + // Need to new up a CTS here instead of re-using the other one, since VS + // will navigate disconnected from the outer routine, leading to an + // OperationCancelledException if you use the one defined outside. + // TODO: review if it's a proper way of doing it + use ct = new CancellationTokenSource() + + do + gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, ct.Token) + |> ignore + + member _.Relationships = seq { yield PredefinedNavigableRelationships.Definition } + + member _.SymbolSpan = symbolSpan + } + + return nav with exc -> TelemetryReporter.ReportFault(TelemetryEvents.GoToDefinitionGetSymbol, FaultSeverity.General, exc) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index e901e0c0113..8cce297ac0d 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -220,13 +220,7 @@ type internal FSharpNavigateToSearchService [] } |> CancellableTask.start cancellationToken - member _.SearchDocumentAsync - ( - document: Document, - searchPattern, - kinds, - cancellationToken - ) : Task> = + member _.SearchDocumentAsync(document: Document, searchPattern, kinds, cancellationToken) = cancellableTask { let! result = processDocument (createMatcherFor searchPattern) kinds document return Array.toImmutableArray result From 524c12c0ba8eee57700f3322ccae506c6fc88f6f Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 12 Sep 2023 17:05:44 +0200 Subject: [PATCH 096/222] wip --- .../Navigation/GoToDefinition.fs | 52 ++++++++----------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 3301faa30ce..d57b15f7fbc 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -696,37 +696,31 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, } member _.TryGoToDefinition(position, cancellationToken) = + // Once we migrate to Roslyn-exposed MAAS and sourcelink (https://github.com/dotnet/fsharp/issues/13951), this can be a "normal" task + // Wrap this in a try/with as if the user clicks "Cancel" on the thread dialog, we'll be cancelled. + // Task.Wait throws an exception if the task is cancelled, so be sure to catch it. try - let gtd = GoToDefinition(metadataAsSource) - - let gtdTask = - cancellableTask { - use _ = - TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.GoToDefinition, [||]) - - let! definition = gtd.FindDefinitionAsync(initialDoc, position) - let! cancellationToken = CancellableTask.getCancellationToken () - - match definition with - | ValueSome (FSharpGoToDefinitionResult.NavigableItem (navItem), _) -> - gtd.NavigateToItem(navItem, cancellationToken) |> ignore - return true - | ValueSome (FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences), _) -> - gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, cancellationToken) - |> ignore + use _ = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.GoToDefinition, [||]) - return true - | _ -> return false - } - - ThreadHelper.JoinableTaskFactory.Run( - SR.NavigatingTo(), - (fun _ ct -> - let cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, ct) - CancellableTask.start cts.Token gtdTask), - TimeSpan.FromSeconds 1 - ) - with :? OperationCanceledException -> + let gtd = GoToDefinition(metadataAsSource) + let gtdTask = gtd.FindDefinitionAsync(initialDoc, position) cancellationToken + + gtdTask.Wait() + + if gtdTask.Status = TaskStatus.RanToCompletion && gtdTask.Result.IsSome then + match gtdTask.Result with + | ValueSome (FSharpGoToDefinitionResult.NavigableItem (navItem), _) -> + gtd.NavigateToItem(navItem, cancellationToken) |> ignore + true + | ValueSome (FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences), _) -> + gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, cancellationToken) |> ignore + true + | _ -> false + else + false + with exc -> + TelemetryReporter.ReportFault(TelemetryEvents.GoToDefinition, FaultSeverity.General, exc) false [] From a6a16640abea427300aa1a203911da14660a5016 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 12 Sep 2023 18:00:20 +0200 Subject: [PATCH 097/222] fantomas --- .../src/FSharp.Editor/Navigation/GoToDefinition.fs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index d57b15f7fbc..a82a58e7406 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -704,7 +704,7 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.GoToDefinition, [||]) let gtd = GoToDefinition(metadataAsSource) - let gtdTask = gtd.FindDefinitionAsync(initialDoc, position) cancellationToken + let gtdTask = gtd.FindDefinitionAsync (initialDoc, position) cancellationToken gtdTask.Wait() @@ -714,7 +714,9 @@ type internal FSharpNavigation(metadataAsSource: FSharpMetadataAsSourceService, gtd.NavigateToItem(navItem, cancellationToken) |> ignore true | ValueSome (FSharpGoToDefinitionResult.ExternalAssembly (targetSymbolUse, metadataReferences), _) -> - gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, cancellationToken) |> ignore + gtd.NavigateToExternalDeclaration(targetSymbolUse, metadataReferences, cancellationToken) + |> ignore + true | _ -> false else From de10ca0a53548f70f338a9945bc7e626baa62126 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 13 Sep 2023 12:54:18 +0200 Subject: [PATCH 098/222] debug info --- src/Compiler/Facilities/AsyncMemoize.fs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 3ccb97cce25..e2755aec57a 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -258,10 +258,10 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers strongList |> Seq.append weakList |> Seq.choose (function - | _, _, _, Strong v -> Some v - | _, _, _, Weak w -> + | _k, version, label, Strong value -> Some (label, version, value) + | _k, version, label, Weak w -> match w.TryGetTarget() with - | true, v -> Some v + | true, value -> Some (label, version, value) | _ -> None) type internal ICacheKey<'TKey, 'TVersion> = @@ -344,7 +344,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let cache = LruCache<'TKey, 'TVersion, Job<'TValue>>( keepStrongly = defaultArg keepStrongly 100, - keepWeakly = defaultArg keepWeakly 100, + keepWeakly = defaultArg keepWeakly 200, requiredToKeep = (function | Running _ -> true @@ -453,6 +453,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore log (Canceled, key) + System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label} version: {key.Version}" else // We need to restart the computation @@ -462,6 +463,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T try log (Started, key) + System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label} version: {key.Version}" let! result = Async.StartAsTask(computation, cancellationToken = cts.Token) post (key, (JobCompleted result)) with @@ -484,6 +486,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore log (Canceled, key) + System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label} version: {key.Version}" | CancelRequest, None | CancelRequest, Some (Completed _) -> () @@ -582,7 +585,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T member _.Locked = lock.Semaphore.CurrentCount < 1 - member _.Running = cache.GetValues() |> Seq.filter (function Running _ -> true | _ -> false) |> Seq.toArray + member _.Running = cache.GetValues() |> Seq.filter (function _, _, Running _ -> true | _ -> false) |> Seq.toArray member this.DebuggerDisplay = let locked = if this.Locked then " [LOCKED]" else "" @@ -590,8 +593,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let valueStats = cache.GetValues() |> Seq.countBy (function - | Running _ -> "Running" - | Completed _ -> "Completed") + | _, _, Running _ -> "Running" + | _, _, Completed _ -> "Completed") |> Seq.map ((<||) (sprintf "%s: %d")) |> String.concat " " From 6c4f126822e212024b923ee9f54a9829854b13a8 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 14 Sep 2023 12:54:20 +0200 Subject: [PATCH 099/222] debug cache version --- src/Compiler/Service/FSharpCheckerResults.fs | 66 +++++++++++++++---- src/Compiler/Service/FSharpCheckerResults.fsi | 38 +++++++++-- src/Compiler/Service/TransparentCompiler.fs | 2 +- 3 files changed, 88 insertions(+), 18 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 98cf78250c1..9f832ad0350 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -275,22 +275,62 @@ type FSharpProjectSnapshot = member this.GetLastModifiedTimeOnDisk() = DateTime.Now - interface ICacheKey with + member this.GetMd5Version() = + Md5Hasher.empty + //|> Md5Hasher.addString this.ProjectFileName + //|> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) + //|> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) + //|> Md5Hasher.addStrings this.OtherOptions + //|> Md5Hasher.addVersions ( + // this.ReferencedProjects + // |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key) + //) + //|> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment + //|> Md5Hasher.addBool this.UseScriptResolutionRules + + member this.GetDebugVersion(): FSharpProjectSnapshotDebugVersion = { + ProjectFileName = this.ProjectFileName + SourceFiles = [for f in this.SourceFiles -> f.FileName, f.Version] + ReferencesOnDisk = this.ReferencesOnDisk + OtherOptions = this.OtherOptions + ReferencedProjects = [for FSharpReference (_, p) in this.ReferencedProjects -> p.WithoutImplFilesThatHaveSignatures.GetDebugVersion()] + IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = this.UseScriptResolutionRules + } + + interface ICacheKey with member this.GetLabel() = this.ToString() member this.GetKey() = this.ProjectFileName + member this.GetVersion() = this.GetDebugVersion() - member this.GetVersion() = - Md5Hasher.empty - |> Md5Hasher.addString this.ProjectFileName - |> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) - |> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) - |> Md5Hasher.addStrings this.OtherOptions - |> Md5Hasher.addVersions ( - this.ReferencedProjects - |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key) - ) - |> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment - |> Md5Hasher.addBool this.UseScriptResolutionRules +and FSharpProjectSnapshotDebugVersion = { + // Note that this may not reduce to just the project directory, because there may be two projects in the same directory. + ProjectFileName: string + + /// The files in the project. + SourceFiles: (string * string) list + + /// Referenced assemblies on disk. + ReferencesOnDisk: ReferenceOnDisk list + + /// Additional command line argument options for the project. + OtherOptions: string list + + /// The command line arguments for the other projects referenced by this project, indexed by the + /// exact text used in the "-r:" reference in FSharpProjectOptions. + ReferencedProjects: FSharpProjectSnapshotDebugVersion list + + /// When true, the typechecking environment is known a priori to be incomplete, for + /// example when a .fs file is opened outside of a project. In this case, the number of error + /// messages reported is reduced. + IsIncompleteTypeCheckEnvironment: bool + + /// When true, use the reference resolution rules for scripts rather than the rules for compiler. + UseScriptResolutionRules: bool + +} + +and FSharpProjectSnapshotVersion = FSharpProjectSnapshotDebugVersion and [] public FSharpReferencedProjectSnapshot = internal diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 42a75d26d62..06331da2241 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -58,6 +58,7 @@ type FSharpFileSnapshot = type ReferenceOnDisk = { Path: string; LastModified: DateTime } + [] type FSharpProjectSnapshot = { @@ -138,11 +139,40 @@ type FSharpProjectSnapshot = member GetLastModifiedTimeOnDisk: unit -> DateTime - member internal Key: ICacheKey + member internal Key: ICacheKey - member internal FileKey: fileName: string -> ICacheKey<(string * string), string> + member internal FileKey: fileName: string -> ICacheKey<(string * string), FSharpProjectSnapshotVersion> - interface ICacheKey + interface ICacheKey + +and FSharpProjectSnapshotDebugVersion = { + // Note that this may not reduce to just the project directory, because there may be two projects in the same directory. + ProjectFileName: string + + /// The files in the project. + SourceFiles: (string * string) list + + /// Referenced assemblies on disk. + ReferencesOnDisk: ReferenceOnDisk list + + /// Additional command line argument options for the project. + OtherOptions: string list + + /// The command line arguments for the other projects referenced by this project, indexed by the + /// exact text used in the "-r:" reference in FSharpProjectOptions. + ReferencedProjects: FSharpProjectSnapshotDebugVersion list + + /// When true, the typechecking environment is known a priori to be incomplete, for + /// example when a .fs file is opened outside of a project. In this case, the number of error + /// messages reported is reduced. + IsIncompleteTypeCheckEnvironment: bool + + /// When true, use the reference resolution rules for scripts rather than the rules for compiler. + UseScriptResolutionRules: bool + +} + +and FSharpProjectSnapshotVersion = FSharpProjectSnapshotDebugVersion and [] public FSharpReferencedProjectSnapshot = internal @@ -167,7 +197,7 @@ and [] public FSharpReferencedProjectSnapshot = static member CreateFSharp: projectOutputFile: string * options: FSharpProjectSnapshot -> FSharpReferencedProjectSnapshot - member internal Key: ICacheKey + member internal Key: ICacheKey /// A set of information describing a project or script build configuration. type public FSharpProjectOptions = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 316f5f000da..01d8520b0db 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1013,7 +1013,7 @@ type internal TransparentCompiler [| Activity.Tags.fileName, fileName |> Path.GetFileName "key", key.GetLabel() - "version", key.GetVersion() + "version", key.GetVersion().ToString() |] let tcConfig = bootstrapInfo.TcConfig From 9116623e1073a632a50a3195a9cbe2e7038c3cb7 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 14 Sep 2023 15:48:51 +0200 Subject: [PATCH 100/222] fix external navigation --- .../src/FSharp.Editor/Common/Extensions.fs | 4 + .../Navigation/GoToDefinition.fs | 127 ++++++++++-------- 2 files changed, 73 insertions(+), 58 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index 87d0007be82..caa87db2286 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -515,6 +515,10 @@ module Exception = |> flattenInner |> String.concat " ---> " +[] +module TextSpan = + let empty = TextSpan() + type Async with static member RunImmediateExceptOnUI(computation: Async<'T>, ?cancellationToken) = diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index a82a58e7406..7623c41c830 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -17,7 +17,6 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Navigation open Microsoft.VisualStudio open Microsoft.VisualStudio.Shell -open Microsoft.VisualStudio.Shell.Interop open Microsoft.VisualStudio.LanguageServices open FSharp.Compiler.CodeAnalysis @@ -30,6 +29,7 @@ open System.Text.RegularExpressions open CancellableTasks open Microsoft.VisualStudio.FSharp.Editor.Telemetry open Microsoft.VisualStudio.Telemetry +open System.Collections.Generic module private Symbol = let fullName (root: ISymbol) : string = @@ -119,6 +119,59 @@ type internal FSharpGoToDefinitionResult = | ExternalAssembly of FSharpSymbolUse * MetadataReference seq type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = + + let rec areTypesEqual (ty1: FSharpType) (ty2: FSharpType) = + let ty1 = ty1.StripAbbreviations() + let ty2 = ty2.StripAbbreviations() + + let generic = + ty1.IsGenericParameter && ty2.IsGenericParameter + || (ty1.GenericArguments.Count = ty2.GenericArguments.Count + && (ty1.GenericArguments, ty2.GenericArguments) ||> Seq.forall2 areTypesEqual) + + if generic then + true + else + let namesEqual = ty1.TypeDefinition.DisplayName = ty2.TypeDefinition.DisplayName + let accessPathsEqual = ty1.TypeDefinition.AccessPath = ty2.TypeDefinition.AccessPath + namesEqual && accessPathsEqual + + let tryFindExternalSymbolUse (targetSymbolUse: FSharpSymbolUse) (x: FSharpSymbolUse) = + match x.Symbol, targetSymbolUse.Symbol with + | (:? FSharpEntity as symbol1), (:? FSharpEntity as symbol2) when x.IsFromDefinition -> + symbol1.DisplayName = symbol2.DisplayName + + | (:? FSharpMemberOrFunctionOrValue as symbol1), (:? FSharpMemberOrFunctionOrValue as symbol2) -> + symbol1.DisplayName = symbol2.DisplayName + && (match symbol1.DeclaringEntity, symbol2.DeclaringEntity with + | Some e1, Some e2 -> e1.CompiledName = e2.CompiledName + | _ -> false) + && symbol1.GenericParameters.Count = symbol2.GenericParameters.Count + && symbol1.CurriedParameterGroups.Count = symbol2.CurriedParameterGroups.Count + && ((symbol1.CurriedParameterGroups, symbol2.CurriedParameterGroups) + ||> Seq.forall2 (fun pg1 pg2 -> + let pg1, pg2 = pg1.ToArray(), pg2.ToArray() + // We filter out/fixup first "unit" parameter in the group, since it just represents the `()` call notation, for example `"string".Clone()` will have one curried group with one parameter which type is unit. + let pg1 = // If parameter has no name and it's unit type, filter it out + if pg1.Length > 0 + && Option.isNone pg1[0].Name + && pg1[0].Type.StripAbbreviations().TypeDefinition.DisplayName = "Unit" then + pg1[1..] + else + pg1 + pg1.Length = pg2.Length + && ((pg1, pg2) ||> Seq.forall2 (fun p1 p2 -> areTypesEqual p1.Type p2.Type)))) + && areTypesEqual symbol1.ReturnParameter.Type symbol2.ReturnParameter.Type + | (:? FSharpField as symbol1), (:? FSharpField as symbol2) when x.IsFromDefinition -> + symbol1.DisplayName = symbol2.DisplayName + && (match symbol1.DeclaringEntity, symbol2.DeclaringEntity with + | Some e1, Some e2 -> e1.CompiledName = e2.CompiledName + | _ -> false) + | (:? FSharpUnionCase as symbol1), (:? FSharpUnionCase as symbol2) -> + symbol1.DisplayName = symbol2.DisplayName + && symbol1.DeclaringEntity.CompiledName = symbol2.DeclaringEntity.CompiledName + | _ -> false + /// Use an origin document to provide the solution & workspace used to /// find the corresponding textSpan and INavigableItem for the range let rangeToNavigableItem (range: range, document: Document) = @@ -548,77 +601,35 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = match tmpShownDocOpt with | ValueSome tmpShownDoc -> let goToAsync = - asyncMaybe { + cancellableTask { - let! ct = Async.CancellationToken |> liftAsync + let! cancellationToken = CancellableTask.getCancellationToken () let! _, checkResults = tmpShownDoc.GetFSharpParseAndCheckResultsAsync("NavigateToExternalDeclaration") - |> CancellableTask.start ct - |> Async.AwaitTask - |> liftAsync - - let! r = - let rec areTypesEqual (ty1: FSharpType) (ty2: FSharpType) = - let ty1 = ty1.StripAbbreviations() - let ty2 = ty2.StripAbbreviations() - - let generic = - ty1.IsGenericParameter && ty2.IsGenericParameter - || (ty1.GenericArguments.Count = ty2.GenericArguments.Count - && (ty1.GenericArguments, ty2.GenericArguments) ||> Seq.forall2 areTypesEqual) - - if generic then - true - else - let namesEqual = ty1.TypeDefinition.DisplayName = ty2.TypeDefinition.DisplayName - let accessPathsEqual = ty1.TypeDefinition.AccessPath = ty2.TypeDefinition.AccessPath - namesEqual && accessPathsEqual + let r = // This tries to find the best possible location of the target symbol's location in the metadata source. // We really should rely on symbol equality within FCS instead of doing it here, // but the generated metadata as source isn't perfect for symbol equality. - checkResults.GetAllUsesOfAllSymbolsInFile(cancellationToken) - |> Seq.tryFindV (fun x -> - match x.Symbol, targetSymbolUse.Symbol with - | (:? FSharpEntity as symbol1), (:? FSharpEntity as symbol2) when x.IsFromDefinition -> - symbol1.DisplayName = symbol2.DisplayName - | (:? FSharpMemberOrFunctionOrValue as symbol1), (:? FSharpMemberOrFunctionOrValue as symbol2) -> - symbol1.DisplayName = symbol2.DisplayName - && (match symbol1.DeclaringEntity, symbol2.DeclaringEntity with - | Some e1, Some e2 -> e1.CompiledName = e2.CompiledName - | _ -> false) - && symbol1.GenericParameters.Count = symbol2.GenericParameters.Count - && symbol1.CurriedParameterGroups.Count = symbol2.CurriedParameterGroups.Count - && ((symbol1.CurriedParameterGroups, symbol2.CurriedParameterGroups) - ||> Seq.forall2 (fun pg1 pg2 -> - pg1.Count = pg2.Count - && ((pg1, pg2) ||> Seq.forall2 (fun p1 p2 -> areTypesEqual p1.Type p2.Type)))) - && areTypesEqual symbol1.ReturnParameter.Type symbol2.ReturnParameter.Type - | (:? FSharpField as symbol1), (:? FSharpField as symbol2) when x.IsFromDefinition -> - symbol1.DisplayName = symbol2.DisplayName - && (match symbol1.DeclaringEntity, symbol2.DeclaringEntity with - | Some e1, Some e2 -> e1.CompiledName = e2.CompiledName - | _ -> false) - | (:? FSharpUnionCase as symbol1), (:? FSharpUnionCase as symbol2) -> - symbol1.DisplayName = symbol2.DisplayName - && symbol1.DeclaringEntity.CompiledName = symbol2.DeclaringEntity.CompiledName - | _ -> false) + let symbols = checkResults.GetAllUsesOfAllSymbolsInFile(cancellationToken) + + symbols + |> Seq.tryFindV (tryFindExternalSymbolUse targetSymbolUse) |> ValueOption.map (fun x -> x.Range) |> ValueOption.toOption - let span = - match RoslynHelpers.TryFSharpRangeToTextSpan(tmpShownDoc.GetTextAsync(cancellationToken).Result, r) with - | ValueSome span -> span - | _ -> TextSpan() + match r with + | None -> return TextSpan.empty + | Some r -> + let! text = tmpShownDoc.GetTextAsync(cancellationToken) + match RoslynHelpers.TryFSharpRangeToTextSpan(text, r) with + | ValueSome span -> return span + | _ -> return TextSpan.empty - return span } - let span = - match Async.RunImmediateExceptOnUI(goToAsync, cancellationToken = cancellationToken) with - | Some span -> span - | _ -> TextSpan() + let span = CancellableTask.runSynchronously cancellationToken goToAsync let navItem = FSharpGoToDefinitionNavigableItem(tmpShownDoc, span) this.NavigateToItem(navItem, cancellationToken) From be1f966ccc44812cf32e7f026363a863ea4b3033 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 14 Sep 2023 18:16:02 +0200 Subject: [PATCH 101/222] SnapshotWithSources wip --- src/Compiler/Service/FSharpCheckerResults.fs | 165 +++++++++++++----- src/Compiler/Service/FSharpCheckerResults.fsi | 52 +++--- src/Compiler/Service/TransparentCompiler.fs | 150 ++++++++++------ .../FSharpChecker/TransparentCompiler.fs | 12 +- 4 files changed, 258 insertions(+), 121 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 9f832ad0350..dfc138e08ba 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -136,6 +136,17 @@ type FSharpFileSnapshot = member this.GetKey() = this.FileName member this.GetVersion() = this.Version +type FSharpFileSnapshotWithSource = + { + FileName: string + SourceHash: string + Source: ISourceText + IsLastCompiland: bool + IsExe: bool + } + + member this.IsSignatureFile = this.FileName.ToLower().EndsWith(".fsi") + type ReferenceOnDisk = { Path: string; LastModified: DateTime } @@ -273,62 +284,130 @@ type FSharpProjectSnapshot = } member this.GetLastModifiedTimeOnDisk() = - DateTime.Now - - member this.GetMd5Version() = - Md5Hasher.empty - //|> Md5Hasher.addString this.ProjectFileName - //|> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) - //|> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) - //|> Md5Hasher.addStrings this.OtherOptions - //|> Md5Hasher.addVersions ( - // this.ReferencedProjects - // |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key) - //) - //|> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment - //|> Md5Hasher.addBool this.UseScriptResolutionRules - - member this.GetDebugVersion(): FSharpProjectSnapshotDebugVersion = { - ProjectFileName = this.ProjectFileName - SourceFiles = [for f in this.SourceFiles -> f.FileName, f.Version] - ReferencesOnDisk = this.ReferencesOnDisk - OtherOptions = this.OtherOptions - ReferencedProjects = [for FSharpReference (_, p) in this.ReferencedProjects -> p.WithoutImplFilesThatHaveSignatures.GetDebugVersion()] - IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment - UseScriptResolutionRules = this.UseScriptResolutionRules - } + // TODO: + DateTime.Now + + member this.GetMd5Version() = Md5Hasher.empty + //|> Md5Hasher.addString this.ProjectFileName + //|> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) + //|> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) + //|> Md5Hasher.addStrings this.OtherOptions + //|> Md5Hasher.addVersions ( + // this.ReferencedProjects + // |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key) + //) + //|> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment + //|> Md5Hasher.addBool this.UseScriptResolutionRules + + member this.GetDebugVersion() : FSharpProjectSnapshotDebugVersion = + { + ProjectFileName = this.ProjectFileName + SourceFiles = [ for f in this.SourceFiles -> f.FileName, f.Version ] + ReferencesOnDisk = this.ReferencesOnDisk + OtherOptions = this.OtherOptions + ReferencedProjects = + [ + for FSharpReference (_, p) in this.ReferencedProjects -> p.WithoutImplFilesThatHaveSignatures.GetDebugVersion() + ] + IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = this.UseScriptResolutionRules + } interface ICacheKey with member this.GetLabel() = this.ToString() member this.GetKey() = this.ProjectFileName member this.GetVersion() = this.GetDebugVersion() -and FSharpProjectSnapshotDebugVersion = { - // Note that this may not reduce to just the project directory, because there may be two projects in the same directory. - ProjectFileName: string +and FSharpProjectSnapshotWithSources = + { + ProjectSnapshot: FSharpProjectSnapshot + SourceFiles: FSharpFileSnapshotWithSource list + } + + member this.IndexOf fileName = + this.SourceFiles + |> List.tryFindIndex (fun x -> x.FileName = fileName) + |> Option.defaultWith (fun () -> + failwith (sprintf "Unable to find file %s in project %s" fileName this.ProjectSnapshot.ProjectFileName)) + + member this.UpTo fileIndex = + { this with + SourceFiles = this.SourceFiles[..fileIndex] + } + + member this.UpTo fileName = this.UpTo(this.IndexOf fileName) + + member this.WithoutImplFilesThatHaveSignatures = + let files = + (([], Set.empty), this.SourceFiles) + ||> Seq.fold (fun (res, sigs) file -> + if file.IsSignatureFile then + file :: res, sigs |> Set.add file.FileName + else + let sigFileName = $"{file.FileName}i" + + if sigs.Contains sigFileName then + res, sigs |> Set.remove sigFileName + else + file :: res, sigs) + |> fst + |> List.rev + + { this with SourceFiles = files } - /// The files in the project. - SourceFiles: (string * string) list + member this.WithoutImplFilesThatHaveSignaturesExceptLastOne = + let lastFile = this.SourceFiles |> List.last - /// Referenced assemblies on disk. - ReferencesOnDisk: ReferenceOnDisk list + if lastFile.IsSignatureFile then + this.WithoutImplFilesThatHaveSignatures + else + let snapshot = this.WithoutImplFilesThatHaveSignatures - /// Additional command line argument options for the project. - OtherOptions: string list + { snapshot with + SourceFiles = snapshot.SourceFiles @ [ lastFile ] + } - /// The command line arguments for the other projects referenced by this project, indexed by the - /// exact text used in the "-r:" reference in FSharpProjectOptions. - ReferencedProjects: FSharpProjectSnapshotDebugVersion list + member internal this.Key = this :> ICacheKey<_, _> - /// When true, the typechecking environment is known a priori to be incomplete, for - /// example when a .fs file is opened outside of a project. In this case, the number of error - /// messages reported is reduced. - IsIncompleteTypeCheckEnvironment: bool + member this.FileKey(fileName) = + { new ICacheKey<_, _> with + member _.GetLabel() = fileName |> shortPath + member _.GetKey() = fileName, this.Key.GetKey() - /// When true, use the reference resolution rules for scripts rather than the rules for compiler. - UseScriptResolutionRules: bool + member _.GetVersion() = + this + .UpTo(fileName) + .WithoutImplFilesThatHaveSignaturesExceptLastOne.Key.GetVersion() + } + + interface ICacheKey with + member this.GetLabel() = this.ProjectSnapshot.Key.ToString() + member this.GetKey() = this.ProjectSnapshot.Key.GetKey() -} + member this.GetVersion() = + { + ProjectSnapshotVersion = this.ProjectSnapshot.WithoutFileVersions.Key.GetVersion() + SourceVersions = this.SourceFiles |> List.map (fun x -> x.SourceHash) + } + +and FSharpProjectSnapshotWithSourcesDebugVersion = + { + ProjectSnapshotVersion: FSharpProjectSnapshotDebugVersion + SourceVersions: string list + } + +and FSharpProjectSnapshotWithSourcesVersion = FSharpProjectSnapshotWithSourcesDebugVersion + +and FSharpProjectSnapshotDebugVersion = + { + ProjectFileName: string + SourceFiles: (string * string) list + ReferencesOnDisk: ReferenceOnDisk list + OtherOptions: string list + ReferencedProjects: FSharpProjectSnapshotDebugVersion list + IsIncompleteTypeCheckEnvironment: bool + UseScriptResolutionRules: bool + } and FSharpProjectSnapshotVersion = FSharpProjectSnapshotDebugVersion diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 06331da2241..ef88a33becf 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -54,11 +54,17 @@ type FSharpFileSnapshot = interface ICacheKey +type FSharpFileSnapshotWithSource = + { FileName: string + SourceHash: string + Source: ISourceText + IsLastCompiland: bool + IsExe: bool } + /// Referenced assembly on disk. Includes last modified time so we know we need to rebuild when it changes. type ReferenceOnDisk = { Path: string; LastModified: DateTime } - [] type FSharpProjectSnapshot = { @@ -145,32 +151,38 @@ type FSharpProjectSnapshot = interface ICacheKey -and FSharpProjectSnapshotDebugVersion = { - // Note that this may not reduce to just the project directory, because there may be two projects in the same directory. - ProjectFileName: string +and FSharpProjectSnapshotWithSources = + { ProjectSnapshot: FSharpProjectSnapshot + SourceFiles: FSharpFileSnapshotWithSource list } - /// The files in the project. - SourceFiles: (string * string) list + /// A snapshot of the same project but only up to the given file index (including). + member UpTo: fileIndex: int -> FSharpProjectSnapshotWithSources + + /// A snapshot of the same project but only up to the given file (including). + member UpTo: fileName: string -> FSharpProjectSnapshotWithSources - /// Referenced assemblies on disk. - ReferencesOnDisk: ReferenceOnDisk list + member WithoutImplFilesThatHaveSignatures: FSharpProjectSnapshotWithSources + member WithoutImplFilesThatHaveSignaturesExceptLastOne: FSharpProjectSnapshotWithSources - /// Additional command line argument options for the project. - OtherOptions: string list + member internal FileKey: fileName: string -> ICacheKey<(string * string), FSharpProjectSnapshotWithSourcesVersion> - /// The command line arguments for the other projects referenced by this project, indexed by the - /// exact text used in the "-r:" reference in FSharpProjectOptions. - ReferencedProjects: FSharpProjectSnapshotDebugVersion list + member internal Key: ICacheKey + interface ICacheKey - /// When true, the typechecking environment is known a priori to be incomplete, for - /// example when a .fs file is opened outside of a project. In this case, the number of error - /// messages reported is reduced. - IsIncompleteTypeCheckEnvironment: bool +and FSharpProjectSnapshotWithSourcesDebugVersion = + { ProjectSnapshotVersion: FSharpProjectSnapshotDebugVersion + SourceVersions: string list } - /// When true, use the reference resolution rules for scripts rather than the rules for compiler. - UseScriptResolutionRules: bool +and FSharpProjectSnapshotWithSourcesVersion = FSharpProjectSnapshotWithSourcesDebugVersion -} +and FSharpProjectSnapshotDebugVersion = + { ProjectFileName: string + SourceFiles: (string * string) list + ReferencesOnDisk: ReferenceOnDisk list + OtherOptions: string list + ReferencedProjects: FSharpProjectSnapshotDebugVersion list + IsIncompleteTypeCheckEnvironment: bool + UseScriptResolutionRules: bool } and FSharpProjectSnapshotVersion = FSharpProjectSnapshotDebugVersion diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 01d8520b0db..7ea08fb2734 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -131,7 +131,7 @@ type internal DependencyGraphType = [] type internal Extensions = [] - static member Key(fileSnapshots: FSharpFileSnapshot list, ?extraKeyFlag) = + static member Key(fileSnapshots: FSharpFileSnapshotWithSource list, ?extraKeyFlag) = { new ICacheKey<_, _> with member _.GetLabel() = @@ -150,7 +150,7 @@ type internal Extensions = member _.GetVersion() = Md5Hasher.empty - |> Md5Hasher.addStrings (fileSnapshots |> Seq.map (fun f -> f.Version)) + |> Md5Hasher.addStrings (fileSnapshots |> Seq.map (fun f -> f.SourceHash)) } [] @@ -260,6 +260,8 @@ module private TypeCheckingGraphProcessing = type internal CompilerCaches() = + member val Source = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "Source") + member val ParseFile = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "ParseFile") member val ParseAndCheckFileInProject = AsyncMemoize(name = "ParseAndCheckFileInProject") @@ -274,7 +276,7 @@ type internal CompilerCaches() = member val BootstrapInfo = AsyncMemoize(name = "BootstrapInfo") - member val TcFile = AsyncMemoize(name = "TcPrior") + member val TcLastFile = AsyncMemoize(name = "TcPrior") member val TcIntermediate = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "TcIntermediate") @@ -753,6 +755,8 @@ type internal TransparentCompiler fileSnapshots.TryFind fileName |> Option.defaultWith (fun () -> // TODO: does this commonly happen? + failwith "Let's find out..." + { FileName = fileName Version = (FileSystem.GetLastWriteTimeShim fileName).Ticks.ToString() @@ -824,15 +828,49 @@ type internal TransparentCompiler } ) - let ComputeParseFile bootstrapInfo (file: FSharpFile) = + // TODO: Not sure if we should cache this. For VS probably not. Maybe it can be configurable by FCS user. + let LoadSource (file: FSharpFile) = + caches.Source.Get( + file.Source.Key, + async { + let! source = file.Source.GetSource() |> Async.AwaitTask + + return + { + FileName = file.Source.FileName + Source = source + SourceHash = Md5Hasher.empty |> Md5Hasher.addString <| source.ToString() + IsLastCompiland = file.IsLastCompiland + IsExe = file.IsExe + } + } + ) + + let LoadSources (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = + async { + + // TODO: deuglify/optimize + let! sources = + projectSnapshot.SourceFiles + |> Seq.map (fun f -> f.FileName |> bootstrapInfo.GetFile |> LoadSource) + |> Async.Parallel + + return + { + ProjectSnapshot = projectSnapshot + SourceFiles = sources |> Array.toList + } + } + + let ComputeParseFile bootstrapInfo (file: FSharpFileSnapshotWithSource) = let key = { new ICacheKey<_, _> with - member _.GetLabel() = shortPath file.Source.FileName - member _.GetKey() = file.Source.FileName + member _.GetLabel() = file.FileName |> shortPath + member _.GetKey() = file.FileName member _.GetVersion() = - file.Source.Version, file.IsLastCompiland, file.IsExe + file.SourceHash, file.IsLastCompiland, file.IsExe } caches.ParseFile.Get( @@ -842,8 +880,8 @@ type internal TransparentCompiler Activity.start "ComputeParseFile" [| - Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName - Activity.Tags.version, file.Source.Version + Activity.Tags.fileName, file.FileName |> shortPath + Activity.Tags.version, file.SourceHash |] let tcConfig = bootstrapInfo.TcConfig @@ -854,8 +892,8 @@ type internal TransparentCompiler use _holder = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parse) let flags = file.IsLastCompiland, file.IsExe - let fileName = file.Source.FileName - let! sourceText = file.Source.GetSource() |> Async.AwaitTask + let fileName = file.FileName + let sourceText = file.Source let input = ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) @@ -974,22 +1012,22 @@ type internal TransparentCompiler |> Seq.map (fun x -> x.Key, x.Value |> Array.filter (fun node -> not (removeIndexes.Contains node))) |> Graph.make - let ComputeDependencyGraphForFile (tcConfig: TcConfig) (priorSnapshot: FSharpProjectSnapshot) parsedInputs = + let ComputeDependencyGraphForFile (tcConfig: TcConfig) (priorSnapshot: FSharpProjectSnapshotWithSources) parsedInputs = let key = priorSnapshot.SourceFiles.Key(DependencyGraphType.File) //let lastFileIndex = (parsedInputs |> Array.length) - 1 //caches.DependencyGraph.Get(key, computeDependencyGraph parsedInputs (Graph.subGraphFor lastFileIndex)) caches.DependencyGraph.Get( key, - computeDependencyGraph tcConfig parsedInputs (removeImplFilesThatHaveSignaturesExceptLastOne priorSnapshot) + computeDependencyGraph tcConfig parsedInputs (removeImplFilesThatHaveSignaturesExceptLastOne priorSnapshot.ProjectSnapshot) ) - let ComputeDependencyGraphForProject (tcConfig: TcConfig) (projectSnapshot: FSharpProjectSnapshot) parsedInputs = + let ComputeDependencyGraphForProject (tcConfig: TcConfig) (projectSnapshot: FSharpProjectSnapshotWithSources) parsedInputs = let key = projectSnapshot.SourceFiles.Key(DependencyGraphType.Project) //caches.DependencyGraph.Get(key, computeDependencyGraph parsedInputs (removeImplFilesThatHaveSignatures projectSnapshot)) caches.DependencyGraph.Get(key, computeDependencyGraph tcConfig parsedInputs id) let ComputeTcIntermediate - (projectSnapshot: FSharpProjectSnapshot) + (projectSnapshot: FSharpProjectSnapshotWithSources) (dependencyGraph: Graph) (node: NodeToTypeCheck) (parsedInput: ParsedInput, parseErrors) @@ -1013,7 +1051,7 @@ type internal TransparentCompiler [| Activity.Tags.fileName, fileName |> Path.GetFileName "key", key.GetLabel() - "version", key.GetVersion().ToString() + "version", "-" // key.GetVersion() |] let tcConfig = bootstrapInfo.TcConfig @@ -1187,27 +1225,22 @@ type internal TransparentCompiler } // Type check file and all its dependencies - let ComputeTcFile (file: FSharpFile) (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = - - let priorSnapshot = projectSnapshot.UpTo file.Source.FileName - - caches.TcFile.Get( - priorSnapshot.FileKey file.Source.FileName, + let ComputeTcLastFile (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = + caches.TcLastFile.Get( + projectSnapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne.Key, async { - use _ = - Activity.start "ComputeTcFile" [| Activity.Tags.fileName, file.Source.FileName |> Path.GetFileName |] + let file = projectSnapshot.SourceFiles |> List.last - // parse required files - let files = - seq { - yield! bootstrapInfo.SourceFiles |> Seq.takeWhile ((<>) file) - file - } + use _ = + Activity.start "ComputeTcFile" [| Activity.Tags.fileName, file.FileName |> Path.GetFileName |] - let! parsedInputs = files |> Seq.map (ComputeParseFile bootstrapInfo) |> Async.Parallel + let! parsedInputs = + projectSnapshot.SourceFiles + |> Seq.map (ComputeParseFile bootstrapInfo) + |> Async.Parallel let! graph, dependencyFiles = - ComputeDependencyGraphForFile bootstrapInfo.TcConfig priorSnapshot (parsedInputs |> Array.map p13) + ComputeDependencyGraphForFile bootstrapInfo.TcConfig projectSnapshot (parsedInputs |> Array.map p13) //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) let! results, tcInfo = @@ -1222,17 +1255,15 @@ type internal TransparentCompiler } ) - let getParseResult (bootstrapInfo: BootstrapInfo) creationDiags fileName = + let getParseResult (bootstrapInfo: BootstrapInfo) creationDiags file = async { - let file = bootstrapInfo.GetFile fileName - let! parseTree, parseDiagnostics, sourceText = ComputeParseFile bootstrapInfo file let parseDiagnostics = DiagnosticHelpers.CreateDiagnostics( bootstrapInfo.TcConfig.diagnosticsOptions, false, - fileName, + file.FileName, parseDiagnostics, suggestNamesForErrors, bootstrapInfo.TcConfig.flatErrors @@ -1268,11 +1299,13 @@ type internal TransparentCompiler | Some bootstrapInfo, creationDiags -> - let file = bootstrapInfo.GetFile fileName + let priorSnapshot = projectSnapshot.UpTo fileName + let! snapshotWithSources = LoadSources bootstrapInfo priorSnapshot + let file = snapshotWithSources.SourceFiles |> List.last - let! parseResults, _sourceText = getParseResult bootstrapInfo creationDiags fileName + let! parseResults, _sourceText = getParseResult bootstrapInfo creationDiags file - let! result, priorTcInfo = ComputeTcFile file bootstrapInfo projectSnapshot + let! result, priorTcInfo = ComputeTcLastFile bootstrapInfo snapshotWithSources let (tcEnv, _topAttribs, checkedImplFileOpt, ccuSigForFile) = result @@ -1340,17 +1373,19 @@ type internal TransparentCompiler } ) - let ComputeParseAndCheckAllFilesInProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = + let ComputeParseAndCheckAllFilesInProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = caches.ParseAndCheckAllFilesInProject.Get( projectSnapshot.Key, async { use _ = Activity.start "ComputeParseAndCheckAllFilesInProject" - [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] + [| + Activity.Tags.project, projectSnapshot.ProjectSnapshot.ProjectFileName |> Path.GetFileName + |] let! parsedInputs = - bootstrapInfo.SourceFiles + projectSnapshot.SourceFiles |> Seq.map (ComputeParseFile bootstrapInfo) |> Async.Parallel @@ -1369,7 +1404,9 @@ type internal TransparentCompiler caches.ProjectExtras.Get( projectSnapshot.WithoutImplFilesThatHaveSignatures.Key, async { - let! results, finalInfo = ComputeParseAndCheckAllFilesInProject bootstrapInfo projectSnapshot + let! snapshotWithSources = projectSnapshot |> LoadSources bootstrapInfo + + let! results, finalInfo = ComputeParseAndCheckAllFilesInProject bootstrapInfo snapshotWithSources let assemblyName = bootstrapInfo.AssemblyName let tcConfig = bootstrapInfo.TcConfig @@ -1462,10 +1499,11 @@ type internal TransparentCompiler caches.AssemblyData.Get( projectSnapshot.WithoutImplFilesThatHaveSignatures.Key, async { - let availableOnDiskModifiedTime = + let availableOnDiskModifiedTime = if FileSystem.FileExistsShim fileName then Some <| FileSystem.GetLastWriteTimeShim fileName - else None + else + None let shouldUseOnDisk = availableOnDiskModifiedTime @@ -1475,16 +1513,16 @@ type internal TransparentCompiler if shouldUseOnDisk then Trace.TraceInformation($"Using assembly on disk: {name}") - return ProjectAssemblyDataResult.Unavailable true + return ProjectAssemblyDataResult.Unavailable true else match! ComputeBootstrapInfo projectSnapshot with - | None, _ -> + | None, _ -> Trace.TraceInformation($"Using assembly on disk (unintentionally): {name}") return ProjectAssemblyDataResult.Unavailable true | Some bootstrapInfo, _creationDiags -> let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot Trace.TraceInformation($"Using in-memory project reference: {name}") - + return assemblyDataResult } ) @@ -1556,15 +1594,15 @@ type internal TransparentCompiler } ) - let tryGetSink fileName (projectSnapshot: FSharpProjectSnapshot) = + let tryGetSink (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = async { match! ComputeBootstrapInfo projectSnapshot with | None, _ -> return None | Some bootstrapInfo, _creationDiags -> - let file = bootstrapInfo.GetFile fileName + let! snapshotWithSources = projectSnapshot.UpTo fileName |> LoadSources bootstrapInfo - let! _, tcInfo = ComputeTcFile file bootstrapInfo projectSnapshot + let! _, tcInfo = ComputeTcLastFile bootstrapInfo snapshotWithSources return tcInfo.sink |> List.tryHead |> Option.map (fun sink -> sink, bootstrapInfo) } @@ -1644,7 +1682,8 @@ type internal TransparentCompiler match! ComputeBootstrapInfo projectSnapshot with | None, creationDiags -> return emptyParseResult fileName creationDiags | Some bootstrapInfo, creationDiags -> - let! parseResult, _ = getParseResult bootstrapInfo creationDiags fileName + let! file = bootstrapInfo.GetFile fileName |> LoadSource + let! parseResult, _ = getParseResult bootstrapInfo creationDiags file return parseResult } @@ -1762,7 +1801,12 @@ type internal TransparentCompiler return! this.GetAssemblyData(snapshot, fileName, userOpName) } - member this.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, fileName, userOpName: string) : NodeCode = + member this.GetAssemblyData + ( + projectSnapshot: FSharpProjectSnapshot, + fileName, + userOpName: string + ) : NodeCode = this.GetAssemblyData(projectSnapshot, fileName, userOpName) member this.GetBackgroundCheckResultsForFileInProject diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 856b5c25645..44e606c9e27 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -226,11 +226,13 @@ let ``File is not checked twice`` () = let intermediateTypeChecks = cacheEvents |> Seq.groupBy (fun (_e, (_l, (f, _p), _)) -> f |> Path.GetFileName) - |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Seq.map (fun (k, g) -> k, g |> Seq.map id |> Seq.toList) |> Map - Assert.Equal([Weakened; Started; Finished], intermediateTypeChecks["FileFirst.fs"]) - Assert.Equal([Weakened; Started; Finished], intermediateTypeChecks["FileThird.fs"]) + ignore intermediateTypeChecks + + //Assert.Equal([Weakened; Started; Finished], intermediateTypeChecks["FileFirst.fs"]) + //Assert.Equal([Weakened; Started; Finished], intermediateTypeChecks["FileThird.fs"]) [] let ``We don't check files that are not depended on`` () = @@ -446,13 +448,13 @@ type SignatureFiles = Yes = 1 | No = 2 | Some = 3 let fuzzingTest seed (project: SyntheticProject) = task { let rng = System.Random seed - let checkingThreads = 20 + let checkingThreads = 10 let maxModificationDelayMs = 10 let maxCheckingDelayMs = 20 //let runTimeMs = 30000 let signatureFileModificationProbability = 0.25 let modificationLoopIterations = 10 - let checkingLoopIterations = 20 + let checkingLoopIterations = 5 let minCheckingTimeoutMs = 0 let maxCheckingTimeoutMs = 300 From a3b02339b7662fba4d900d6841c14ff82510f456 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 15 Sep 2023 15:47:46 +0200 Subject: [PATCH 102/222] SnapshotWithSources --- src/Compiler/Facilities/AsyncMemoize.fs | 11 +++++-- src/Compiler/Service/BackgroundCompiler.fs | 20 ++++++++--- src/Compiler/Service/FSharpCheckerResults.fs | 33 ++++++++++++------- src/Compiler/Service/TransparentCompiler.fs | 10 +++--- .../FSharpChecker/ProjectSnapshot.fs | 29 ++++++++++++++++ .../FSharpChecker/TransparentCompiler.fs | 31 ++++++++++++++--- .../src/FSharp.Editor/Common/Extensions.fs | 2 ++ .../src/FSharp.Editor/Common/Logging.fs | 3 +- 8 files changed, 110 insertions(+), 29 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index e2755aec57a..da907cc06fb 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -258,10 +258,10 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers strongList |> Seq.append weakList |> Seq.choose (function - | _k, version, label, Strong value -> Some (label, version, value) + | _k, version, label, Strong value -> Some(label, version, value) | _k, version, label, Weak w -> match w.TryGetTarget() with - | true, value -> Some (label, version, value) + | true, value -> Some(label, version, value) | _ -> None) type internal ICacheKey<'TKey, 'TVersion> = @@ -585,7 +585,12 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T member _.Locked = lock.Semaphore.CurrentCount < 1 - member _.Running = cache.GetValues() |> Seq.filter (function _, _, Running _ -> true | _ -> false) |> Seq.toArray + member _.Running = + cache.GetValues() + |> Seq.filter (function + | _, _, Running _ -> true + | _ -> false) + |> Seq.toArray member this.DebuggerDisplay = let locked = if this.Locked then " [LOCKED]" else "" diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 7aef64e54fa..bc3d2d35f58 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -85,10 +85,12 @@ type internal IBackgroundCompiler = NodeCode> abstract member GetAssemblyData: - options: FSharpProjectOptions * outputFileName: string * userOpName: string -> NodeCode + options: FSharpProjectOptions * outputFileName: string * userOpName: string -> + NodeCode abstract member GetAssemblyData: - projectSnapshot: FSharpProjectSnapshot * outputFileName: string * userOpName: string -> NodeCode + projectSnapshot: FSharpProjectSnapshot * outputFileName: string * userOpName: string -> + NodeCode /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) abstract member GetBackgroundCheckResultsForFileInProject: @@ -1493,10 +1495,20 @@ type internal BackgroundCompiler member _.FrameworkImportsCache: FrameworkImportsCache = self.FrameworkImportsCache - member _.GetAssemblyData(options: FSharpProjectOptions, _fileName: string, userOpName: string) : NodeCode = + member _.GetAssemblyData + ( + options: FSharpProjectOptions, + _fileName: string, + userOpName: string + ) : NodeCode = self.GetAssemblyData(options, userOpName) - member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, _fileName: string, userOpName: string) : NodeCode = + member _.GetAssemblyData + ( + projectSnapshot: FSharpProjectSnapshot, + _fileName: string, + userOpName: string + ) : NodeCode = self.GetAssemblyData(projectSnapshot.ToOptions(), userOpName) member _.GetBackgroundCheckResultsForFileInProject diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index dfc138e08ba..4b5864c77a7 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -241,13 +241,17 @@ type FSharpProjectSnapshot = member this.WithoutImplFilesThatHaveSignaturesExceptLastOne = let lastFile = this.SourceFiles |> List.last - if lastFile.IsSignatureFile then + let snapshotWithoutImplFilesThatHaveSignatures = this.WithoutImplFilesThatHaveSignatures - else - let snapshot = this.WithoutImplFilesThatHaveSignatures - { snapshot with - SourceFiles = snapshot.SourceFiles @ [ lastFile ] + if + lastFile.IsSignatureFile + || snapshotWithoutImplFilesThatHaveSignatures.SourceFiles |> List.last = lastFile + then + this.WithoutImplFilesThatHaveSignatures + else + { snapshotWithoutImplFilesThatHaveSignatures with + SourceFiles = snapshotWithoutImplFilesThatHaveSignatures.SourceFiles @ [ lastFile ] } member this.SourceFileNames = this.SourceFiles |> List.map (fun x -> x.FileName) @@ -330,8 +334,9 @@ and FSharpProjectSnapshotWithSources = |> Option.defaultWith (fun () -> failwith (sprintf "Unable to find file %s in project %s" fileName this.ProjectSnapshot.ProjectFileName)) - member this.UpTo fileIndex = - { this with + member this.UpTo(fileIndex: FileIndex) = + { + ProjectSnapshot = this.ProjectSnapshot.UpTo fileIndex SourceFiles = this.SourceFiles[..fileIndex] } @@ -358,13 +363,17 @@ and FSharpProjectSnapshotWithSources = member this.WithoutImplFilesThatHaveSignaturesExceptLastOne = let lastFile = this.SourceFiles |> List.last - if lastFile.IsSignatureFile then + let snapshotWithoutImplFilesThatHaveSignatures = this.WithoutImplFilesThatHaveSignatures - else - let snapshot = this.WithoutImplFilesThatHaveSignatures - { snapshot with - SourceFiles = snapshot.SourceFiles @ [ lastFile ] + if + lastFile.IsSignatureFile + || snapshotWithoutImplFilesThatHaveSignatures.SourceFiles |> List.last = lastFile + then + this.WithoutImplFilesThatHaveSignatures + else + { snapshotWithoutImplFilesThatHaveSignatures with + SourceFiles = snapshotWithoutImplFilesThatHaveSignatures.SourceFiles @ [ lastFile ] } member internal this.Key = this :> ICacheKey<_, _> diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 7ea08fb2734..32980c00ae3 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -848,11 +848,10 @@ type internal TransparentCompiler let LoadSources (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = async { - // TODO: deuglify/optimize - let! sources = - projectSnapshot.SourceFiles - |> Seq.map (fun f -> f.FileName |> bootstrapInfo.GetFile |> LoadSource) + let! sources = + projectSnapshot.SourceFiles + |> Seq.map (fun f -> f.FileName |> bootstrapInfo.GetFile |> LoadSource) |> Async.Parallel return @@ -1038,6 +1037,7 @@ type internal TransparentCompiler ignore dependencyGraph let key = projectSnapshot.FileKey parsedInput.FileName + let _label, _k, _version = key.GetLabel(), key.GetKey(), key.GetVersion() caches.TcIntermediate.Get( key, @@ -1232,7 +1232,7 @@ type internal TransparentCompiler let file = projectSnapshot.SourceFiles |> List.last use _ = - Activity.start "ComputeTcFile" [| Activity.Tags.fileName, file.FileName |> Path.GetFileName |] + Activity.start "ComputeTcLastFile" [| Activity.Tags.fileName, file.FileName |> Path.GetFileName |] let! parsedInputs = projectSnapshot.SourceFiles diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs index 20590a80e11..a2c40b64b81 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs @@ -64,3 +64,32 @@ let WithoutImplFilesThatHaveSignaturesExceptLastOne () = let expected = [| "A.fsi"; "B.fs"; "C.fsi"; "C.fs" |] Assert.Equal(expected, result.SourceFileNames |> List.toArray) + + +[] +let WithoutImplFilesThatHaveSignaturesExceptLastOne_2 () = + + let snapshot: FSharpProjectSnapshot = { + ProjectFileName = Unchecked.defaultof<_> + ProjectId = Unchecked.defaultof<_> + SourceFiles = [ + { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } + { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } + { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } + ] + ReferencesOnDisk = Unchecked.defaultof<_> + OtherOptions = Unchecked.defaultof<_> + ReferencedProjects = Unchecked.defaultof<_> + IsIncompleteTypeCheckEnvironment = Unchecked.defaultof<_> + UseScriptResolutionRules = Unchecked.defaultof<_> + LoadTime = Unchecked.defaultof<_> + UnresolvedReferences = Unchecked.defaultof<_> + OriginalLoadReferences = Unchecked.defaultof<_> + Stamp = Unchecked.defaultof<_> + } + + let result = snapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne + + let expected = [| "A.fs"; "B.fs"; "C.fs" |] + + Assert.Equal(expected, result.SourceFileNames |> List.toArray) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 44e606c9e27..e98b9fd31fd 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -226,13 +226,36 @@ let ``File is not checked twice`` () = let intermediateTypeChecks = cacheEvents |> Seq.groupBy (fun (_e, (_l, (f, _p), _)) -> f |> Path.GetFileName) - |> Seq.map (fun (k, g) -> k, g |> Seq.map id |> Seq.toList) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) |> Map - ignore intermediateTypeChecks + Assert.Equal([Weakened; Started; Finished], intermediateTypeChecks["FileFirst.fs"]) + Assert.Equal([Weakened; Started; Finished], intermediateTypeChecks["FileThird.fs"]) + +[] +let ``If a file is checked as a dependency it's not re-checked later`` () = + let cacheEvents = ResizeArray() + + testWorkflow() { + withChecker (fun checker -> + async { + do! Async.Sleep 50 // wait for events from initial project check + checker.Caches.TcIntermediate.OnEvent cacheEvents.Add + }) + updateFile "First" updatePublicSurface + checkFile "Last" expectOk + checkFile "Third" expectOk + } |> ignore + + let intermediateTypeChecks = + cacheEvents + |> Seq.groupBy (fun (_e, (_l, (f, _p), _)) -> f |> Path.GetFileName) + |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) + |> Map + + Assert.Equal([Weakened; Started; Finished], intermediateTypeChecks["FileThird.fs"]) + - //Assert.Equal([Weakened; Started; Finished], intermediateTypeChecks["FileFirst.fs"]) - //Assert.Equal([Weakened; Started; Finished], intermediateTypeChecks["FileThird.fs"]) [] let ``We don't check files that are not depended on`` () = diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index d05b5166a08..ab1c5bdeaba 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -111,6 +111,8 @@ module private SourceText = |> Hash.combine encodingHash |> Hash.combine contentsHash |> Hash.combine sourceText.Length + + override _.ToString() = sourceText.ToString() interface ISourceText with member _.Item diff --git a/vsintegration/src/FSharp.Editor/Common/Logging.fs b/vsintegration/src/FSharp.Editor/Common/Logging.fs index 3cc088c8615..537361f6db1 100644 --- a/vsintegration/src/FSharp.Editor/Common/Logging.fs +++ b/vsintegration/src/FSharp.Editor/Common/Logging.fs @@ -93,7 +93,8 @@ module Logging = let inline debug msg = Printf.kprintf Debug.WriteLine msg let private logger = lazy Logger(Logger.GlobalServiceProvider) - let private log logType msg = + + let private log logType msg = logger.Value.Log(logType, msg) System.Diagnostics.Trace.TraceInformation(msg) From a5ac09d8d2ddfa11544adc82e65dcfaa2e0074d7 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 18 Sep 2023 15:30:01 +0200 Subject: [PATCH 103/222] wip --- .../Classification/ClassificationService.fs | 8 +- .../CodeFixes/AddOpenCodeFixProvider.fs | 6 +- .../FSharp.Editor/Common/CancellableTasks.fs | 6 +- .../src/FSharp.Editor/Common/Extensions.fs | 12 +-- .../Completion/CompletionProvider.fs | 74 +++++++++---------- .../AssemblyContentProvider.fs | 11 ++- .../Structure/BlockStructureService.fs | 15 ++-- .../CompletionProviderTests.fs | 4 +- .../FsxCompletionProviderTests.fs | 2 +- 9 files changed, 68 insertions(+), 70 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 35eefa24828..28c6e96220c 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -247,6 +247,7 @@ type internal FSharpClassificationService [] () = TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticCalssifications, eventProps) let! classificationData = document.GetFSharpSemanticClassificationAsync(nameof (FSharpClassificationService)) + let classificationDataLookup = toSemanticClassificationLookup classificationData do! unopenedDocumentsSemanticClassificationCache.SetAsync(document, classificationDataLookup) addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result @@ -288,10 +289,11 @@ type internal FSharpClassificationService [] () = let classificationData = checkResults.GetSemanticClassification(Some targetRange) - let classificationDataLookup = itemToSemanticClassificationLookup classificationData - do! unopenedDocumentsSemanticClassificationCache.SetAsync(document, classificationDataLookup) + if classificationData.Length > 0 then + let classificationDataLookup = itemToSemanticClassificationLookup classificationData + do! unopenedDocumentsSemanticClassificationCache.SetAsync(document, classificationDataLookup) - addSemanticClassification sourceText textSpan classificationData result + addSemanticClassification sourceText textSpan classificationData result } |> CancellableTask.startAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs index 08ce2ae91c6..b611146d2e0 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs @@ -121,8 +121,8 @@ type internal AddOpenCodeFixProvider [] (assemblyContentPr let entities = assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies checkResults - |> List.collect (fun s -> - [ + |> Array.collect (fun s -> + [| yield s.TopRequireQualifiedAccessParent, s.AutoOpenParent, s.Namespace, s.CleanedIdents if isAttribute then let lastIdent = s.CleanedIdents.[s.CleanedIdents.Length - 1] @@ -137,7 +137,7 @@ type internal AddOpenCodeFixProvider [] (assemblyContentPr s.Namespace, s.CleanedIdents |> Array.replace (s.CleanedIdents.Length - 1) (lastIdent.Substring(0, lastIdent.Length - 9)) - ]) + |]) let longIdent = ParsedInput.GetLongIdentAt parseResults.ParseTree unresolvedIdentRange.End diff --git a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs index 7d5ed3205d2..1cd71d4228e 100644 --- a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs +++ b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs @@ -1096,13 +1096,15 @@ module CancellableTasks = let inline whenAll (tasks: CancellableTask<'a> seq) = cancellableTask { let! ct = getCancellationToken () - return! Task.WhenAll (seq { for task in tasks do yield start ct task }) + let tasks = seq { for task in tasks do yield start ct task } + return! Task.WhenAll (tasks) } let inline whenAllTasks (tasks: CancellableTask seq) = cancellableTask { let! ct = getCancellationToken () - return! Task.WhenAll (seq { for task in tasks do yield startTask ct task }) + let tasks = seq { for task in tasks do yield startTask ct task } + return! Task.WhenAll (tasks) } let inline ignore ([] ctask: CancellableTask<_>) = toUnit ctask diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index caa87db2286..7a5b69355f9 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -302,15 +302,15 @@ module ValueOption = module IEnumerator = let chooseV f (e: IEnumerator<'T>) = let mutable started = false - let mutable curr = None + let mutable curr = ValueNone let get () = if not started then raise (InvalidOperationException("Not started")) match curr with - | None -> raise (InvalidOperationException("Already finished")) - | Some x -> x + | ValueNone -> raise (InvalidOperationException("Already finished")) + | ValueSome x -> x { new IEnumerator<'U> with member _.Current = get () @@ -321,12 +321,12 @@ module IEnumerator = if not started then started <- true - curr <- None + curr <- ValueNone while (curr.IsNone && e.MoveNext()) do curr <- f e.Current - Option.isSome curr + ValueOption.isSome curr member _.Reset() = raise (NotSupportedException("Reset is not supported")) @@ -387,7 +387,7 @@ module Seq = res - let chooseV chooser source = + let chooseV (chooser: 'a -> 'b voption) source = revamp (IEnumerator.chooseV chooser) source [] diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index d0ce229f64b..01a7a171da1 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -150,14 +150,14 @@ type internal FSharpCompletionProvider ( document: Document, caretPosition: int, - getAllSymbols: FSharpCheckFileResults -> AssemblySymbol list + getAllSymbols: FSharpCheckFileResults -> AssemblySymbol array ) = cancellableTask { - let! parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("ProvideCompletionsAsyncAux") - let! ct = CancellableTask.getCancellationToken () + let! parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("ProvideCompletionsAsyncAux") + let! sourceText = document.GetTextAsync(ct) let textLines = sourceText.Lines let caretLinePos = textLines.GetLinePosition(caretPosition) @@ -194,9 +194,8 @@ type internal FSharpCompletionProvider let results = List() - declarationItems <- - declarations.Items - |> Array.sortWith (fun x y -> + Array.sortInPlaceWith + (fun (x: DeclarationListItem) (y: DeclarationListItem) -> let mutable n = (not x.IsResolved).CompareTo(not y.IsResolved) if n <> 0 then @@ -219,7 +218,9 @@ type internal FSharpCompletionProvider if n <> 0 then n else - x.MinorPriority.CompareTo(y.MinorPriority)) + x.MinorPriority.CompareTo(y.MinorPriority)) declarations.Items + + declarationItems <- declarations.Items let maxHints = if mruItems.Values.Count = 0 then @@ -227,23 +228,19 @@ type internal FSharpCompletionProvider else Seq.max mruItems.Values - declarationItems - |> Array.iteri (fun number declarationItem -> + for number = 0 to declarationItems.Length - 1 do + let declarationItem = declarationItems[number] let glyph = Tokenizer.FSharpGlyphToRoslynGlyph(declarationItem.Glyph, declarationItem.Accessibility) - let namespaceName = - match declarationItem.NamespaceToOpen with - | Some namespaceToOpen -> namespaceToOpen - | _ -> null // Icky, but this is how roslyn handles it - - let filterText = + let namespaceName, filterText = match declarationItem.NamespaceToOpen, declarationItem.NameInList.Split '.' with // There is no namespace to open and the item name does not contain dots, so we don't need to pass special FilterText to Roslyn. - | None, [| _ |] -> null + | None, [| _ |] -> null, null + | Some namespaceToOpen, idents -> namespaceToOpen, Array.last idents // Either we have a namespace to open ("DateTime (open System)") or item name contains dots ("Array.map"), or both. // We are passing last part of long ident as FilterText. - | _, idents -> Array.last idents + | None, idents -> null, Array.last idents let completionItem = FSharpCommonCompletionItem @@ -282,8 +279,8 @@ type internal FSharpCompletionProvider let sortText = priority.ToString("D6") let completionItem = completionItem.WithSortText(sortText) - results.Add(completionItem)) - + results.Add(completionItem) + if results.Count > 0 && not declarations.IsForType @@ -352,11 +349,11 @@ type internal FSharpCompletionProvider ) if shouldProvideCompetion then - let getAllSymbols (fileCheckResults: FSharpCheckFileResults) = + let inline getAllSymbols (fileCheckResults: FSharpCheckFileResults) = if settings.IntelliSense.IncludeSymbolsFromUnopenedNamespacesOrModules then assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies(fileCheckResults) else - [] + [||] let! results = FSharpCompletionProvider.ProvideCompletionsAsyncAux(context.Document, context.Position, getAllSymbols) @@ -369,31 +366,26 @@ type internal FSharpCompletionProvider ( document: Document, completionItem: Completion.CompletionItem, - cancellationToken: CancellationToken + _cancellationToken: CancellationToken ) : Task = match completionItem.Properties.TryGetValue IndexPropName with | true, completionItemIndexStr when int completionItemIndexStr >= declarationItems.Length -> Task.FromResult CompletionDescription.Empty | true, completionItemIndexStr -> - // TODO: Not entirely sure why do we use tasks here, since everything here is synchronous. - cancellableTask { - use _logBlock = - Logger.LogBlockMessage document.Name LogEditorFunctionId.Completion_GetDescriptionAsync - - let completionItemIndex = int completionItemIndexStr + use _logBlock = + Logger.LogBlockMessage document.Name LogEditorFunctionId.Completion_GetDescriptionAsync - let declarationItem = declarationItems.[completionItemIndex] - let description = declarationItem.Description - let documentation = List() - let collector = RoslynHelpers.CollectTaggedText documentation - // mix main description and xmldoc by using one collector - XmlDocumentation.BuildDataTipText(documentationBuilder, collector, collector, collector, collector, collector, description) + let completionItemIndex = int completionItemIndexStr - return CompletionDescription.Create(documentation.ToImmutableArray()) - } - |> CancellableTask.start cancellationToken + let declarationItem = declarationItems.[completionItemIndex] + let description = declarationItem.Description + let documentation = List() + let collector = RoslynHelpers.CollectTaggedText documentation + // mix main description and xmldoc by using one collector + XmlDocumentation.BuildDataTipText(documentationBuilder, collector, collector, collector, collector, collector, description) + Task.FromResult(CompletionDescription.Create(documentation.ToImmutableArray())) | _ -> match completionItem.Properties.TryGetValue KeywordDescription with | true, keywordDescription -> Task.FromResult(CompletionDescription.FromText(keywordDescription)) @@ -406,8 +398,8 @@ type internal FSharpCompletionProvider let fullName = match item.Properties.TryGetValue FullNamePropName with - | true, x -> Some x - | _ -> None + | true, x -> ValueSome x + | _ -> ValueNone // do not add extension members and unresolved symbols to the MRU list if @@ -415,7 +407,7 @@ type internal FSharpCompletionProvider && not (item.Properties.ContainsKey IsExtensionMemberPropName) then match fullName with - | Some fullName -> + | ValueSome fullName -> match mruItems.TryGetValue fullName with | true, hints -> mruItems.[fullName] <- hints + 1 | _ -> mruItems.[fullName] <- 1 @@ -439,7 +431,7 @@ type internal FSharpCompletionProvider let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpCompletionProvider)) let fullNameIdents = - fullName |> Option.map (fun x -> x.Split '.') |> Option.defaultValue [||] + fullName |> ValueOption.map (fun x -> x.Split '.') |> ValueOption.defaultValue [||] let insertionPoint = if settings.CodeFixes.AlwaysPlaceOpensAtTopLevel then diff --git a/vsintegration/src/FSharp.Editor/LanguageService/AssemblyContentProvider.fs b/vsintegration/src/FSharp.Editor/LanguageService/AssemblyContentProvider.fs index f34ca68a2dc..e2e06d2725d 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/AssemblyContentProvider.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/AssemblyContentProvider.fs @@ -12,8 +12,8 @@ open FSharp.Compiler.EditorServices type internal AssemblyContentProvider() = let entityCache = EntityCache() - member x.GetAllEntitiesInProjectAndReferencedAssemblies(fileCheckResults: FSharpCheckFileResults) = - [ + member _.GetAllEntitiesInProjectAndReferencedAssemblies(fileCheckResults: FSharpCheckFileResults) = + [| yield! AssemblyContent.GetAssemblySignatureContent AssemblyContentType.Full fileCheckResults.PartialAssemblySignature // FCS sometimes returns several FSharpAssembly for single referenced assembly. // For example, it returns two different ones for Swensen.Unquote; the first one @@ -24,11 +24,10 @@ type internal AssemblyContentProvider() = fileCheckResults.ProjectContext.GetReferencedAssemblies() |> Seq.groupBy (fun asm -> asm.FileName) |> Seq.map (fun (fileName, asms) -> fileName, List.ofSeq asms) - |> Seq.toList - |> List.rev // if mscorlib.dll is the first then FSC raises exception when we try to - // get Content.Entities from it. + |> Seq.rev // if mscorlib.dll is the first then FSC raises exception when we try to get Content.Entities from it. + |> Seq.toArray for fileName, signatures in assembliesByFileName do let contentType = AssemblyContentType.Public // it's always Public for now since we don't support InternalsVisibleTo attribute yet yield! AssemblyContent.GetAssemblyContent entityCache.Locking contentType fileName signatures - ] + |] diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index ca11623b55e..c84a82b9632 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -115,12 +115,15 @@ module internal BlockStructure = | Scope.While | Scope.For -> false + [] + let ellipsis = "..." + let createBlockSpans isBlockStructureEnabled (sourceText: SourceText) (parsedInput: ParsedInput) = let linetext = sourceText.Lines |> Seq.map (fun x -> x.ToString()) |> Seq.toArray Structure.getOutliningRanges linetext parsedInput |> Seq.distinctBy (fun x -> x.Range.StartLine) - |> Seq.choose (fun scopeRange -> + |> Seq.chooseV (fun scopeRange -> // the range of text to collapse let textSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, scopeRange.CollapseRange) @@ -132,9 +135,9 @@ module internal BlockStructure = let line = sourceText.Lines.GetLineFromPosition textSpan.Start let bannerText = - match Option.ofNullable (line.Span.Intersection textSpan) with - | Some span -> sourceText.GetSubText(span).ToString() + "..." - | None -> "..." + match ValueOption.ofNullable (line.Span.Intersection textSpan) with + | ValueSome span -> sourceText.GetSubText(span).ToString() + ellipsis + | ValueNone -> ellipsis let blockType = if isBlockStructureEnabled then @@ -142,8 +145,8 @@ module internal BlockStructure = else FSharpBlockTypes.Nonstructural - Some(FSharpBlockSpan(blockType, true, textSpan, hintSpan, bannerText, autoCollapse = isAutoCollapsible scopeRange.Scope)) - | _, _ -> None) + ValueSome(FSharpBlockSpan(blockType, true, textSpan, hintSpan, bannerText, autoCollapse = isAutoCollapsible scopeRange.Scope)) + | _, _ -> ValueNone) open BlockStructure open CancellableTasks diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs index e8294caff85..3b8a354e570 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -34,7 +34,7 @@ module CompletionProviderTests = let results = let task = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [||])) |> CancellableTask.start CancellationToken.None task.Result |> Seq.map (fun result -> result.DisplayText) @@ -82,7 +82,7 @@ module CompletionProviderTests = let actual = let task = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [||])) |> CancellableTask.start CancellationToken.None task.Result diff --git a/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs index 49955dedbb4..9d9432aaf13 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs @@ -32,7 +32,7 @@ type Worker() = let actual = let x = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [||])) |> CancellableTask.start CancellationToken.None x.Result From 27cb2f3f507cd58e802ce1030a915d145ebe7369 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 18 Sep 2023 16:38:51 +0200 Subject: [PATCH 104/222] wip --- .../BraceCompletionSessionProvider.fs | 28 ++++++-------- .../src/FSharp.Editor/Common/Pervasive.fs | 38 +++++++++---------- .../src/FSharp.Editor/Common/RoslynHelpers.fs | 7 ---- .../LanguageService/WorkspaceExtensions.fs | 12 ------ .../Navigation/FindUsagesService.fs | 4 +- 5 files changed, 33 insertions(+), 56 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs b/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs index d9339a099d7..1fd0d3da25b 100644 --- a/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs +++ b/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs @@ -22,14 +22,10 @@ open Microsoft.CodeAnalysis.Classification [] module BraceCompletionSessionProviderHelpers = - let tryGetCaretPoint (buffer: ITextBuffer) (session: IBraceCompletionSession) = - let point = - session.TextView.Caret.Position.Point.GetPoint(buffer, PositionAffinity.Predecessor) + let inline tryGetCaretPoint (buffer: ITextBuffer) (session: IBraceCompletionSession) = + ValueOption.ofNullable (session.TextView.Caret.Position.Point.GetPoint(buffer, PositionAffinity.Predecessor)) - if point.HasValue then Some point.Value else None - - let tryGetCaretPosition session = - session |> tryGetCaretPoint session.SubjectBuffer + let inline tryGetCaretPosition (session: IBraceCompletionSession) = tryGetCaretPoint session.SubjectBuffer session let tryInsertAdditionalBracePair (session: IBraceCompletionSession) openingChar closingChar = let sourceCode = session.TextView.TextSnapshot @@ -134,13 +130,13 @@ type BraceCompletionSession // exit without setting the closing point which will take us off the stack edit.Cancel() undo.Cancel() - None + ValueNone else - Some(edit.Apply()) // FIXME: perhaps, it should be ApplyAndLogExceptions() + ValueSome(edit.Apply()) // FIXME: perhaps, it should be ApplyAndLogExceptions() match nextSnapshot with - | None -> () - | Some (nextSnapshot) -> + | ValueNone -> () + | ValueSome (nextSnapshot) -> let beforePoint = beforeTrackingPoint.GetPoint(textView.TextSnapshot) @@ -185,7 +181,7 @@ type BraceCompletionSession if closingSnapshotPoint.Position > 0 then match tryGetCaretPosition this with - | Some caretPos when not (this.HasNoForwardTyping(caretPos, closingSnapshotPoint.Subtract(1))) -> true + | ValueSome caretPos when not (this.HasNoForwardTyping(caretPos, closingSnapshotPoint.Subtract(1))) -> true | _ -> false else false @@ -265,7 +261,7 @@ type BraceCompletionSession match caretPos with // ensure that we are within the session before clearing - | Some caretPos when + | ValueSome caretPos when caretPos.Position < closingSnapshotPoint.Position && closingSnapshotPoint.Position > 0 -> @@ -310,7 +306,7 @@ type BraceCompletionSession member this.PostReturn() = match tryGetCaretPosition this with - | Some caretPos -> + | ValueSome caretPos -> let closingSnapshotPoint = closingPoint.GetPoint(subjectBuffer.CurrentSnapshot) if @@ -580,13 +576,13 @@ type BraceCompletionSessionProvider [] maybe { let! document = openingPoint.Snapshot.GetOpenDocumentInCurrentContextWithChanges() - |> Option.ofObj + |> ValueOption.ofObj let! sessionFactory = document.TryGetLanguageService() let! session = sessionFactory.TryCreateSession(document, openingPoint.Position, openingBrace, CancellationToken.None) - |> Option.ofObj + |> ValueOption.ofObj let undoHistory = undoManager.GetTextBufferUndoManager(textView.TextBuffer).TextBufferUndoHistory diff --git a/vsintegration/src/FSharp.Editor/Common/Pervasive.fs b/vsintegration/src/FSharp.Editor/Common/Pervasive.fs index 0ad2b8ce8c7..bbd45cdbb43 100644 --- a/vsintegration/src/FSharp.Editor/Common/Pervasive.fs +++ b/vsintegration/src/FSharp.Editor/Common/Pervasive.fs @@ -10,14 +10,14 @@ let inline isSignatureFile (filePath: string) = String.Equals(Path.GetExtension filePath, ".fsi", StringComparison.OrdinalIgnoreCase) /// Returns the corresponding signature file path for given implementation file path or vice versa -let getOtherFile (filePath: string) = +let inline getOtherFile (filePath: string) = if isSignatureFile filePath then Path.ChangeExtension(filePath, ".fs") else Path.ChangeExtension(filePath, ".fsi") /// Checks if the file paht ends with '.fsx' or '.fsscript' -let isScriptFile (filePath: string) = +let inline isScriptFile (filePath: string) = let ext = Path.GetExtension filePath String.Equals(ext, ".fsx", StringComparison.OrdinalIgnoreCase) @@ -42,7 +42,7 @@ type MaybeBuilder() = // (unit -> M<'T>) -> M<'T> [] - member _.Delay(f: unit -> 'T option) : 'T option = f () + member inline _.Delay([] f: unit -> 'T option) : 'T option = f () // M<'T> -> M<'T> -> M<'T> // or @@ -55,18 +55,18 @@ type MaybeBuilder() = // M<'T> * ('T -> M<'U>) -> M<'U> [] - member inline _.Bind(value, f: 'T -> 'U option) : 'U option = Option.bind f value + member inline _.Bind(value, [] f: 'T -> 'U option) : 'U option = Option.bind f value // M<'T> * ('T -> M<'U>) -> M<'U> [] - member inline _.Bind(value: 'T voption, f: 'T -> 'U option) : 'U option = + member inline _.Bind(value: 'T voption, [] f: 'T -> 'U option) : 'U option = match value with | ValueNone -> None | ValueSome value -> f value // 'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable [] - member _.Using(resource: ('T :> System.IDisposable), body: _ -> _ option) : _ option = + member inline _.Using(resource: ('T :> System.IDisposable), [] body: _ -> _ option) : _ option = try body resource finally @@ -86,28 +86,28 @@ type MaybeBuilder() = // or // seq<'T> * ('T -> M<'U>) -> seq> [] - member x.For(sequence: seq<_>, body: 'T -> unit option) : _ option = + member inline x.For(sequence: seq<_>, [] body: 'T -> unit option) : _ option = // OPTIMIZE: This could be simplified so we don't need to make calls to Using, While, Delay. x.Using(sequence.GetEnumerator(), (fun enum -> x.While(enum.MoveNext, x.Delay(fun () -> body enum.Current)))) let maybe = MaybeBuilder() -[] +[] type AsyncMaybeBuilder() = [] - member _.Return value : Async<'T option> = Some value |> async.Return + member inline _.Return value : Async<'T option> = async.Return(Some value) [] - member _.ReturnFrom value : Async<'T option> = value + member inline _.ReturnFrom value : Async<'T option> = value [] - member _.ReturnFrom(value: 'T option) : Async<'T option> = async.Return value + member inline _.ReturnFrom(value: 'T option) : Async<'T option> = async.Return value [] - member _.Zero() : Async = Some() |> async.Return + member inline _.Zero() : Async = async.Return(Some()) [] - member _.Delay(f: unit -> Async<'T option>) : Async<'T option> = async.Delay f + member inline _.Delay([] f: unit -> Async<'T option>) : Async<'T option> = async.Delay f [] member _.Combine(r1, r2: Async<'T option>) : Async<'T option> = @@ -120,7 +120,7 @@ type AsyncMaybeBuilder() = } [] - member _.Bind(value: Async<'T option>, f: 'T -> Async<'U option>) : Async<'U option> = + member inline _.Bind(value: Async<'T option>, [] f: 'T -> Async<'U option>) : Async<'U option> = async { let! value' = value @@ -130,14 +130,14 @@ type AsyncMaybeBuilder() = } [] - member _.Bind(value: System.Threading.Tasks.Task<'T>, f: 'T -> Async<'U option>) : Async<'U option> = + member inline _.Bind(value: System.Threading.Tasks.Task<'T>, [] f: 'T -> Async<'U option>) : Async<'U option> = async { let! value' = Async.AwaitTask value return! f value' } [] - member _.Bind(value: 'T option, f: 'T -> Async<'U option>) : Async<'U option> = + member inline _.Bind(value: 'T option, [] f: 'T -> Async<'U option>) : Async<'U option> = async { match value with | None -> return None @@ -145,7 +145,7 @@ type AsyncMaybeBuilder() = } [] - member _.Bind(value: 'T voption, f: 'T -> Async<'U option>) : Async<'U option> = + member inline _.Bind(value: 'T voption, [] f: 'T -> Async<'U option>) : Async<'U option> = async { match value with | ValueNone -> return None @@ -153,7 +153,7 @@ type AsyncMaybeBuilder() = } [] - member _.Using(resource: ('T :> IDisposable), body: 'T -> Async<'U option>) : Async<'U option> = + member inline _.Using(resource: ('T :> IDisposable), [] body: 'T -> Async<'U option>) : Async<'U option> = async { use resource = resource return! body resource @@ -167,7 +167,7 @@ type AsyncMaybeBuilder() = x.Zero() [] - member x.For(sequence: seq<_>, body: 'T -> Async) : Async = + member inline x.For(sequence: seq<_>, [] body: 'T -> Async) : Async = x.Using(sequence.GetEnumerator(), (fun enum -> x.While(enum.MoveNext, x.Delay(fun () -> body enum.Current)))) [] diff --git a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs index 2c0475d4d37..eec57d2a238 100644 --- a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs +++ b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs @@ -62,13 +62,6 @@ module internal RoslynHelpers = (Position.fromZ startLine.LineNumber (textSpan.Start - startLine.Start)) (Position.fromZ endLine.LineNumber (textSpan.End - endLine.Start)) - let GetCompletedTaskResult (task: Task<'TResult>) = - if task.Status = TaskStatus.RanToCompletion then - task.Result - else - Assert.Exception(task.Exception.GetBaseException()) - raise (task.Exception.GetBaseException()) - /// maps from `TextTag` of the F# Compiler to Roslyn `TextTags` for use in tooltips let roslynTag = function diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index a13b116c4ab..e994b957111 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -2,10 +2,7 @@ module internal Microsoft.VisualStudio.FSharp.Editor.WorkspaceExtensions open System -open System.Diagnostics open System.Runtime.CompilerServices -open System.Threading -open System.Threading.Tasks open Microsoft.CodeAnalysis open Microsoft.VisualStudio.FSharp.Editor @@ -15,8 +12,6 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks -open CancellableTasks -open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks [] module private CheckerExtensions = @@ -161,13 +156,6 @@ type Document with ProjectCache.Projects.GetValue(this.Project, ConditionalWeakTable<_, _>.CreateValueCallback (fun _ -> result)) } - /// Get the compilation defines from F# project that is associated with the given F# document. - member this.GetFSharpCompilationDefinesAsync(userOpName) = - async { - let! _, _, parsingOptions, _ = this.GetFSharpCompilationOptionsAsync(userOpName) - return CompilerEnvironment.GetConditionalDefinesForEditing parsingOptions - } - /// Get the compilation defines and language version from F# project that is associated with the given F# document. member this.GetFsharpParsingOptionsAsync(userOpName) = async { diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index ab27f8d92af..429b21ce64a 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -122,14 +122,14 @@ module FSharpFindUsagesService = let! declarationSpans = match declarationRange with - | Some range -> cancellableTask { return! rangeToDocumentSpans (document.Project.Solution, range) } + | Some range -> rangeToDocumentSpans (document.Project.Solution, range) | None -> CancellableTask.singleton [||] let declarationSpans = declarationSpans |> Array.distinctBy (fun x -> x.Document.FilePath, x.Document.Project.FilePath) - let isExternal = declarationSpans |> Array.isEmpty + let isExternal = Array.isEmpty declarationSpans let displayParts = ImmutableArray.Create(Microsoft.CodeAnalysis.TaggedText(TextTags.Text, symbol.Ident.idText)) From 6da2790461595867f9ae1f9bc4f2fe1c6f335c49 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 18 Sep 2023 19:49:50 +0200 Subject: [PATCH 105/222] wip --- src/Compiler/Service/TransparentCompiler.fs | 75 ++++++++++++--------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 078ae15f265..dd8779dea72 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -41,6 +41,7 @@ open FSharp.Compiler.EditorServices open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.CreateILModule open FSharp.Compiler.TypedTreeOps +open System.Threading type internal FSharpFile = { @@ -276,7 +277,7 @@ type internal CompilerCaches() = member val BootstrapInfo = AsyncMemoize(name = "BootstrapInfo") - member val TcLastFile = AsyncMemoize(name = "TcPrior") + member val TcLastFile = AsyncMemoize(name = "TcLastFile") member val TcIntermediate = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "TcIntermediate") @@ -313,6 +314,10 @@ type internal TransparentCompiler let caches = CompilerCaches() + let maxTypeCheckingParallelism = max 1 (Environment.ProcessorCount / 2) + + let maxParallelismSemaphore = new SemaphoreSlim(maxTypeCheckingParallelism) + // We currently share one global dependency provider for all scripts for the FSharpChecker. // For projects, one is used per project. // @@ -1082,33 +1087,39 @@ type internal TransparentCompiler let input, moduleNamesDict = DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input - let! finisher = - CheckOneInputWithCallback - node - ((fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), - tcConfig, - tcImports, - tcGlobals, - None, - TcResultsSink.WithSink sink, - prevTcInfo.tcState, - input, - true) - |> Cancellable.toAsync - - //fileChecked.Trigger fileName - - let newErrors = - Array.append parseErrors (capturingDiagnosticsLogger.Diagnostics |> List.toArray) - - return - { - finisher = finisher - moduleNamesDict = moduleNamesDict - tcDiagnosticsRev = [ newErrors ] - tcDependencyFiles = [ fileName ] - sink = sink - } + let! ct = Async.CancellationToken + try + do! maxParallelismSemaphore.WaitAsync(ct) |> Async.AwaitTask + + let! finisher = + CheckOneInputWithCallback + node + ((fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), + tcConfig, + tcImports, + tcGlobals, + None, + TcResultsSink.WithSink sink, + prevTcInfo.tcState, + input, + true) + |> Cancellable.toAsync + + //fileChecked.Trigger fileName + + let newErrors = + Array.append parseErrors (capturingDiagnosticsLogger.Diagnostics |> List.toArray) + + return + { + finisher = finisher + moduleNamesDict = moduleNamesDict + tcDiagnosticsRev = [ newErrors ] + tcDependencyFiles = [ fileName ] + sink = sink + } + finally + maxParallelismSemaphore.Release() |> ignore } ) @@ -1306,13 +1317,13 @@ type internal TransparentCompiler let! parseResults, _sourceText = getParseResult bootstrapInfo creationDiags file - let! result, priorTcInfo = ComputeTcLastFile bootstrapInfo snapshotWithSources + let! result, tcInfo = ComputeTcLastFile bootstrapInfo snapshotWithSources let (tcEnv, _topAttribs, checkedImplFileOpt, ccuSigForFile) = result - let tcState = priorTcInfo.tcState + let tcState = tcInfo.tcState - let sink = priorTcInfo.sink.Head // TODO: don't use head + let sink = tcInfo.sink.Head // TODO: don't use head let tcResolutions = sink.GetResolutions() let tcSymbolUses = sink.GetSymbolUses() @@ -1322,7 +1333,7 @@ type internal TransparentCompiler let tcDiagnostics = seq { - yield! priorTcInfo.TcDiagnostics + yield! tcInfo.TcDiagnostics //for x in tcIntermediate.tcDiagnosticsRev do // yield! x From 4901a68a5640465db26f8eccadb0974d4c278d73 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Wed, 20 Sep 2023 12:30:26 +0200 Subject: [PATCH 106/222] wip --- vsintegration/src/FSharp.Editor/Common/Extensions.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index 7a5b69355f9..0f058aee79b 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -269,7 +269,7 @@ module String = [] module Option = - let guard (x: bool) : Option = if x then Some() else None + let guard (x: bool) : ValueOption = if x then ValueSome() else ValueNone let attempt (f: unit -> 'T) = try From bd4baba8abcfe2bb87047865bb42008ce10cadcc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 20 Sep 2023 13:21:15 +0000 Subject: [PATCH 107/222] Automated command ran: fantomas Co-authored-by: vzarytovskii <1260985+vzarytovskii@users.noreply.github.com> --- .../BraceCompletionSessionProvider.fs | 3 ++- .../Completion/CompletionProvider.fs | 10 ++++++--- .../LanguageService/WorkspaceExtensions.fs | 1 - .../Navigation/GoToDefinition.fs | 22 ++++++++++--------- .../Structure/BlockStructureService.fs | 4 +++- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs b/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs index 1fd0d3da25b..6c090529f2c 100644 --- a/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs +++ b/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs @@ -25,7 +25,8 @@ module BraceCompletionSessionProviderHelpers = let inline tryGetCaretPoint (buffer: ITextBuffer) (session: IBraceCompletionSession) = ValueOption.ofNullable (session.TextView.Caret.Position.Point.GetPoint(buffer, PositionAffinity.Predecessor)) - let inline tryGetCaretPosition (session: IBraceCompletionSession) = tryGetCaretPoint session.SubjectBuffer session + let inline tryGetCaretPosition (session: IBraceCompletionSession) = + tryGetCaretPoint session.SubjectBuffer session let tryInsertAdditionalBracePair (session: IBraceCompletionSession) openingChar closingChar = let sourceCode = session.TextView.TextSnapshot diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 01a7a171da1..e78e7783790 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -218,7 +218,8 @@ type internal FSharpCompletionProvider if n <> 0 then n else - x.MinorPriority.CompareTo(y.MinorPriority)) declarations.Items + x.MinorPriority.CompareTo(y.MinorPriority)) + declarations.Items declarationItems <- declarations.Items @@ -230,6 +231,7 @@ type internal FSharpCompletionProvider for number = 0 to declarationItems.Length - 1 do let declarationItem = declarationItems[number] + let glyph = Tokenizer.FSharpGlyphToRoslynGlyph(declarationItem.Glyph, declarationItem.Accessibility) @@ -280,7 +282,7 @@ type internal FSharpCompletionProvider let sortText = priority.ToString("D6") let completionItem = completionItem.WithSortText(sortText) results.Add(completionItem) - + if results.Count > 0 && not declarations.IsForType @@ -431,7 +433,9 @@ type internal FSharpCompletionProvider let! parseResults = document.GetFSharpParseResultsAsync(nameof (FSharpCompletionProvider)) let fullNameIdents = - fullName |> ValueOption.map (fun x -> x.Split '.') |> ValueOption.defaultValue [||] + fullName + |> ValueOption.map (fun x -> x.Split '.') + |> ValueOption.defaultValue [||] let insertionPoint = if settings.CodeFixes.AlwaysPlaceOpensAtTopLevel then diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index e994b957111..3f3842b27ce 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -12,7 +12,6 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks - [] module private CheckerExtensions = diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 7623c41c830..0c5727fb4cb 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -138,8 +138,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let tryFindExternalSymbolUse (targetSymbolUse: FSharpSymbolUse) (x: FSharpSymbolUse) = match x.Symbol, targetSymbolUse.Symbol with - | (:? FSharpEntity as symbol1), (:? FSharpEntity as symbol2) when x.IsFromDefinition -> - symbol1.DisplayName = symbol2.DisplayName + | (:? FSharpEntity as symbol1), (:? FSharpEntity as symbol2) when x.IsFromDefinition -> symbol1.DisplayName = symbol2.DisplayName | (:? FSharpMemberOrFunctionOrValue as symbol1), (:? FSharpMemberOrFunctionOrValue as symbol2) -> symbol1.DisplayName = symbol2.DisplayName @@ -153,12 +152,15 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let pg1, pg2 = pg1.ToArray(), pg2.ToArray() // We filter out/fixup first "unit" parameter in the group, since it just represents the `()` call notation, for example `"string".Clone()` will have one curried group with one parameter which type is unit. let pg1 = // If parameter has no name and it's unit type, filter it out - if pg1.Length > 0 + if + pg1.Length > 0 && Option.isNone pg1[0].Name - && pg1[0].Type.StripAbbreviations().TypeDefinition.DisplayName = "Unit" then - pg1[1..] + && pg1[0].Type.StripAbbreviations().TypeDefinition.DisplayName = "Unit" + then + pg1[1..] else - pg1 + pg1 + pg1.Length = pg2.Length && ((pg1, pg2) ||> Seq.forall2 (fun p1 p2 -> areTypesEqual p1.Type p2.Type)))) && areTypesEqual symbol1.ReturnParameter.Type symbol2.ReturnParameter.Type @@ -605,16 +607,15 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let! cancellationToken = CancellableTask.getCancellationToken () - let! _, checkResults = - tmpShownDoc.GetFSharpParseAndCheckResultsAsync("NavigateToExternalDeclaration") + let! _, checkResults = tmpShownDoc.GetFSharpParseAndCheckResultsAsync("NavigateToExternalDeclaration") let r = // This tries to find the best possible location of the target symbol's location in the metadata source. // We really should rely on symbol equality within FCS instead of doing it here, // but the generated metadata as source isn't perfect for symbol equality. let symbols = checkResults.GetAllUsesOfAllSymbolsInFile(cancellationToken) - - symbols + + symbols |> Seq.tryFindV (tryFindExternalSymbolUse targetSymbolUse) |> ValueOption.map (fun x -> x.Range) |> ValueOption.toOption @@ -623,6 +624,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = | None -> return TextSpan.empty | Some r -> let! text = tmpShownDoc.GetTextAsync(cancellationToken) + match RoslynHelpers.TryFSharpRangeToTextSpan(text, r) with | ValueSome span -> return span | _ -> return TextSpan.empty diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index c84a82b9632..f96ea848c1f 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -145,7 +145,9 @@ module internal BlockStructure = else FSharpBlockTypes.Nonstructural - ValueSome(FSharpBlockSpan(blockType, true, textSpan, hintSpan, bannerText, autoCollapse = isAutoCollapsible scopeRange.Scope)) + ValueSome( + FSharpBlockSpan(blockType, true, textSpan, hintSpan, bannerText, autoCollapse = isAutoCollapsible scopeRange.Scope) + ) | _, _ -> ValueNone) open BlockStructure From 21898a1c5394c17172fe69c9b8aa723583d09021 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 21 Sep 2023 12:29:04 +0200 Subject: [PATCH 108/222] clear caches on solution unload --- src/Compiler/Facilities/AsyncMemoize.fs | 7 +++++++ src/Compiler/Service/TransparentCompiler.fs | 7 +++++-- src/Compiler/Service/service.fs | 1 - 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index da907cc06fb..71d328ed700 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -254,6 +254,11 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers member this.Remove(key) = this.Remove(key, Unchecked.defaultof<_>) + member _.Clear() = + dictionary.Clear() + strongList.Clear() + weakList.Clear() + member _.GetValues() = strongList |> Seq.append weakList @@ -579,6 +584,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | Existing job -> return! job |> Async.AwaitTask } + member _.Clear() = cache.Clear() + member val Event = event.Publish member this.OnEvent = this.Event.Add diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index dd8779dea72..56f752b7293 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -312,7 +312,7 @@ type internal TransparentCompiler // Is having just one of these ok? let lexResourceManager = Lexhelp.LexResourceManager() - let caches = CompilerCaches() + let mutable caches = CompilerCaches() let maxTypeCheckingParallelism = max 1 (Environment.ProcessorCount / 2) @@ -1773,7 +1773,10 @@ type internal TransparentCompiler member _.ClearCache(options: seq, userOpName: string) : unit = backgroundCompiler.ClearCache(options, userOpName) - member _.ClearCaches() : unit = backgroundCompiler.ClearCaches() + member _.ClearCaches() : unit = + backgroundCompiler.ClearCaches() + caches <- CompilerCaches() + member _.DownsizeCaches() : unit = backgroundCompiler.DownsizeCaches() member _.FileChecked: IEvent = diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index ce51817a73b..2821a5bfe85 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -363,7 +363,6 @@ type FSharpChecker backgroundCompiler.ClearCaches() ClearAllILModuleReaderCache() - // This is for unit testing only member ic.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() = use _ = Activity.startNoTags "FsharpChecker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients" From d3d1d87c460e8573c2a648653b85bb546dfee693 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 21 Sep 2023 17:59:38 +0200 Subject: [PATCH 109/222] wip --- src/Compiler/Service/TransparentCompiler.fs | 40 ++++++++++++--------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 56f752b7293..808c281648f 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -312,6 +312,7 @@ type internal TransparentCompiler // Is having just one of these ok? let lexResourceManager = Lexhelp.LexResourceManager() + // Mutable so we can easily clear them by creating a new instance let mutable caches = CompilerCaches() let maxTypeCheckingParallelism = max 1 (Environment.ProcessorCount / 2) @@ -520,10 +521,18 @@ type internal TransparentCompiler ] /// Bootstrap info that does not depend on contents of the files - let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) = + let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) removeReferences = - let key = projectSnapshot.WithoutFileVersions.Key + let projectSnapshot = if removeReferences then { projectSnapshot with ReferencedProjects = [] } else projectSnapshot + let projectSnapshotKey = projectSnapshot.WithoutFileVersions.Key + + let key = + { new ICacheKey<_, _> with + member _.GetLabel() = projectSnapshotKey.GetLabel() + member _.GetKey() = projectSnapshotKey.GetKey(), removeReferences + member _.GetVersion() = projectSnapshotKey.GetVersion() } + caches.BootstrapInfoStatic.Get( key, async { @@ -749,7 +758,7 @@ type internal TransparentCompiler tcGlobals, initialTcInfo, loadClosureOpt, - _importsInvalidatedByTypeProvider = ComputeBootstrapInfoStatic projectSnapshot + _importsInvalidatedByTypeProvider = ComputeBootstrapInfoStatic projectSnapshot false let fileSnapshots = Map [ for f in projectSnapshot.SourceFiles -> f.FileName, f ] @@ -866,7 +875,7 @@ type internal TransparentCompiler } } - let ComputeParseFile bootstrapInfo (file: FSharpFileSnapshotWithSource) = + let ComputeParseFile (tcConfig: TcConfig) (file: FSharpFileSnapshotWithSource) = let key = { new ICacheKey<_, _> with @@ -888,8 +897,6 @@ type internal TransparentCompiler Activity.Tags.version, file.SourceHash |] - let tcConfig = bootstrapInfo.TcConfig - let diagnosticsLogger = CompilationDiagnosticLogger("Parse", tcConfig.diagnosticsOptions) // Return the disposable object that cleans up @@ -1247,7 +1254,7 @@ type internal TransparentCompiler let! parsedInputs = projectSnapshot.SourceFiles - |> Seq.map (ComputeParseFile bootstrapInfo) + |> Seq.map (ComputeParseFile bootstrapInfo.TcConfig) |> Async.Parallel let! graph, dependencyFiles = @@ -1266,18 +1273,18 @@ type internal TransparentCompiler } ) - let getParseResult (bootstrapInfo: BootstrapInfo) creationDiags file = + let getParseResult (tcConfig: TcConfig) creationDiags file = async { - let! parseTree, parseDiagnostics, sourceText = ComputeParseFile bootstrapInfo file + let! parseTree, parseDiagnostics, sourceText = ComputeParseFile tcConfig file let parseDiagnostics = DiagnosticHelpers.CreateDiagnostics( - bootstrapInfo.TcConfig.diagnosticsOptions, + tcConfig.diagnosticsOptions, false, file.FileName, parseDiagnostics, suggestNamesForErrors, - bootstrapInfo.TcConfig.flatErrors, + tcConfig.flatErrors, None ) @@ -1315,7 +1322,7 @@ type internal TransparentCompiler let! snapshotWithSources = LoadSources bootstrapInfo priorSnapshot let file = snapshotWithSources.SourceFiles |> List.last - let! parseResults, _sourceText = getParseResult bootstrapInfo creationDiags file + let! parseResults, _sourceText = getParseResult bootstrapInfo.TcConfig creationDiags file let! result, tcInfo = ComputeTcLastFile bootstrapInfo snapshotWithSources @@ -1399,7 +1406,7 @@ type internal TransparentCompiler let! parsedInputs = projectSnapshot.SourceFiles - |> Seq.map (ComputeParseFile bootstrapInfo) + |> Seq.map (ComputeParseFile bootstrapInfo.TcConfig) |> Async.Parallel let! graph, dependencyFiles = @@ -1560,7 +1567,7 @@ type internal TransparentCompiler let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile let tcDiagnostics = tcInfo.TcDiagnostics let tcDependencyFiles = tcInfo.tcDependencyFiles - + let tcDiagnostics = DiagnosticHelpers.CreateDiagnostics( diagnosticsOptions, @@ -1692,12 +1699,13 @@ type internal TransparentCompiler async { //use _ = // Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + let snapshotWithoutReferences = { projectSnapshot with ReferencedProjects = [] } - match! ComputeBootstrapInfo projectSnapshot with + match! ComputeBootstrapInfo snapshotWithoutReferences with | None, creationDiags -> return emptyParseResult fileName creationDiags | Some bootstrapInfo, creationDiags -> let! file = bootstrapInfo.GetFile fileName |> LoadSource - let! parseResult, _ = getParseResult bootstrapInfo creationDiags file + let! parseResult, _ = getParseResult bootstrapInfo.TcConfig creationDiags file return parseResult } From 152185195b5591b3b3e9f17a9e7156f081e47b8c Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 22 Sep 2023 13:22:14 +0200 Subject: [PATCH 110/222] Checking referenced projects not needed for parsing --- src/Compiler/Facilities/AsyncMemoize.fs | 14 +++---- src/Compiler/Service/FSharpCheckerResults.fs | 12 +++--- src/Compiler/Service/FSharpCheckerResults.fsi | 12 +++--- src/Compiler/Service/TransparentCompiler.fs | 38 ++++++++----------- 4 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 71d328ed700..146d817eb43 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -37,7 +37,7 @@ type MemoizeRequest<'TValue> = | Sync type internal Job<'TValue> = - | Running of TaskCompletionSource<'TValue> * CancellationTokenSource * Async<'TValue> + | Running of TaskCompletionSource<'TValue> * CancellationTokenSource * Async<'TValue> * DateTime | Completed of 'TValue type internal CacheEvent = @@ -407,7 +407,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T match msg, cached with | Sync, _ -> New | GetOrCompute _, Some (Completed result) -> Existing(Task.FromResult result) - | GetOrCompute (_, ct), Some (Running (tcs, _, _)) -> + | GetOrCompute (_, ct), Some (Running (tcs, _, _, _)) -> incrRequestCount key ct.Register(fun _ -> @@ -429,7 +429,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T key.Key, key.Version, key.Label, - (Running(TaskCompletionSource(), (new CancellationTokenSource()), computation)) + (Running(TaskCompletionSource(), (new CancellationTokenSource()), computation, DateTime.Now)) ) New @@ -447,7 +447,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T match action, cached with - | OriginatorCanceled, Some (Running (tcs, cts, computation)) -> + | OriginatorCanceled, Some (Running (tcs, cts, computation, _)) -> decrRequestCount key @@ -480,7 +480,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cts.Token) |> ignore - | CancelRequest, Some (Running (tcs, cts, _c)) -> + | CancelRequest, Some (Running (tcs, cts, _c, _)) -> decrRequestCount key @@ -496,14 +496,14 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | CancelRequest, None | CancelRequest, Some (Completed _) -> () - | JobFailed ex, Some (Running (tcs, _cts, _c)) -> + | JobFailed ex, Some (Running (tcs, _cts, _c, _ts)) -> cancelRegistration key cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore log (Failed, key) tcs.TrySetException ex |> ignore - | JobCompleted result, Some (Running (tcs, _cts, _c)) -> + | JobCompleted result, Some (Running (tcs, _cts, _c, _ts)) -> cancelRegistration key cache.Set(key.Key, key.Version, key.Label, (Completed result)) requestCounts.Remove key |> ignore diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index b4b3811d03a..d4ee510d342 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -150,6 +150,8 @@ type FSharpFileSnapshotWithSource = type ReferenceOnDisk = { Path: string; LastModified: DateTime } +type ProjectSnapshotKey = string * bool + [] type FSharpProjectSnapshot = { @@ -279,7 +281,7 @@ type FSharpProjectSnapshot = member this.FileKey(fileName) = { new ICacheKey<_, _> with member _.GetLabel() = fileName |> shortPath - member _.GetKey() = fileName, this.Key.GetKey() + member _.GetKey() = fileName, this.Key.GetKey() |> fst member _.GetVersion() = this @@ -317,9 +319,9 @@ type FSharpProjectSnapshot = UseScriptResolutionRules = this.UseScriptResolutionRules } - interface ICacheKey with + interface ICacheKey<(string * bool), FSharpProjectSnapshotDebugVersion> with member this.GetLabel() = this.ToString() - member this.GetKey() = this.ProjectFileName + member this.GetKey() = this.ProjectFileName, this.ReferencedProjects = [] // This is for bootstrapInfo cache where we might need to keep an extra version without project references to speed up getting TcConfig for just parsing files. Probably this should be reworked eventually... member this.GetVersion() = this.GetDebugVersion() and FSharpProjectSnapshotWithSources = @@ -381,7 +383,7 @@ and FSharpProjectSnapshotWithSources = member this.FileKey(fileName) = { new ICacheKey<_, _> with member _.GetLabel() = fileName |> shortPath - member _.GetKey() = fileName, this.Key.GetKey() + member _.GetKey() = fileName, this.Key.GetKey() |> fst member _.GetVersion() = this @@ -389,7 +391,7 @@ and FSharpProjectSnapshotWithSources = .WithoutImplFilesThatHaveSignaturesExceptLastOne.Key.GetVersion() } - interface ICacheKey with + interface ICacheKey<(string * bool), FSharpProjectSnapshotWithSourcesVersion> with member this.GetLabel() = this.ProjectSnapshot.Key.ToString() member this.GetKey() = this.ProjectSnapshot.Key.GetKey() diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index ef88a33becf..1f9f8e6935d 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -65,6 +65,8 @@ type FSharpFileSnapshotWithSource = type ReferenceOnDisk = { Path: string; LastModified: DateTime } +type ProjectSnapshotKey = string * bool + [] type FSharpProjectSnapshot = { @@ -145,11 +147,11 @@ type FSharpProjectSnapshot = member GetLastModifiedTimeOnDisk: unit -> DateTime - member internal Key: ICacheKey + member internal Key: ICacheKey member internal FileKey: fileName: string -> ICacheKey<(string * string), FSharpProjectSnapshotVersion> - interface ICacheKey + interface ICacheKey and FSharpProjectSnapshotWithSources = { ProjectSnapshot: FSharpProjectSnapshot @@ -166,8 +168,8 @@ and FSharpProjectSnapshotWithSources = member internal FileKey: fileName: string -> ICacheKey<(string * string), FSharpProjectSnapshotWithSourcesVersion> - member internal Key: ICacheKey - interface ICacheKey + member internal Key: ICacheKey + interface ICacheKey and FSharpProjectSnapshotWithSourcesDebugVersion = { ProjectSnapshotVersion: FSharpProjectSnapshotDebugVersion @@ -209,7 +211,7 @@ and [] public FSharpReferencedProjectSnapshot = static member CreateFSharp: projectOutputFile: string * options: FSharpProjectSnapshot -> FSharpReferencedProjectSnapshot - member internal Key: ICacheKey + member internal Key: ICacheKey /// A set of information describing a project or script build configuration. type public FSharpProjectOptions = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 808c281648f..2a67b8f37b4 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -521,25 +521,17 @@ type internal TransparentCompiler ] /// Bootstrap info that does not depend on contents of the files - let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) removeReferences = + let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) = - let projectSnapshot = if removeReferences then { projectSnapshot with ReferencedProjects = [] } else projectSnapshot - - let projectSnapshotKey = projectSnapshot.WithoutFileVersions.Key - - let key = - { new ICacheKey<_, _> with - member _.GetLabel() = projectSnapshotKey.GetLabel() - member _.GetKey() = projectSnapshotKey.GetKey(), removeReferences - member _.GetVersion() = projectSnapshotKey.GetVersion() } - caches.BootstrapInfoStatic.Get( - key, + projectSnapshot.WithoutFileVersions.Key, async { use _ = Activity.start "ComputeBootstrapInfoStatic" - [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] + [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName + "references", projectSnapshot.ReferencedProjects.Length.ToString() + |] let useSimpleResolutionSwitch = "--simpleresolution" let commandLineArgs = projectSnapshot.CommandLineOptions @@ -758,7 +750,7 @@ type internal TransparentCompiler tcGlobals, initialTcInfo, loadClosureOpt, - _importsInvalidatedByTypeProvider = ComputeBootstrapInfoStatic projectSnapshot false + _importsInvalidatedByTypeProvider = ComputeBootstrapInfoStatic projectSnapshot let fileSnapshots = Map [ for f in projectSnapshot.SourceFiles -> f.FileName, f ] @@ -799,7 +791,10 @@ type internal TransparentCompiler } } - let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) = + let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) withoutReferences = + + let projectSnapshot = if withoutReferences then { projectSnapshot with ReferencedProjects = [] } else projectSnapshot + caches.BootstrapInfo.Get( projectSnapshot.Key, async { @@ -1313,7 +1308,7 @@ type internal TransparentCompiler use _ = Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - match! ComputeBootstrapInfo projectSnapshot with + match! ComputeBootstrapInfo projectSnapshot false with | None, creationDiags -> return emptyParseResult fileName creationDiags, FSharpCheckFileAnswer.Aborted | Some bootstrapInfo, creationDiags -> @@ -1535,7 +1530,7 @@ type internal TransparentCompiler Trace.TraceInformation($"Using assembly on disk: {name}") return ProjectAssemblyDataResult.Unavailable true else - match! ComputeBootstrapInfo projectSnapshot with + match! ComputeBootstrapInfo projectSnapshot false with | None, _ -> Trace.TraceInformation($"Using assembly on disk (unintentionally): {name}") return ProjectAssemblyDataResult.Unavailable true @@ -1552,7 +1547,7 @@ type internal TransparentCompiler projectSnapshot.Key, async { - match! ComputeBootstrapInfo projectSnapshot with + match! ComputeBootstrapInfo projectSnapshot false with | None, creationDiags -> return FSharpCheckProjectResults(projectSnapshot.ProjectFileName, None, keepAssemblyContents, creationDiags, None) | Some bootstrapInfo, creationDiags -> @@ -1567,7 +1562,7 @@ type internal TransparentCompiler let tcEnvAtEnd = tcInfo.tcEnvAtEndOfFile let tcDiagnostics = tcInfo.TcDiagnostics let tcDependencyFiles = tcInfo.tcDependencyFiles - + let tcDiagnostics = DiagnosticHelpers.CreateDiagnostics( diagnosticsOptions, @@ -1617,7 +1612,7 @@ type internal TransparentCompiler let tryGetSink (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = async { - match! ComputeBootstrapInfo projectSnapshot with + match! ComputeBootstrapInfo projectSnapshot false with | None, _ -> return None | Some bootstrapInfo, _creationDiags -> @@ -1699,9 +1694,8 @@ type internal TransparentCompiler async { //use _ = // Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - let snapshotWithoutReferences = { projectSnapshot with ReferencedProjects = [] } - match! ComputeBootstrapInfo snapshotWithoutReferences with + match! ComputeBootstrapInfo projectSnapshot true with | None, creationDiags -> return emptyParseResult fileName creationDiags | Some bootstrapInfo, creationDiags -> let! file = bootstrapInfo.GetFile fileName |> LoadSource From a0ce1d89811d7cd5bf15a320582a92a599894576 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 25 Sep 2023 12:12:03 +0200 Subject: [PATCH 111/222] cancel running jobs with the same key --- src/Compiler/Facilities/AsyncMemoize.fs | 55 ++++++++++++++++--- .../CompilerService/AsyncMemoize.fs | 37 +++++++++++++ .../ProjectGeneration.fs | 2 +- 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 146d817eb43..8386c83201d 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -7,6 +7,8 @@ open System.IO open System.Threading open System.Threading.Tasks +open Internal.Utilities.Library.Extras + [] module Utils = @@ -29,16 +31,23 @@ type internal StateUpdate<'TValue> = | JobFailed of exn type internal MemoizeReply<'TValue> = - | New + | New of CancellationToken | Existing of Task<'TValue> type MemoizeRequest<'TValue> = | GetOrCompute of Async<'TValue> * CancellationToken | Sync +[] type internal Job<'TValue> = | Running of TaskCompletionSource<'TValue> * CancellationTokenSource * Async<'TValue> * DateTime | Completed of 'TValue + member this.DebuggerDisplay = + match this with + | Running (_, cts, _, ts) -> + let cancellation = if cts.IsCancellationRequested then " ! Cancellation Requested" else "" + $"Running since {ts.ToShortTimeString()}{cancellation}" + | Completed value -> $"Completed {value}" type internal CacheEvent = | Evicted @@ -229,6 +238,24 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers event CacheEvent.Collected (label, key, version) None + /// Returns an option of a value for given key and version, and also a list of all other versions for given key + member this.GetAll(key, version) = + this.TryGet(key, version), + + match dictionary.TryGetValue key with + | false, _ -> [] + | true, versionDict -> + versionDict.Values + |> Seq.map (fun node -> node.Value) + |> Seq.filter (p24 >> ((<>) version)) + |> Seq.choose (function + | _, _, _, Strong v -> Some v + | _, _, _, Weak r -> + match r.TryGetTarget() with + | true, x -> Some x + | _ -> None) + |> Seq.toList + member _.Remove(key, version) = match dictionary.TryGetValue key with | false, _ -> () @@ -339,10 +366,12 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T ( ?keepStrongly, ?keepWeakly, - ?name: string + ?name: string, + ?cancelDuplicateRunningJobs: bool ) = let name = defaultArg name "N/A" + let cancelDuplicateRunningJobs = defaultArg cancelDuplicateRunningJobs true let event = Event<_>() @@ -401,11 +430,11 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T lock.Do(fun () -> task { - let cached = cache.TryGet(key.Key, key.Version) + let cached, otherVersions = cache.GetAll(key.Key, key.Version) return match msg, cached with - | Sync, _ -> New + | Sync, _ -> New Unchecked.defaultof<_> | GetOrCompute _, Some (Completed result) -> Existing(Task.FromResult result) | GetOrCompute (_, ct), Some (Running (tcs, _, _, _)) -> incrRequestCount key @@ -425,14 +454,21 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T post (key, OriginatorCanceled)) |> saveRegistration key + let cts = new CancellationTokenSource() + cache.Set( key.Key, key.Version, key.Label, - (Running(TaskCompletionSource(), (new CancellationTokenSource()), computation, DateTime.Now)) + (Running(TaskCompletionSource(), cts, computation, DateTime.Now)) ) - New + if cancelDuplicateRunningJobs then + otherVersions + |> Seq.choose (function Running (_tcs, cts, _, _) -> Some cts | _ -> None) + |> Seq.iter (fun cts -> cts.Cancel()) + + New cts.Token }) let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = @@ -561,7 +597,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let! ct = Async.CancellationToken match! processRequest post (key, GetOrCompute(computation, ct)) |> Async.AwaitTask with - | New -> + | New internalCt -> + + let linkedCtSource = CancellationTokenSource.CreateLinkedTokenSource(ct, internalCt) + try return! Async.StartAsTask( @@ -571,7 +610,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T post (key, (JobCompleted result)) return result }, - cancellationToken = ct + cancellationToken = linkedCtSource.Token ) |> Async.AwaitTask with diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index cd6f2c0da14..178b9041798 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -331,3 +331,40 @@ let ``Stress test`` () = Assert.Equal (started, completed + canceled + failed + timeout) Assert.True ((float completed) > ((float started) * 0.1), "Less than 10 % completed jobs") + + +[] +let ``Cancel running jobs with the same key`` () = + + let cache = AsyncMemoize(cancelDuplicateRunningJobs=false) + + let mutable started = 0 + let mutable cancelled = 0 + + let work () = async { + Interlocked.Increment &started |> ignore + let! ct = Async.CancellationToken + use _ = ct.Register(fun () -> Interlocked.Increment &cancelled |> ignore) + for _ in 1..10 do + do! Async.Sleep 30 + } + + let key1 = + { new ICacheKey<_, _> with + member _.GetKey() = 1 + member _.GetVersion() = 1 + member _.GetLabel() = "key1" } + + cache.Get(key1, work()) |> Async.Start + + let key2 = + { new ICacheKey<_, _> with + member _.GetKey() = key1.GetKey() + member _.GetVersion() = key1.GetVersion() + 1 + member _.GetLabel() = "key2" } + + cache.Get(key2, work()) |> Async.Start + + Async.Sleep 100 |> Async.RunSynchronously + + Assert.Equal((2, 1), (started, cancelled)) \ No newline at end of file diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index f92e08f5254..7f5f42764c8 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -833,7 +833,7 @@ type ProjectWorkflowBuilder ?autoStart ) = - let useTransparentCompiler = defaultArg useTransparentCompiler false + let useTransparentCompiler = defaultArg useTransparentCompiler true let useGetSource = not useTransparentCompiler && defaultArg useGetSource false let useChangeNotifications = not useTransparentCompiler && defaultArg useChangeNotifications false let autoStart = defaultArg autoStart true From ce9715a885b191eaf5da5dfec54c79b6220c3d18 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 25 Sep 2023 19:58:05 +0200 Subject: [PATCH 112/222] wip --- src/Compiler/Facilities/AsyncMemoize.fs | 25 ++++++++++++--------- src/Compiler/Service/TransparentCompiler.fs | 3 ++- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 8386c83201d..f21f22ea6b7 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -249,10 +249,10 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers |> Seq.map (fun node -> node.Value) |> Seq.filter (p24 >> ((<>) version)) |> Seq.choose (function - | _, _, _, Strong v -> Some v - | _, _, _, Weak r -> + | _, ver, _, Strong v -> Some (ver, v) + | _, ver, _, Weak r -> match r.TryGetTarget() with - | true, x -> Some x + | true, x -> Some (ver, x) | _ -> None) |> Seq.toList @@ -371,7 +371,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T ) = let name = defaultArg name "N/A" - let cancelDuplicateRunningJobs = defaultArg cancelDuplicateRunningJobs true + let cancelDuplicateRunningJobs = defaultArg cancelDuplicateRunningJobs false let event = Event<_>() @@ -463,10 +463,13 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T (Running(TaskCompletionSource(), cts, computation, DateTime.Now)) ) - if cancelDuplicateRunningJobs then - otherVersions - |> Seq.choose (function Running (_tcs, cts, _, _) -> Some cts | _ -> None) - |> Seq.iter (fun cts -> cts.Cancel()) + otherVersions + |> Seq.choose (function v, Running (_tcs, cts, _, _) -> Some (v, cts) | _ -> None) + |> Seq.iter (fun (_v, cts) -> + System.Diagnostics.Trace.TraceWarning($"{name} Duplicate {key.Label}") + if cancelDuplicateRunningJobs then + System.Diagnostics.Trace.TraceWarning("Canceling") + cts.Cancel()) New cts.Token }) @@ -494,7 +497,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore log (Canceled, key) - System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label} version: {key.Version}" + System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" else // We need to restart the computation @@ -504,7 +507,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T try log (Started, key) - System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label} version: {key.Version}" + System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}" let! result = Async.StartAsTask(computation, cancellationToken = cts.Token) post (key, (JobCompleted result)) with @@ -527,7 +530,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore log (Canceled, key) - System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label} version: {key.Version}" + System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" | CancelRequest, None | CancelRequest, Some (Completed _) -> () diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 2a67b8f37b4..d3bb07ba37b 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1239,8 +1239,9 @@ type internal TransparentCompiler // Type check file and all its dependencies let ComputeTcLastFile (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = + let fileName = projectSnapshot.SourceFiles |> List.last |> (fun f -> f.FileName) caches.TcLastFile.Get( - projectSnapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne.Key, + projectSnapshot.FileKey fileName, async { let file = projectSnapshot.SourceFiles |> List.last From c1025c1cf9a64a704c4a70c0605e4f088c7d614d Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 26 Sep 2023 16:03:23 +0200 Subject: [PATCH 113/222] wip --- src/Compiler/Facilities/AsyncMemoize.fs | 44 +++++++++++++++---- .../FSharpChecker/TransparentCompiler.fs | 18 +++++++- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index f21f22ea6b7..b220ce0c520 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -8,6 +8,7 @@ open System.Threading open System.Threading.Tasks open Internal.Utilities.Library.Extras +open FSharp.Compiler.Diagnostics [] module Utils = @@ -375,6 +376,15 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let event = Event<_>() + let mutable hits = 0 + let mutable started = 0 + let mutable completed = 0 + let mutable canceled = 0 + let mutable restarted = 0 + let mutable failed = 0 + let mutable evicted = 0 + let mutable collected = 0 + let cache = LruCache<'TKey, 'TVersion, Job<'TValue>>( keepStrongly = defaultArg keepStrongly 100, @@ -385,8 +395,12 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | _ -> false), event = (function - | CacheEvent.Evicted -> (fun k -> event.Trigger(JobEvent.Evicted, k)) - | CacheEvent.Collected -> (fun k -> event.Trigger(JobEvent.Collected, k)) + | CacheEvent.Evicted -> (fun k -> + Interlocked.Increment &evicted + event.Trigger(JobEvent.Evicted, k)) + | CacheEvent.Collected -> (fun k -> + Interlocked.Increment &collected + event.Trigger(JobEvent.Collected, k)) | CacheEvent.Weakened -> (fun k -> event.Trigger(JobEvent.Weakened, k)) | CacheEvent.Strengthened -> (fun k -> event.Trigger(JobEvent.Strengthened, k))) ) @@ -435,8 +449,11 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T return match msg, cached with | Sync, _ -> New Unchecked.defaultof<_> - | GetOrCompute _, Some (Completed result) -> Existing(Task.FromResult result) + | GetOrCompute _, Some (Completed result) -> + Interlocked.Increment &hits + Existing(Task.FromResult result) | GetOrCompute (_, ct), Some (Running (tcs, _, _, _)) -> + Interlocked.Increment &hits incrRequestCount key ct.Register(fun _ -> @@ -447,6 +464,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T Existing tcs.Task | GetOrCompute (computation, ct), None -> + Interlocked.Increment &started incrRequestCount key ct.Register(fun _ -> @@ -465,10 +483,11 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T otherVersions |> Seq.choose (function v, Running (_tcs, cts, _, _) -> Some (v, cts) | _ -> None) - |> Seq.iter (fun (_v, cts) -> - System.Diagnostics.Trace.TraceWarning($"{name} Duplicate {key.Label}") + |> Seq.iter (fun (v, cts) -> + use _ = Activity.start $"{name}: Duplicate running job" [| "key", key.Label; "version", $"%A{v}" |] + //System.Diagnostics.Trace.TraceWarning($"{name} Duplicate {key.Label}") if cancelDuplicateRunningJobs then - System.Diagnostics.Trace.TraceWarning("Canceling") + //System.Diagnostics.Trace.TraceWarning("Canceling") cts.Cancel()) New cts.Token @@ -497,7 +516,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore log (Canceled, key) - System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" + Interlocked.Increment &canceled + use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label; "version", $"%A{key.Version}" |] + () + //System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" else // We need to restart the computation @@ -507,6 +529,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T try log (Started, key) + Interlocked.Increment &restarted System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}" let! result = Async.StartAsTask(computation, cancellationToken = cts.Token) post (key, (JobCompleted result)) @@ -530,7 +553,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore log (Canceled, key) - System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" + Interlocked.Increment &canceled + use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label; "version", $"%A{key.Version}" |] + () + //System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" | CancelRequest, None | CancelRequest, Some (Completed _) -> () @@ -540,6 +566,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore log (Failed, key) + Interlocked.Increment &failed tcs.TrySetException ex |> ignore | JobCompleted result, Some (Running (tcs, _cts, _c, _ts)) -> @@ -547,6 +574,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.Set(key.Key, key.Version, key.Label, (Completed result)) requestCounts.Remove key |> ignore log (Finished, key) + Interlocked.Increment &completed if tcs.TrySetResult result = false then failwith "Invalid state: Completed job already completed" diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index e98b9fd31fd..29365320445 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -18,6 +18,10 @@ open System.Threading.Tasks open System.Threading open TypeChecks +open OpenTelemetry +open OpenTelemetry.Resources +open OpenTelemetry.Trace + [] let ``Use Transparent Compiler`` () = @@ -471,7 +475,7 @@ type SignatureFiles = Yes = 1 | No = 2 | Some = 3 let fuzzingTest seed (project: SyntheticProject) = task { let rng = System.Random seed - let checkingThreads = 10 + let checkingThreads = 3 let maxModificationDelayMs = 10 let maxCheckingDelayMs = 20 //let runTimeMs = 30000 @@ -484,7 +488,7 @@ let fuzzingTest seed (project: SyntheticProject) = task { let builder = ProjectWorkflowBuilder(project, useTransparentCompiler = true, autoStart = false) let checker = builder.Checker - + // Force creation and caching of options do! SaveAndCheckProject project checker |> Async.Ignore @@ -590,6 +594,16 @@ let fuzzingTest seed (project: SyntheticProject) = task { do! Task.Delay (rng.Next maxCheckingDelayMs) } + + use tracerProvider = + Sdk.CreateTracerProviderBuilder() + .AddSource("fsc") + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(serviceName="F#", serviceVersion = "1")) + .AddJaegerExporter() + .Build() + |> Some + + use _ = Activity.start $"Fuzzing {project.Name}" [ Activity.Tags.project, project.Name; "seed", seed.ToString() ] |> Some do! task { let threads = From efa0a3f4dea983cebeff1f023964d1de17b0b0c1 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 26 Sep 2023 19:41:27 +0200 Subject: [PATCH 114/222] wip --- src/Compiler/Facilities/AsyncMemoize.fs | 36 ++++++++++--------- .../FSharpChecker/TransparentCompiler.fs | 14 +++++--- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index b220ce0c520..4d18d9541b6 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -396,10 +396,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T event = (function | CacheEvent.Evicted -> (fun k -> - Interlocked.Increment &evicted + Interlocked.Increment &evicted |> ignore event.Trigger(JobEvent.Evicted, k)) | CacheEvent.Collected -> (fun k -> - Interlocked.Increment &collected + Interlocked.Increment &collected |> ignore event.Trigger(JobEvent.Collected, k)) | CacheEvent.Weakened -> (fun k -> event.Trigger(JobEvent.Weakened, k)) | CacheEvent.Strengthened -> (fun k -> event.Trigger(JobEvent.Strengthened, k))) @@ -450,10 +450,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T match msg, cached with | Sync, _ -> New Unchecked.defaultof<_> | GetOrCompute _, Some (Completed result) -> - Interlocked.Increment &hits + Interlocked.Increment &hits |> ignore Existing(Task.FromResult result) | GetOrCompute (_, ct), Some (Running (tcs, _, _, _)) -> - Interlocked.Increment &hits + Interlocked.Increment &hits |> ignore incrRequestCount key ct.Register(fun _ -> @@ -464,7 +464,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T Existing tcs.Task | GetOrCompute (computation, ct), None -> - Interlocked.Increment &started + Interlocked.Increment &started |> ignore incrRequestCount key ct.Register(fun _ -> @@ -483,8 +483,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T otherVersions |> Seq.choose (function v, Running (_tcs, cts, _, _) -> Some (v, cts) | _ -> None) - |> Seq.iter (fun (v, cts) -> - use _ = Activity.start $"{name}: Duplicate running job" [| "key", key.Label; "version", $"%A{v}" |] + |> Seq.iter (fun (_v, cts) -> + use _ = Activity.start $"{name}: Duplicate running job" [| "key", key.Label |] //System.Diagnostics.Trace.TraceWarning($"{name} Duplicate {key.Label}") if cancelDuplicateRunningJobs then //System.Diagnostics.Trace.TraceWarning("Canceling") @@ -516,8 +516,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore log (Canceled, key) - Interlocked.Increment &canceled - use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label; "version", $"%A{key.Version}" |] + Interlocked.Increment &canceled |> ignore + use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] () //System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" @@ -529,7 +529,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T try log (Started, key) - Interlocked.Increment &restarted + Interlocked.Increment &restarted |> ignore System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}" let! result = Async.StartAsTask(computation, cancellationToken = cts.Token) post (key, (JobCompleted result)) @@ -553,8 +553,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore log (Canceled, key) - Interlocked.Increment &canceled - use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label; "version", $"%A{key.Version}" |] + Interlocked.Increment &canceled |> ignore + use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] () //System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" @@ -566,7 +566,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore log (Failed, key) - Interlocked.Increment &failed + Interlocked.Increment &failed |> ignore tcs.TrySetException ex |> ignore | JobCompleted result, Some (Running (tcs, _cts, _c, _ts)) -> @@ -574,7 +574,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.Set(key.Key, key.Version, key.Label, (Completed result)) requestCounts.Remove key |> ignore log (Finished, key) - Interlocked.Increment &completed + Interlocked.Increment &completed |> ignore if tcs.TrySetResult result = false then failwith "Invalid state: Completed job already completed" @@ -677,7 +677,9 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T |> Seq.countBy (function | _, _, Running _ -> "Running" | _, _, Completed _ -> "Completed") - |> Seq.map ((<||) (sprintf "%s: %d")) - |> String.concat " " + |> Map + //|> Seq.map ((<||) (sprintf "%s: %d")) + //|> String.concat " " + let running = valueStats.TryFind "Running" |> Option.map (sprintf " Running: %d") |> Option.defaultValue "" - $"{name}{locked} {valueStats} ({cache.DebuggerDisplay})" + $"{name}{locked}{running} {cache.DebuggerDisplay} | hits: {hits} | started: {started} | completed: {completed} | canceled: {canceled} | restarted: {restarted} | failed: {failed} | evicted: {evicted} | collected: {collected}" diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 29365320445..fd09cbbb073 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -7,6 +7,7 @@ open Internal.Utilities.Collections open FSharp.Compiler.CodeAnalysis.TransparentCompiler open Internal.Utilities.Library.Extras open FSharp.Compiler.GraphChecking.GraphProcessing +open FSharp.Compiler.Diagnostics open Xunit @@ -548,6 +549,8 @@ let fuzzingTest seed (project: SyntheticProject) = task { let modify project = match getRandomModification() with | Update n -> + + use _ = Activity.start "Update" [||] let files = Set [ for _ in 1..n -> getRandomFile project |> snd ] (project, files) ||> Seq.fold (fun p file -> @@ -578,6 +581,8 @@ let fuzzingTest seed (project: SyntheticProject) = task { ct.CancelAfter(timeout) let job = Async.StartAsTask(checker |> checkFile file.Id p, cancellationToken = ct.Token) try + use _ = Activity.start "Check" [||] + let! parseResult, checkResult = job log.Value.Add (DateTime.Now.Ticks, FinishedChecking (match checkResult with FSharpCheckFileAnswer.Succeeded _ -> true | _ -> false), $"Loop #{n} {file.Id}") expectOk (parseResult, checkResult) () @@ -595,15 +600,14 @@ let fuzzingTest seed (project: SyntheticProject) = task { do! Task.Delay (rng.Next maxCheckingDelayMs) } - use tracerProvider = + use _tracerProvider = Sdk.CreateTracerProviderBuilder() .AddSource("fsc") - .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(serviceName="F#", serviceVersion = "1")) + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(serviceName="F# Fuzzing", serviceVersion = "1")) .AddJaegerExporter() .Build() - |> Some - - use _ = Activity.start $"Fuzzing {project.Name}" [ Activity.Tags.project, project.Name; "seed", seed.ToString() ] |> Some + + use _ = Activity.start $"Fuzzing {project.Name}" [ Activity.Tags.project, project.Name; "seed", seed.ToString() ] do! task { let threads = From d4ce1daa9c0d835cb3e6c30d43249ff9e76689f3 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 29 Sep 2023 12:43:33 +0200 Subject: [PATCH 115/222] wip --- src/Compiler/FSharp.Compiler.Service.fsproj | 2 +- src/Compiler/Facilities/AsyncMemoize.fs | 49 +-- src/Compiler/Facilities/prim-lexing.fs | 9 + src/Compiler/Facilities/prim-lexing.fsi | 2 + src/Compiler/Service/FSharpCheckerResults.fs | 4 + src/Compiler/Service/FSharpCheckerResults.fsi | 5 + src/Compiler/Service/TransparentCompiler.fs | 323 ++++++++++-------- src/Compiler/Utilities/Activity.fsi | 2 + .../CompilerService/AsyncMemoize.fs | 63 ++-- .../CompilerServiceBenchmarks/SourceText.fs | 2 + .../src/FSharp.Editor/Common/Extensions.fs | 2 + 11 files changed, 264 insertions(+), 199 deletions(-) diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index d7ad4c91942..dafa0c44841 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -143,6 +143,7 @@ + @@ -156,7 +157,6 @@ - --module FSharp.Compiler.AbstractIL.AsciiLexer --internal --open Internal.Utilities.Text.Lexing --open FSharp.Compiler.AbstractIL.AsciiParser --unicode --lexlib Internal.Utilities.Text.Lexing AbstractIL\illex.fsl diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 4d18d9541b6..010770936c0 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -317,10 +317,13 @@ module internal Md5Hasher = let private computeHash (bytes: byte array) = md5.Value.ComputeHash(bytes) + let hashString (s: string) = + System.Text.Encoding.UTF8.GetBytes(s) |> computeHash + let empty = String.Empty let addBytes (bytes: byte array) (s: string) = - let sbytes = System.Text.Encoding.UTF8.GetBytes(s) + let sbytes = s |> hashString Array.append sbytes bytes |> computeHash @@ -385,6 +388,9 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let mutable evicted = 0 let mutable collected = 0 + let failures = ResizeArray() + let durations = ResizeArray() + let cache = LruCache<'TKey, 'TVersion, Job<'TValue>>( keepStrongly = defaultArg keepStrongly 100, @@ -395,10 +401,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | _ -> false), event = (function - | CacheEvent.Evicted -> (fun k -> + | CacheEvent.Evicted -> (fun k -> Interlocked.Increment &evicted |> ignore event.Trigger(JobEvent.Evicted, k)) - | CacheEvent.Collected -> (fun k -> + | CacheEvent.Collected -> (fun k -> Interlocked.Increment &collected |> ignore event.Trigger(JobEvent.Collected, k)) | CacheEvent.Weakened -> (fun k -> event.Trigger(JobEvent.Weakened, k)) @@ -449,7 +455,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T return match msg, cached with | Sync, _ -> New Unchecked.defaultof<_> - | GetOrCompute _, Some (Completed result) -> + | GetOrCompute _, Some (Completed result) -> Interlocked.Increment &hits |> ignore Existing(Task.FromResult result) | GetOrCompute (_, ct), Some (Running (tcs, _, _, _)) -> @@ -483,7 +489,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T otherVersions |> Seq.choose (function v, Running (_tcs, cts, _, _) -> Some (v, cts) | _ -> None) - |> Seq.iter (fun (_v, cts) -> + |> Seq.iter (fun (_v, cts) -> use _ = Activity.start $"{name}: Duplicate running job" [| "key", key.Label |] //System.Diagnostics.Trace.TraceWarning($"{name} Duplicate {key.Label}") if cancelDuplicateRunningJobs then @@ -493,6 +499,11 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T New cts.Token }) + let internalError message = + let ex = exn(message) + failures.Add ex + raise ex + let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = task { do! Task.Delay 0 @@ -567,6 +578,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T requestCounts.Remove key |> ignore log (Failed, key) Interlocked.Increment &failed |> ignore + failures.Add ex tcs.TrySetException ex |> ignore | JobCompleted result, Some (Running (tcs, _cts, _c, _ts)) -> @@ -575,29 +587,26 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T requestCounts.Remove key |> ignore log (Finished, key) Interlocked.Increment &completed |> ignore + durations.Add (DateTime.Now - _ts) if tcs.TrySetResult result = false then - failwith "Invalid state: Completed job already completed" - () + internalError "Invalid state: Completed job already completed" // Job can't be evicted from cache while it's running because then subsequent requesters would be waiting forever - | JobFailed _, None -> failwith "Invalid state: Running job missing in cache (failed)" + | JobFailed _, None -> internalError "Invalid state: Running job missing in cache (failed)" - | OriginatorCanceled, None -> failwith "Invalid state: Running job missing in cache (canceled)" + | OriginatorCanceled, None -> internalError "Invalid state: Running job missing in cache (canceled)" - | JobCompleted _, None -> failwith "Invalid state: Running job missing in cache (completed)" + | JobCompleted _, None -> internalError "Invalid state: Running job missing in cache (completed)" | JobFailed ex, Some (Completed _job) -> - failwith $"Invalid state: Failed Completed job \n%A{ex}" - ignore ex + internalError $"Invalid state: Failed Completed job \n%A{ex}" | JobCompleted _result, Some (Completed _job) -> - failwith "Invalid state: Double-Completed job" - () + internalError "Invalid state: Double-Completed job" | OriginatorCanceled, Some (Completed _result) -> - failwith "Invalid state: Canceled Completed job" - () + internalError "Invalid state: Canceled Completed job" }) } @@ -629,7 +638,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T match! processRequest post (key, GetOrCompute(computation, ct)) |> Async.AwaitTask with | New internalCt -> - + let linkedCtSource = CancellationTokenSource.CreateLinkedTokenSource(ct, internalCt) try @@ -678,8 +687,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | _, _, Running _ -> "Running" | _, _, Completed _ -> "Completed") |> Map - //|> Seq.map ((<||) (sprintf "%s: %d")) - //|> String.concat " " + let running = valueStats.TryFind "Running" |> Option.map (sprintf " Running: %d") |> Option.defaultValue "" + let avgDuration = durations |> Seq.averageBy (fun x -> x.TotalMilliseconds) |> sprintf "%f ms" - $"{name}{locked}{running} {cache.DebuggerDisplay} | hits: {hits} | started: {started} | completed: {completed} | canceled: {canceled} | restarted: {restarted} | failed: {failed} | evicted: {evicted} | collected: {collected}" + $"{name}{locked}{running} {cache.DebuggerDisplay} | hits: {hits} | started: {started} | completed: {completed} | canceled: {canceled} | restarted: {restarted} | failed: {failed} | evicted: {evicted} | collected: {collected} | Avg: {avgDuration}" diff --git a/src/Compiler/Facilities/prim-lexing.fs b/src/Compiler/Facilities/prim-lexing.fs index 785b7fbdf6f..4601976838b 100644 --- a/src/Compiler/Facilities/prim-lexing.fs +++ b/src/Compiler/Facilities/prim-lexing.fs @@ -7,6 +7,8 @@ namespace FSharp.Compiler.Text open System open System.IO open FSharp.Compiler +open Internal.Utilities.Collections +open System.Collections.Immutable type ISourceText = @@ -28,6 +30,8 @@ type ISourceText = abstract CopyTo: sourceIndex: int * destination: char[] * destinationIndex: int * count: int -> unit + abstract GetChecksum: unit -> System.Collections.Immutable.ImmutableArray + [] type StringText(str: string) = @@ -108,6 +112,11 @@ type StringText(str: string) = member _.CopyTo(sourceIndex, destination, destinationIndex, count) = str.CopyTo(sourceIndex, destination, destinationIndex, count) + member _.GetChecksum() = + str + |> Md5Hasher.hashString + |> ImmutableArray.Create + module SourceText = let ofString str = StringText(str) :> ISourceText diff --git a/src/Compiler/Facilities/prim-lexing.fsi b/src/Compiler/Facilities/prim-lexing.fsi index a7c919991d3..d7dc0b80985 100644 --- a/src/Compiler/Facilities/prim-lexing.fsi +++ b/src/Compiler/Facilities/prim-lexing.fsi @@ -35,6 +35,8 @@ type ISourceText = /// Copies a section of the input to the given destination ad the given index abstract CopyTo: sourceIndex: int * destination: char[] * destinationIndex: int * count: int -> unit + abstract GetChecksum: unit -> System.Collections.Immutable.ImmutableArray + /// Functions related to ISourceText objects module SourceText = diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index d4ee510d342..f21979a1302 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -272,6 +272,10 @@ type FSharpProjectSnapshot = SourceFiles = this.SourceFiles |> List.map (fun x -> { x with Version = "" }) } + member this.WithoutReferences = { this with ReferencedProjects = []; ReferencesOnDisk = [] } + + member this.WithoutSourceFiles = { this with SourceFiles = [] } + override this.ToString() = Path.GetFileNameWithoutExtension this.ProjectFileName |> sprintf "FSharpProjectSnapshot(%s)" diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 1f9f8e6935d..88d112ba19d 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -145,6 +145,11 @@ type FSharpProjectSnapshot = /// A snapshot of the same project with file versions removed. member WithoutFileVersions: FSharpProjectSnapshot + /// A snapshot of the same project with ReferencedProjects and ReferencesOnDisk removed. + member WithoutReferences: FSharpProjectSnapshot + + member WithoutSourceFiles: FSharpProjectSnapshot + member GetLastModifiedTimeOnDisk: unit -> DateTime member internal Key: ICacheKey diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index d3bb07ba37b..4b90bd16fce 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -261,7 +261,7 @@ module private TypeCheckingGraphProcessing = type internal CompilerCaches() = - member val Source = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "Source") + // member val Source = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "Source") member val ParseFile = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "ParseFile") @@ -277,7 +277,7 @@ type internal CompilerCaches() = member val BootstrapInfo = AsyncMemoize(name = "BootstrapInfo") - member val TcLastFile = AsyncMemoize(name = "TcLastFile") + // member val TcLastFile = AsyncMemoize(name = "TcLastFile") member val TcIntermediate = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "TcIntermediate") @@ -520,102 +520,106 @@ type internal TransparentCompiler } ] - /// Bootstrap info that does not depend on contents of the files - let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) = - - caches.BootstrapInfoStatic.Get( - projectSnapshot.WithoutFileVersions.Key, - async { - use _ = - Activity.start - "ComputeBootstrapInfoStatic" - [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName - "references", projectSnapshot.ReferencedProjects.Length.ToString() - |] - - let useSimpleResolutionSwitch = "--simpleresolution" - let commandLineArgs = projectSnapshot.CommandLineOptions - let defaultFSharpBinariesDir = FSharpCheckerResultsSettings.defaultFSharpBinariesDir - let useScriptResolutionRules = projectSnapshot.UseScriptResolutionRules + let ComputeTcConfigBuilder (projectSnapshot: FSharpProjectSnapshot) = + + let useSimpleResolutionSwitch = "--simpleresolution" + let commandLineArgs = projectSnapshot.CommandLineOptions + let defaultFSharpBinariesDir = FSharpCheckerResultsSettings.defaultFSharpBinariesDir + let useScriptResolutionRules = projectSnapshot.UseScriptResolutionRules + + let projectReferences = getProjectReferences projectSnapshot "ComputeBootstrapInfo" + + // TODO: script support + let loadClosureOpt: LoadClosure option = None + + let getSwitchValue (switchString: string) = + match commandLineArgs |> List.tryFindIndex (fun s -> s.StartsWithOrdinal switchString) with + | Some idx -> Some(commandLineArgs[ idx ].Substring(switchString.Length)) + | _ -> None + + let sdkDirOverride = + match loadClosureOpt with + | None -> None + | Some loadClosure -> loadClosure.SdkDirOverride + + // see also fsc.fs: runFromCommandLineToImportingAssemblies(), as there are many similarities to where the PS creates a tcConfigB + let tcConfigB = + TcConfigBuilder.CreateNew( + legacyReferenceResolver, + defaultFSharpBinariesDir, + implicitIncludeDir = projectSnapshot.ProjectDirectory, + reduceMemoryUsage = ReduceMemoryFlag.Yes, + isInteractive = useScriptResolutionRules, + isInvalidationSupported = true, + defaultCopyFSharpCore = CopyFSharpCoreFlag.No, + tryGetMetadataSnapshot = tryGetMetadataSnapshot, + sdkDirOverride = sdkDirOverride, + rangeForErrors = range0 + ) - let projectReferences = getProjectReferences projectSnapshot "ComputeBootstrapInfo" + tcConfigB.primaryAssembly <- + match loadClosureOpt with + | None -> PrimaryAssembly.Mscorlib + | Some loadClosure -> + if loadClosure.UseDesktopFramework then + PrimaryAssembly.Mscorlib + else + PrimaryAssembly.System_Runtime - // TODO: script support - let loadClosureOpt: LoadClosure option = None + tcConfigB.resolutionEnvironment <- (LegacyResolutionEnvironment.EditingOrCompilation true) - let tcConfigB, sourceFiles = + tcConfigB.conditionalDefines <- + let define = + if useScriptResolutionRules then + "INTERACTIVE" + else + "COMPILED" - let getSwitchValue (switchString: string) = - match commandLineArgs |> List.tryFindIndex (fun s -> s.StartsWithOrdinal switchString) with - | Some idx -> Some(commandLineArgs[ idx ].Substring(switchString.Length)) - | _ -> None + define :: tcConfigB.conditionalDefines - let sdkDirOverride = - match loadClosureOpt with - | None -> None - | Some loadClosure -> loadClosure.SdkDirOverride - - // see also fsc.fs: runFromCommandLineToImportingAssemblies(), as there are many similarities to where the PS creates a tcConfigB - let tcConfigB = - TcConfigBuilder.CreateNew( - legacyReferenceResolver, - defaultFSharpBinariesDir, - implicitIncludeDir = projectSnapshot.ProjectDirectory, - reduceMemoryUsage = ReduceMemoryFlag.Yes, - isInteractive = useScriptResolutionRules, - isInvalidationSupported = true, - defaultCopyFSharpCore = CopyFSharpCoreFlag.No, - tryGetMetadataSnapshot = tryGetMetadataSnapshot, - sdkDirOverride = sdkDirOverride, - rangeForErrors = range0 - ) + tcConfigB.projectReferences <- projectReferences - tcConfigB.primaryAssembly <- - match loadClosureOpt with - | None -> PrimaryAssembly.Mscorlib - | Some loadClosure -> - if loadClosure.UseDesktopFramework then - PrimaryAssembly.Mscorlib - else - PrimaryAssembly.System_Runtime + tcConfigB.useSimpleResolution <- (getSwitchValue useSimpleResolutionSwitch) |> Option.isSome - tcConfigB.resolutionEnvironment <- (LegacyResolutionEnvironment.EditingOrCompilation true) + // Apply command-line arguments and collect more source files if they are in the arguments + let sourceFilesNew = + ApplyCommandLineArgs(tcConfigB, projectSnapshot.SourceFileNames, commandLineArgs) - tcConfigB.conditionalDefines <- - let define = - if useScriptResolutionRules then - "INTERACTIVE" - else - "COMPILED" + // Never open PDB files for the language service, even if --standalone is specified + tcConfigB.openDebugInformationForLaterStaticLinking <- false - define :: tcConfigB.conditionalDefines + tcConfigB.xmlDocInfoLoader <- + { new IXmlDocumentationInfoLoader with + /// Try to load xml documentation associated with an assembly by the same file path with the extension ".xml". + member _.TryLoad(assemblyFileName) = + let xmlFileName = Path.ChangeExtension(assemblyFileName, ".xml") - tcConfigB.projectReferences <- projectReferences + // REVIEW: File IO - Will eventually need to change this to use a file system interface of some sort. + XmlDocumentationInfo.TryCreateFromFile(xmlFileName) + } + |> Some - tcConfigB.useSimpleResolution <- (getSwitchValue useSimpleResolutionSwitch) |> Option.isSome + tcConfigB.parallelReferenceResolution <- parallelReferenceResolution + tcConfigB.captureIdentifiersWhenParsing <- captureIdentifiersWhenParsing - // Apply command-line arguments and collect more source files if they are in the arguments - let sourceFilesNew = - ApplyCommandLineArgs(tcConfigB, projectSnapshot.SourceFileNames, commandLineArgs) + tcConfigB, sourceFilesNew, loadClosureOpt - // Never open PDB files for the language service, even if --standalone is specified - tcConfigB.openDebugInformationForLaterStaticLinking <- false - tcConfigB.xmlDocInfoLoader <- - { new IXmlDocumentationInfoLoader with - /// Try to load xml documentation associated with an assembly by the same file path with the extension ".xml". - member _.TryLoad(assemblyFileName) = - let xmlFileName = Path.ChangeExtension(assemblyFileName, ".xml") - // REVIEW: File IO - Will eventually need to change this to use a file system interface of some sort. - XmlDocumentationInfo.TryCreateFromFile(xmlFileName) - } - |> Some + /// Bootstrap info that does not depend on contents of the files + let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) = - tcConfigB.parallelReferenceResolution <- parallelReferenceResolution - tcConfigB.captureIdentifiersWhenParsing <- captureIdentifiersWhenParsing + caches.BootstrapInfoStatic.Get( + projectSnapshot.WithoutFileVersions.Key, + async { + use _ = + Activity.start + "ComputeBootstrapInfoStatic" + [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName + "references", projectSnapshot.ReferencedProjects.Length.ToString() + |] - tcConfigB, sourceFilesNew + let tcConfigB, sourceFiles, loadClosureOpt = ComputeTcConfigBuilder projectSnapshot // If this is a builder for a script, re-apply the settings inferred from the // script and its load closure to the configuration. @@ -770,7 +774,7 @@ type internal TransparentCompiler }) { - Range = m + Range = m // TODO: is this used for anything? Source = source IsLastCompiland = isLastCompiland IsExe = isExe @@ -791,9 +795,7 @@ type internal TransparentCompiler } } - let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) withoutReferences = - - let projectSnapshot = if withoutReferences then { projectSnapshot with ReferencedProjects = [] } else projectSnapshot + let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) = caches.BootstrapInfo.Get( projectSnapshot.Key, @@ -839,28 +841,26 @@ type internal TransparentCompiler // TODO: Not sure if we should cache this. For VS probably not. Maybe it can be configurable by FCS user. let LoadSource (file: FSharpFile) = - caches.Source.Get( - file.Source.Key, - async { - let! source = file.Source.GetSource() |> Async.AwaitTask + async { + let! source = file.Source.GetSource() |> Async.AwaitTask - return - { - FileName = file.Source.FileName - Source = source - SourceHash = Md5Hasher.empty |> Md5Hasher.addString <| source.ToString() - IsLastCompiland = file.IsLastCompiland - IsExe = file.IsExe - } - } - ) + return + { + FileName = file.Source.FileName + Source = source + SourceHash = Md5Hasher.empty |> Md5Hasher.addBytes (source.GetChecksum().ToBuilder().ToArray()) + IsLastCompiland = file.IsLastCompiland + IsExe = file.IsExe + } + } let LoadSources (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = async { - // TODO: deuglify/optimize + let sourceFileMap = bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName, f) |> Map + let! sources = projectSnapshot.SourceFiles - |> Seq.map (fun f -> f.FileName |> bootstrapInfo.GetFile |> LoadSource) + |> Seq.map (fun f -> f.FileName |> sourceFileMap.TryFind |> Option.defaultWith (fun _ -> failwith $"File {f.FileName} not found in {projectSnapshot.Key.GetLabel()}") |> LoadSource) |> Async.Parallel return @@ -870,15 +870,17 @@ type internal TransparentCompiler } } - let ComputeParseFile (tcConfig: TcConfig) (file: FSharpFileSnapshotWithSource) = + let ComputeParseFile (projectSnapshot: FSharpProjectSnapshot) (file: FSharpFileSnapshotWithSource) = + + let projectKey = projectSnapshot.WithoutSourceFiles.WithoutReferences.Key let key = { new ICacheKey<_, _> with member _.GetLabel() = file.FileName |> shortPath - member _.GetKey() = file.FileName + member _.GetKey() = projectKey.GetKey(), file.FileName member _.GetVersion() = - file.SourceHash, file.IsLastCompiland, file.IsExe + projectKey.GetVersion(), file.SourceHash, file.IsLastCompiland, file.IsExe } caches.ParseFile.Get( @@ -892,6 +894,12 @@ type internal TransparentCompiler Activity.Tags.version, file.SourceHash |] + // TODO: might need to deal with exceptions here: + let tcConfigB, _, _ = ComputeTcConfigBuilder projectSnapshot + + let tcConfig = TcConfig.Create(tcConfigB, validate = true) + + let diagnosticsLogger = CompilationDiagnosticLogger("Parse", tcConfig.diagnosticsOptions) // Return the disposable object that cleans up @@ -904,7 +912,7 @@ type internal TransparentCompiler let input = ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) - return input, diagnosticsLogger.GetDiagnostics(), sourceText + return input, diagnosticsLogger.GetDiagnostics(), sourceText, tcConfig } ) @@ -1138,7 +1146,7 @@ type internal TransparentCompiler match fileNode with | NodeToTypeCheck.PhysicalFile index -> - let input, parseErrors, _ = parsedInputs[index] + let input, parseErrors, _, _ = parsedInputs[index] let! tcIntermediate = ComputeTcIntermediate projectSnapshot dependencyFiles fileNode (input, parseErrors) bootstrapInfo tcInfo @@ -1203,7 +1211,7 @@ type internal TransparentCompiler then failwith $"Oops!!!" - let parsedInput, _parseErrors, _ = parsedInputs[index] + let parsedInput, _parseErrors, _, _ = parsedInputs[index] let prefixPathOpt = None // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. let partialResult, tcState = @@ -1239,39 +1247,39 @@ type internal TransparentCompiler // Type check file and all its dependencies let ComputeTcLastFile (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = - let fileName = projectSnapshot.SourceFiles |> List.last |> (fun f -> f.FileName) - caches.TcLastFile.Get( - projectSnapshot.FileKey fileName, - async { - let file = projectSnapshot.SourceFiles |> List.last + //let fileName = projectSnapshot.SourceFiles |> List.last |> (fun f -> f.FileName) + //caches.TcLastFile.Get( + // projectSnapshot.FileKey fileName, + async { + let file = projectSnapshot.SourceFiles |> List.last - use _ = - Activity.start "ComputeTcLastFile" [| Activity.Tags.fileName, file.FileName |> Path.GetFileName |] + use _ = + Activity.start "ComputeTcLastFile" [| Activity.Tags.fileName, file.FileName |> Path.GetFileName |] - let! parsedInputs = - projectSnapshot.SourceFiles - |> Seq.map (ComputeParseFile bootstrapInfo.TcConfig) - |> Async.Parallel + let! parsedInputs = + projectSnapshot.SourceFiles + |> Seq.map (ComputeParseFile projectSnapshot.ProjectSnapshot) + |> Async.Parallel - let! graph, dependencyFiles = - ComputeDependencyGraphForFile bootstrapInfo.TcConfig projectSnapshot (parsedInputs |> Array.map p13) - //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) + let! graph, dependencyFiles = + ComputeDependencyGraphForFile bootstrapInfo.TcConfig projectSnapshot (parsedInputs |> Array.map p14) + //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) - let! results, tcInfo = - processTypeCheckingGraph - graph - (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles false) - bootstrapInfo.InitialTcInfo + let! results, tcInfo = + processTypeCheckingGraph + graph + (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles false) + bootstrapInfo.InitialTcInfo - let lastResult = results |> List.head |> snd + let lastResult = results |> List.head |> snd - return lastResult, tcInfo - } - ) + return lastResult, tcInfo + } + //) - let getParseResult (tcConfig: TcConfig) creationDiags file = + let getParseResult (projectSnapshot: FSharpProjectSnapshot) creationDiags file = async { - let! parseTree, parseDiagnostics, sourceText = ComputeParseFile tcConfig file + let! parseTree, parseDiagnostics, sourceText, tcConfig = ComputeParseFile projectSnapshot file let parseDiagnostics = DiagnosticHelpers.CreateDiagnostics( @@ -1309,7 +1317,7 @@ type internal TransparentCompiler use _ = Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - match! ComputeBootstrapInfo projectSnapshot false with + match! ComputeBootstrapInfo projectSnapshot with | None, creationDiags -> return emptyParseResult fileName creationDiags, FSharpCheckFileAnswer.Aborted | Some bootstrapInfo, creationDiags -> @@ -1318,7 +1326,7 @@ type internal TransparentCompiler let! snapshotWithSources = LoadSources bootstrapInfo priorSnapshot let file = snapshotWithSources.SourceFiles |> List.last - let! parseResults, _sourceText = getParseResult bootstrapInfo.TcConfig creationDiags file + let! parseResults, _sourceText = getParseResult projectSnapshot creationDiags file let! result, tcInfo = ComputeTcLastFile bootstrapInfo snapshotWithSources @@ -1402,11 +1410,11 @@ type internal TransparentCompiler let! parsedInputs = projectSnapshot.SourceFiles - |> Seq.map (ComputeParseFile bootstrapInfo.TcConfig) + |> Seq.map (ComputeParseFile projectSnapshot.ProjectSnapshot) |> Async.Parallel let! graph, dependencyFiles = - ComputeDependencyGraphForProject bootstrapInfo.TcConfig projectSnapshot (parsedInputs |> Array.map p13) + ComputeDependencyGraphForProject bootstrapInfo.TcConfig projectSnapshot (parsedInputs |> Array.map p14) return! processTypeCheckingGraph @@ -1416,13 +1424,12 @@ type internal TransparentCompiler } ) - let ComputeProjectExtras (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = + let ComputeProjectExtras (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = caches.ProjectExtras.Get( projectSnapshot.WithoutImplFilesThatHaveSignatures.Key, async { - let! snapshotWithSources = projectSnapshot |> LoadSources bootstrapInfo - let! results, finalInfo = ComputeParseAndCheckAllFilesInProject bootstrapInfo snapshotWithSources + let! results, finalInfo = ComputeParseAndCheckAllFilesInProject bootstrapInfo projectSnapshot let assemblyName = bootstrapInfo.AssemblyName let tcConfig = bootstrapInfo.TcConfig @@ -1531,12 +1538,15 @@ type internal TransparentCompiler Trace.TraceInformation($"Using assembly on disk: {name}") return ProjectAssemblyDataResult.Unavailable true else - match! ComputeBootstrapInfo projectSnapshot false with + match! ComputeBootstrapInfo projectSnapshot with | None, _ -> Trace.TraceInformation($"Using assembly on disk (unintentionally): {name}") return ProjectAssemblyDataResult.Unavailable true | Some bootstrapInfo, _creationDiags -> - let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot + + let! snapshotWithSources = LoadSources bootstrapInfo projectSnapshot + + let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo snapshotWithSources Trace.TraceInformation($"Using in-memory project reference: {name}") return assemblyDataResult @@ -1548,12 +1558,14 @@ type internal TransparentCompiler projectSnapshot.Key, async { - match! ComputeBootstrapInfo projectSnapshot false with + match! ComputeBootstrapInfo projectSnapshot with | None, creationDiags -> return FSharpCheckProjectResults(projectSnapshot.ProjectFileName, None, keepAssemblyContents, creationDiags, None) | Some bootstrapInfo, creationDiags -> - let! tcInfo, ilAssemRef, assemblyDataResult = ComputeProjectExtras bootstrapInfo projectSnapshot + let! snapshotWithSources = LoadSources bootstrapInfo projectSnapshot + + let! tcInfo, ilAssemRef, assemblyDataResult = ComputeProjectExtras bootstrapInfo snapshotWithSources let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions let fileName = DummyFileNameForRangesWithoutASpecificLocation @@ -1613,7 +1625,7 @@ type internal TransparentCompiler let tryGetSink (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = async { - match! ComputeBootstrapInfo projectSnapshot false with + match! ComputeBootstrapInfo projectSnapshot with | None, _ -> return None | Some bootstrapInfo, _creationDiags -> @@ -1696,12 +1708,23 @@ type internal TransparentCompiler //use _ = // Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] - match! ComputeBootstrapInfo projectSnapshot true with - | None, creationDiags -> return emptyParseResult fileName creationDiags - | Some bootstrapInfo, creationDiags -> - let! file = bootstrapInfo.GetFile fileName |> LoadSource - let! parseResult, _ = getParseResult bootstrapInfo.TcConfig creationDiags file - return parseResult + // TODO: might need to deal with exceptions here: + let tcConfigB, _, _ = ComputeTcConfigBuilder projectSnapshot + + let tcConfig = TcConfig.Create(tcConfigB, validate = true) + + let index, fileSnapshot = projectSnapshot.SourceFiles |> Seq.mapi pair |> Seq.tryFind (fun (_, f) -> f.FileName = fileName) |> Option.defaultWith (fun () -> failwith $"File not found: {fileName}") + + let file = { + Range = rangeStartup + Source = fileSnapshot + IsLastCompiland = index = projectSnapshot.SourceFiles.Length - 1 + IsExe = tcConfig.target.IsExe + } + + let! file = file |> LoadSource + let! parseResult, _ = getParseResult projectSnapshot Seq.empty file + return parseResult } member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index b05f0c4ba54..39551c271e8 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -1,5 +1,7 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + + namespace FSharp.Compiler.Diagnostics open System diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 178b9041798..627edadd5e4 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -333,38 +333,45 @@ let ``Stress test`` () = Assert.True ((float completed) > ((float started) * 0.1), "Less than 10 % completed jobs") -[] -let ``Cancel running jobs with the same key`` () = - - let cache = AsyncMemoize(cancelDuplicateRunningJobs=false) +[] +[] +[] +let ``Cancel running jobs with the same key`` cancelDuplicate expectCancelled = + task { + let cache = AsyncMemoize(cancelDuplicateRunningJobs=cancelDuplicate) + + let mutable started = 0 + let mutable cancelled = 0 + let mutable finished = 0 + + let work () = async { + Interlocked.Increment &started |> ignore + let! ct = Async.CancellationToken + use _ = ct.Register(fun () -> Interlocked.Increment &cancelled |> ignore) + for _ in 1..10 do + do! Async.Sleep 30 + Interlocked.Increment &finished |> ignore + } - let mutable started = 0 - let mutable cancelled = 0 - - let work () = async { - Interlocked.Increment &started |> ignore - let! ct = Async.CancellationToken - use _ = ct.Register(fun () -> Interlocked.Increment &cancelled |> ignore) - for _ in 1..10 do - do! Async.Sleep 30 - } + let key1 = + { new ICacheKey<_, _> with + member _.GetKey() = 1 + member _.GetVersion() = 1 + member _.GetLabel() = "key1" } - let key1 = - { new ICacheKey<_, _> with - member _.GetKey() = 1 - member _.GetVersion() = 1 - member _.GetLabel() = "key1" } + cache.Get(key1, work()) |> Async.Start - cache.Get(key1, work()) |> Async.Start + do! Task.Delay 100 - let key2 = - { new ICacheKey<_, _> with - member _.GetKey() = key1.GetKey() - member _.GetVersion() = key1.GetVersion() + 1 - member _.GetLabel() = "key2" } + let key2 = + { new ICacheKey<_, _> with + member _.GetKey() = key1.GetKey() + member _.GetVersion() = key1.GetVersion() + 1 + member _.GetLabel() = "key2" } - cache.Get(key2, work()) |> Async.Start + cache.Get(key2, work()) |> Async.Start - Async.Sleep 100 |> Async.RunSynchronously + do! Task.Delay 100 - Assert.Equal((2, 1), (started, cancelled)) \ No newline at end of file + Assert.Equal((2, expectCancelled, 0), (started, cancelled, finished)) + } \ No newline at end of file diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs index 49d46568ff4..7682e26f2f1 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs @@ -64,6 +64,8 @@ module internal SourceText = member __.CopyTo(sourceIndex, destination, destinationIndex, count) = sourceText.CopyTo(sourceIndex, destination, destinationIndex, count) + + member _.GetChecksum() = sourceText.GetChecksum() } sourceText diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index ab1c5bdeaba..747e6aacdcd 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -165,6 +165,8 @@ module private SourceText = member _.CopyTo(sourceIndex, destination, destinationIndex, count) = sourceText.CopyTo(sourceIndex, destination, destinationIndex, count) + + member _.GetChecksum() = sourceText.GetChecksum() } sourceText From 6758b9a89f5f3f42efd589bc307ee380ae11e27f Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 29 Sep 2023 14:56:24 +0200 Subject: [PATCH 116/222] wip --- src/Compiler/Facilities/AsyncMemoize.fs | 100 +++++++++++------- src/Compiler/Facilities/prim-lexing.fs | 4 +- src/Compiler/Service/FSharpCheckerResults.fs | 18 ++-- src/Compiler/Service/FSharpCheckerResults.fsi | 2 +- src/Compiler/Service/TransparentCompiler.fs | 45 +++++--- src/Compiler/Utilities/Activity.fsi | 2 - .../FSharpChecker/TransparentCompiler.fs | 4 +- 7 files changed, 107 insertions(+), 68 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 010770936c0..73945632ccb 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -43,10 +43,16 @@ type MemoizeRequest<'TValue> = type internal Job<'TValue> = | Running of TaskCompletionSource<'TValue> * CancellationTokenSource * Async<'TValue> * DateTime | Completed of 'TValue + member this.DebuggerDisplay = match this with | Running (_, cts, _, ts) -> - let cancellation = if cts.IsCancellationRequested then " ! Cancellation Requested" else "" + let cancellation = + if cts.IsCancellationRequested then + " ! Cancellation Requested" + else + "" + $"Running since {ts.ToShortTimeString()}{cancellation}" | Completed value -> $"Completed {value}" @@ -250,10 +256,10 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers |> Seq.map (fun node -> node.Value) |> Seq.filter (p24 >> ((<>) version)) |> Seq.choose (function - | _, ver, _, Strong v -> Some (ver, v) + | _, ver, _, Strong v -> Some(ver, v) | _, ver, _, Weak r -> match r.TryGetTarget() with - | true, x -> Some (ver, x) + | true, x -> Some(ver, x) | _ -> None) |> Seq.toList @@ -379,6 +385,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let event = Event<_>() + let mutable errors = 0 let mutable hits = 0 let mutable started = 0 let mutable completed = 0 @@ -401,12 +408,14 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | _ -> false), event = (function - | CacheEvent.Evicted -> (fun k -> - Interlocked.Increment &evicted |> ignore - event.Trigger(JobEvent.Evicted, k)) - | CacheEvent.Collected -> (fun k -> - Interlocked.Increment &collected |> ignore - event.Trigger(JobEvent.Collected, k)) + | CacheEvent.Evicted -> + (fun k -> + Interlocked.Increment &evicted |> ignore + event.Trigger(JobEvent.Evicted, k)) + | CacheEvent.Collected -> + (fun k -> + Interlocked.Increment &collected |> ignore + event.Trigger(JobEvent.Collected, k)) | CacheEvent.Weakened -> (fun k -> event.Trigger(JobEvent.Weakened, k)) | CacheEvent.Strengthened -> (fun k -> event.Trigger(JobEvent.Strengthened, k))) ) @@ -480,15 +489,12 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let cts = new CancellationTokenSource() - cache.Set( - key.Key, - key.Version, - key.Label, - (Running(TaskCompletionSource(), cts, computation, DateTime.Now)) - ) + cache.Set(key.Key, key.Version, key.Label, (Running(TaskCompletionSource(), cts, computation, DateTime.Now))) otherVersions - |> Seq.choose (function v, Running (_tcs, cts, _, _) -> Some (v, cts) | _ -> None) + |> Seq.choose (function + | v, Running (_tcs, cts, _, _) -> Some(v, cts) + | _ -> None) |> Seq.iter (fun (_v, cts) -> use _ = Activity.start $"{name}: Duplicate running job" [| "key", key.Label |] //System.Diagnostics.Trace.TraceWarning($"{name} Duplicate {key.Label}") @@ -499,9 +505,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T New cts.Token }) - let internalError message = - let ex = exn(message) - failures.Add ex + let internalError key message = + let ex = exn (message) + failures.Add(key, ex) + Interlocked.Increment &errors |> ignore raise ex let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = @@ -530,7 +537,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T Interlocked.Increment &canceled |> ignore use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] () - //System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" + //System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" else // We need to restart the computation @@ -567,7 +574,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T Interlocked.Increment &canceled |> ignore use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] () - //System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" + //System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" | CancelRequest, None | CancelRequest, Some (Completed _) -> () @@ -578,7 +585,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T requestCounts.Remove key |> ignore log (Failed, key) Interlocked.Increment &failed |> ignore - failures.Add ex + failures.Add(key.Label, ex) tcs.TrySetException ex |> ignore | JobCompleted result, Some (Running (tcs, _cts, _c, _ts)) -> @@ -587,26 +594,23 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T requestCounts.Remove key |> ignore log (Finished, key) Interlocked.Increment &completed |> ignore - durations.Add (DateTime.Now - _ts) + durations.Add(DateTime.Now - _ts) if tcs.TrySetResult result = false then - internalError "Invalid state: Completed job already completed" + internalError key.Label "Invalid state: Completed job already completed" // Job can't be evicted from cache while it's running because then subsequent requesters would be waiting forever - | JobFailed _, None -> internalError "Invalid state: Running job missing in cache (failed)" + | JobFailed _, None -> internalError key.Label "Invalid state: Running job missing in cache (failed)" - | OriginatorCanceled, None -> internalError "Invalid state: Running job missing in cache (canceled)" + | OriginatorCanceled, None -> internalError key.Label "Invalid state: Running job missing in cache (canceled)" - | JobCompleted _, None -> internalError "Invalid state: Running job missing in cache (completed)" + | JobCompleted _, None -> internalError key.Label "Invalid state: Running job missing in cache (completed)" - | JobFailed ex, Some (Completed _job) -> - internalError $"Invalid state: Failed Completed job \n%A{ex}" + | JobFailed ex, Some (Completed _job) -> internalError key.Label $"Invalid state: Failed Completed job \n%A{ex}" - | JobCompleted _result, Some (Completed _job) -> - internalError "Invalid state: Double-Completed job" + | JobCompleted _result, Some (Completed _job) -> internalError key.Label "Invalid state: Double-Completed job" - | OriginatorCanceled, Some (Completed _result) -> - internalError "Invalid state: Canceled Completed job" + | OriginatorCanceled, Some (Completed _result) -> internalError key.Label "Invalid state: Canceled Completed job" }) } @@ -688,7 +692,31 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | _, _, Completed _ -> "Completed") |> Map - let running = valueStats.TryFind "Running" |> Option.map (sprintf " Running: %d") |> Option.defaultValue "" - let avgDuration = durations |> Seq.averageBy (fun x -> x.TotalMilliseconds) |> sprintf "%f ms" + let running = + valueStats.TryFind "Running" + |> Option.map (sprintf " Running: %d") + |> Option.defaultValue "" - $"{name}{locked}{running} {cache.DebuggerDisplay} | hits: {hits} | started: {started} | completed: {completed} | canceled: {canceled} | restarted: {restarted} | failed: {failed} | evicted: {evicted} | collected: {collected} | Avg: {avgDuration}" + let avgDuration = + match durations.Count with + | 0 -> "" + | _ -> + durations + |> Seq.averageBy (fun x -> x.TotalMilliseconds) + |> sprintf "| Avg: %.0f ms" + + let stats = + [| + if errors > 0 then $"| errors: {errors} " else "" + if hits > 0 then $"| hits: {hits} " else "" + if started > 0 then $"| started: {started} " else "" + if completed > 0 then $"| completed: {completed} " else "" + if canceled > 0 then $"| canceled: {canceled} " else "" + if restarted > 0 then $"| restarted: {restarted} " else "" + if failed > 0 then $"| failed: {failed} " else "" + if evicted > 0 then $"| evicted: {evicted} " else "" + if collected > 0 then $"| collected: {collected} " else "" + |] + |> String.concat "" + + $"{name}{locked}{running} {cache.DebuggerDisplay} {stats}{avgDuration}" diff --git a/src/Compiler/Facilities/prim-lexing.fs b/src/Compiler/Facilities/prim-lexing.fs index 4601976838b..f3eea8f59a3 100644 --- a/src/Compiler/Facilities/prim-lexing.fs +++ b/src/Compiler/Facilities/prim-lexing.fs @@ -113,9 +113,7 @@ type StringText(str: string) = str.CopyTo(sourceIndex, destination, destinationIndex, count) member _.GetChecksum() = - str - |> Md5Hasher.hashString - |> ImmutableArray.Create + str |> Md5Hasher.hashString |> ImmutableArray.Create module SourceText = diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index f21979a1302..54d9f874e9d 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -150,7 +150,7 @@ type FSharpFileSnapshotWithSource = type ReferenceOnDisk = { Path: string; LastModified: DateTime } -type ProjectSnapshotKey = string * bool +type ProjectSnapshotKey = string [] type FSharpProjectSnapshot = @@ -272,7 +272,11 @@ type FSharpProjectSnapshot = SourceFiles = this.SourceFiles |> List.map (fun x -> { x with Version = "" }) } - member this.WithoutReferences = { this with ReferencedProjects = []; ReferencesOnDisk = [] } + member this.WithoutReferences = + { this with + ReferencedProjects = [] + ReferencesOnDisk = [] + } member this.WithoutSourceFiles = { this with SourceFiles = [] } @@ -285,7 +289,7 @@ type FSharpProjectSnapshot = member this.FileKey(fileName) = { new ICacheKey<_, _> with member _.GetLabel() = fileName |> shortPath - member _.GetKey() = fileName, this.Key.GetKey() |> fst + member _.GetKey() = fileName, this.Key.GetKey() member _.GetVersion() = this @@ -323,9 +327,9 @@ type FSharpProjectSnapshot = UseScriptResolutionRules = this.UseScriptResolutionRules } - interface ICacheKey<(string * bool), FSharpProjectSnapshotDebugVersion> with + interface ICacheKey with member this.GetLabel() = this.ToString() - member this.GetKey() = this.ProjectFileName, this.ReferencedProjects = [] // This is for bootstrapInfo cache where we might need to keep an extra version without project references to speed up getting TcConfig for just parsing files. Probably this should be reworked eventually... + member this.GetKey() = this.ProjectFileName member this.GetVersion() = this.GetDebugVersion() and FSharpProjectSnapshotWithSources = @@ -387,7 +391,7 @@ and FSharpProjectSnapshotWithSources = member this.FileKey(fileName) = { new ICacheKey<_, _> with member _.GetLabel() = fileName |> shortPath - member _.GetKey() = fileName, this.Key.GetKey() |> fst + member _.GetKey() = fileName, this.Key.GetKey() member _.GetVersion() = this @@ -395,7 +399,7 @@ and FSharpProjectSnapshotWithSources = .WithoutImplFilesThatHaveSignaturesExceptLastOne.Key.GetVersion() } - interface ICacheKey<(string * bool), FSharpProjectSnapshotWithSourcesVersion> with + interface ICacheKey with member this.GetLabel() = this.ProjectSnapshot.Key.ToString() member this.GetKey() = this.ProjectSnapshot.Key.GetKey() diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 88d112ba19d..c3440c44c75 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -65,7 +65,7 @@ type FSharpFileSnapshotWithSource = type ReferenceOnDisk = { Path: string; LastModified: DateTime } -type ProjectSnapshotKey = string * bool +type ProjectSnapshotKey = string [] type FSharpProjectSnapshot = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 4b90bd16fce..652fd128b81 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -604,8 +604,6 @@ type internal TransparentCompiler tcConfigB, sourceFilesNew, loadClosureOpt - - /// Bootstrap info that does not depend on contents of the files let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) = @@ -615,8 +613,9 @@ type internal TransparentCompiler use _ = Activity.start "ComputeBootstrapInfoStatic" - [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName - "references", projectSnapshot.ReferencedProjects.Length.ToString() + [| + Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName + "references", projectSnapshot.ReferencedProjects.Length.ToString() |] let tcConfigB, sourceFiles, loadClosureOpt = ComputeTcConfigBuilder projectSnapshot @@ -848,7 +847,9 @@ type internal TransparentCompiler { FileName = file.Source.FileName Source = source - SourceHash = Md5Hasher.empty |> Md5Hasher.addBytes (source.GetChecksum().ToBuilder().ToArray()) + SourceHash = + Md5Hasher.empty + |> Md5Hasher.addBytes (source.GetChecksum().ToBuilder().ToArray()) IsLastCompiland = file.IsLastCompiland IsExe = file.IsExe } @@ -856,11 +857,16 @@ type internal TransparentCompiler let LoadSources (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = async { - let sourceFileMap = bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName, f) |> Map + let sourceFileMap = + bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName, f) |> Map let! sources = projectSnapshot.SourceFiles - |> Seq.map (fun f -> f.FileName |> sourceFileMap.TryFind |> Option.defaultWith (fun _ -> failwith $"File {f.FileName} not found in {projectSnapshot.Key.GetLabel()}") |> LoadSource) + |> Seq.map (fun f -> + f.FileName + |> sourceFileMap.TryFind + |> Option.defaultWith (fun _ -> failwith $"File {f.FileName} not found in {projectSnapshot.Key.GetLabel()}") + |> LoadSource) |> Async.Parallel return @@ -899,7 +905,6 @@ type internal TransparentCompiler let tcConfig = TcConfig.Create(tcConfigB, validate = true) - let diagnosticsLogger = CompilationDiagnosticLogger("Parse", tcConfig.diagnosticsOptions) // Return the disposable object that cleans up @@ -1098,6 +1103,7 @@ type internal TransparentCompiler DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input let! ct = Async.CancellationToken + try do! maxParallelismSemaphore.WaitAsync(ct) |> Async.AwaitTask @@ -1275,7 +1281,7 @@ type internal TransparentCompiler return lastResult, tcInfo } - //) + //) let getParseResult (projectSnapshot: FSharpProjectSnapshot) creationDiags file = async { @@ -1677,7 +1683,7 @@ type internal TransparentCompiler return sinkOpt - |> Option.bind (fun (sink, {TcGlobals = g}) -> + |> Option.bind (fun (sink, { TcGlobals = g }) -> let sResolutions = sink.GetResolutions() let builder = ItemKeyStoreBuilder(g) @@ -1713,14 +1719,19 @@ type internal TransparentCompiler let tcConfig = TcConfig.Create(tcConfigB, validate = true) - let index, fileSnapshot = projectSnapshot.SourceFiles |> Seq.mapi pair |> Seq.tryFind (fun (_, f) -> f.FileName = fileName) |> Option.defaultWith (fun () -> failwith $"File not found: {fileName}") + let index, fileSnapshot = + projectSnapshot.SourceFiles + |> Seq.mapi pair + |> Seq.tryFind (fun (_, f) -> f.FileName = fileName) + |> Option.defaultWith (fun () -> failwith $"File not found: {fileName}") - let file = { - Range = rangeStartup - Source = fileSnapshot - IsLastCompiland = index = projectSnapshot.SourceFiles.Length - 1 - IsExe = tcConfig.target.IsExe - } + let file = + { + Range = rangeStartup + Source = fileSnapshot + IsLastCompiland = index = projectSnapshot.SourceFiles.Length - 1 + IsExe = tcConfig.target.IsExe + } let! file = file |> LoadSource let! parseResult, _ = getParseResult projectSnapshot Seq.empty file diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index 39551c271e8..b05f0c4ba54 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -1,7 +1,5 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - - namespace FSharp.Compiler.Diagnostics open System diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index fd09cbbb073..6d07475d954 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -599,14 +599,14 @@ let fuzzingTest seed (project: SyntheticProject) = task { do! Task.Delay (rng.Next maxCheckingDelayMs) } - + use _tracerProvider = Sdk.CreateTracerProviderBuilder() .AddSource("fsc") .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(serviceName="F# Fuzzing", serviceVersion = "1")) .AddJaegerExporter() .Build() - + use _ = Activity.start $"Fuzzing {project.Name}" [ Activity.Tags.project, project.Name; "seed", seed.ToString() ] do! task { From ff94633048d7830afe2bf093a21412d416ab74c4 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 29 Sep 2023 17:05:27 +0200 Subject: [PATCH 117/222] wip --- src/Compiler/Service/TransparentCompiler.fs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 652fd128b81..4bbdf5679c6 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -263,7 +263,7 @@ type internal CompilerCaches() = // member val Source = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "Source") - member val ParseFile = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "ParseFile") + member val ParseFile = AsyncMemoize(keepStrongly = 5000, keepWeakly = 2000, name = "ParseFile") member val ParseAndCheckFileInProject = AsyncMemoize(name = "ParseAndCheckFileInProject") @@ -527,7 +527,7 @@ type internal TransparentCompiler let defaultFSharpBinariesDir = FSharpCheckerResultsSettings.defaultFSharpBinariesDir let useScriptResolutionRules = projectSnapshot.UseScriptResolutionRules - let projectReferences = getProjectReferences projectSnapshot "ComputeBootstrapInfo" + let projectReferences = getProjectReferences projectSnapshot "ComputeTcConfigBuilder" // TODO: script support let loadClosureOpt: LoadClosure option = None @@ -886,7 +886,9 @@ type internal TransparentCompiler member _.GetKey() = projectKey.GetKey(), file.FileName member _.GetVersion() = - projectKey.GetVersion(), file.SourceHash, file.IsLastCompiland, file.IsExe + projectKey.GetVersion(), file.SourceHash, + // TODO: is there a situation where this is not enough and we need to have them separate? + file.IsLastCompiland && file.IsExe } caches.ParseFile.Get( From c64a9ff54ecb60832404f9a042e91cf413fefdcb Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 2 Oct 2023 13:29:48 +0200 Subject: [PATCH 118/222] Project key depends on output/target --- src/Compiler/Service/FSharpCheckerResults.fs | 6 ++++-- src/Compiler/Service/FSharpCheckerResults.fsi | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 54d9f874e9d..906b1b5a8f6 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -150,7 +150,7 @@ type FSharpFileSnapshotWithSource = type ReferenceOnDisk = { Path: string; LastModified: DateTime } -type ProjectSnapshotKey = string +type ProjectSnapshotKey = string * string [] type FSharpProjectSnapshot = @@ -201,6 +201,8 @@ type FSharpProjectSnapshot = member po.ProjectDirectory = Path.GetDirectoryName(po.ProjectFileName) + member this.OutputFileName = this.OtherOptions |> List.tryFind (fun x -> x.StartsWith("-o:")) |> Option.map (fun x -> x.Substring(3)) + member this.IndexOf fileName = this.SourceFiles |> List.tryFindIndex (fun x -> x.FileName = fileName) @@ -329,7 +331,7 @@ type FSharpProjectSnapshot = interface ICacheKey with member this.GetLabel() = this.ToString() - member this.GetKey() = this.ProjectFileName + member this.GetKey() = this.ProjectFileName, this.OutputFileName |> Option.defaultValue "" member this.GetVersion() = this.GetDebugVersion() and FSharpProjectSnapshotWithSources = diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index c3440c44c75..0b90939cd5a 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -65,7 +65,7 @@ type FSharpFileSnapshotWithSource = type ReferenceOnDisk = { Path: string; LastModified: DateTime } -type ProjectSnapshotKey = string +type ProjectSnapshotKey = string * string [] type FSharpProjectSnapshot = @@ -123,6 +123,8 @@ type FSharpProjectSnapshot = /// Compute the project directory. member internal ProjectDirectory: string + member OutputFileName: string option + member SourceFileNames: string list member CommandLineOptions: string list @@ -154,7 +156,7 @@ type FSharpProjectSnapshot = member internal Key: ICacheKey - member internal FileKey: fileName: string -> ICacheKey<(string * string), FSharpProjectSnapshotVersion> + member internal FileKey: fileName: string -> ICacheKey<(string * ProjectSnapshotKey), FSharpProjectSnapshotVersion> interface ICacheKey @@ -171,7 +173,7 @@ and FSharpProjectSnapshotWithSources = member WithoutImplFilesThatHaveSignatures: FSharpProjectSnapshotWithSources member WithoutImplFilesThatHaveSignaturesExceptLastOne: FSharpProjectSnapshotWithSources - member internal FileKey: fileName: string -> ICacheKey<(string * string), FSharpProjectSnapshotWithSourcesVersion> + member internal FileKey: fileName: string -> ICacheKey<(string * ProjectSnapshotKey), FSharpProjectSnapshotWithSourcesVersion> member internal Key: ICacheKey interface ICacheKey From 4c2846a467b6a9bb33a6c721c73bf6ddd04efbbd Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 2 Oct 2023 14:44:52 +0200 Subject: [PATCH 119/222] wip --- .../src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index cea2a5cc016..20b45f8db11 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -55,9 +55,9 @@ module private CheckerExtensions = } | None -> - Trace.TraceError("Could not find document {0} in project {1}", path, options.ProjectFileName) + // This happens with files that are read from /obj - // Fall back to file system, although this is already suspicious + // Fall back to file system let version = System.IO.File.GetLastWriteTimeUtc(path) let getSource () = From 029f060b460a257cf620e70382f09d67733d217c Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 2 Oct 2023 17:54:00 +0200 Subject: [PATCH 120/222] wip --- src/Compiler/FSharp.Compiler.Service.fsproj | 3 +- .../LanguageService/WorkspaceExtensions.fs | 98 +++++++++++-------- 2 files changed, 58 insertions(+), 43 deletions(-) diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index d01700df6eb..f4950034765 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -76,7 +76,8 @@ - + + diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 9f6ee7d4cbd..bb0c45d9f13 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -14,65 +14,79 @@ open FSharp.Compiler.Symbols open CancellableTasks +open Internal.Utilities.Collections + [] module private CheckerExtensions = + let snapshotCache = AsyncMemoize(5000, 500, "SnapshotCache") + let getProjectSnapshot (document: Document, options: FSharpProjectOptions) = - async { - let project = document.Project - let solution = project.Solution - // TODO cache? - let projects = - solution.Projects - |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) - |> Map - let getFileSnapshot (options: FSharpProjectOptions) path = - async { - let project = projects.TryFind options.ProjectFileName + let key = + { new ICacheKey<_, _> + with member _.GetKey() = document.Id, options.ProjectFileName + member _.GetVersion() = document + member _.GetLabel() = document.FilePath } - if project.IsNone then - System.Diagnostics.Trace.TraceError("Could not find project {0} in solution {1}", options.ProjectFileName, solution.FilePath) + snapshotCache.Get( + key, + async { - let documentOpt = project |> Option.bind (Map.tryFind path) + let project = document.Project + let solution = project.Solution - let! version, getSource = - match documentOpt with - | Some document -> - async { + let projects = + solution.Projects + |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) + |> Map - let! version = document.GetTextVersionAsync() |> Async.AwaitTask + let getFileSnapshot (options: FSharpProjectOptions) path = + async { + let project = projects.TryFind options.ProjectFileName - let getSource () = - task { - let! sourceText = document.GetTextAsync() - return sourceText.ToFSharpSourceText() - } + if project.IsNone then + System.Diagnostics.Trace.TraceError("Could not find project {0} in solution {1}", options.ProjectFileName, solution.FilePath) - return version.ToString(), getSource + let documentOpt = project |> Option.bind (Map.tryFind path) - } - | None -> - // This happens with files that are read from /obj + let! version, getSource = + match documentOpt with + | Some document -> + async { - // Fall back to file system - let version = System.IO.File.GetLastWriteTimeUtc(path) + let! version = document.GetTextVersionAsync() |> Async.AwaitTask - let getSource () = - task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceText.ofString } + let getSource () = + task { + let! sourceText = document.GetTextAsync() + return sourceText.ToFSharpSourceText() + } - async.Return(version.ToString(), getSource) + return version.ToString(), getSource - return - { - FileName = path - Version = version - GetSource = getSource - } - } + } + | None -> + // This happens with files that are read from /obj - return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) - } + // Fall back to file system + let version = System.IO.File.GetLastWriteTimeUtc(path) + + let getSource () = + task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceText.ofString } + + async.Return(version.ToString(), getSource) + + return + { + FileName = path + Version = version + GetSource = getSource + } + } + + return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + }) type FSharpChecker with From 08072a70edf0943801d240d0a7b98a7c827225cd Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 3 Oct 2023 17:46:28 +0200 Subject: [PATCH 121/222] reuse snapshots for finding references --- src/Compiler/Service/FSharpCheckerResults.fs | 93 +++++----- src/Compiler/Service/FSharpCheckerResults.fsi | 4 +- .../FSharp.Editor/Common/CancellableTasks.fs | 10 ++ .../LanguageService/SymbolHelpers.fs | 9 +- .../LanguageService/WorkspaceExtensions.fs | 160 +++++++++++++----- 5 files changed, 185 insertions(+), 91 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 01d34a1ed26..79a26a5c4a5 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -6,6 +6,7 @@ namespace FSharp.Compiler.CodeAnalysis open System +open System.Collections.Generic open System.Diagnostics open System.IO open System.Reflection @@ -572,52 +573,60 @@ type FSharpProjectSnapshot with Stamp = this.Stamp } - static member FromOptions(options: FSharpProjectOptions, getFileSnapshot) = + static member FromOptions(options: FSharpProjectOptions, getFileSnapshot, ?snapshotAccumulator) = + let snapshotAccumulator = defaultArg snapshotAccumulator (Dictionary()) async { - let! sourceFiles = options.SourceFiles |> Seq.map (getFileSnapshot options) |> Async.Parallel - - let! referencedProjects = - options.ReferencedProjects - |> Seq.choose (function - | FSharpReferencedProject.FSharpReference (outputName, options) -> - Some( - async { - let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) - return FSharpReferencedProjectSnapshot.FSharpReference(outputName, snapshot) - } - ) - // TODO: other types - | _ -> None) - |> Async.Parallel - let referencesOnDisk, otherOptions = - options.OtherOptions - |> Array.partition (fun x -> x.StartsWith("-r:")) - |> map1Of2 ( - Array.map (fun x -> - let path = x.Substring(3) + // TODO: check if options is a good key here + if not (snapshotAccumulator.ContainsKey options) then - { - Path = path - LastModified = FileSystem.GetLastWriteTimeShim(path) - }) - ) + let! sourceFiles = options.SourceFiles |> Seq.map (getFileSnapshot options) |> Async.Parallel - return - { - ProjectFileName = options.ProjectFileName - ProjectId = options.ProjectId - SourceFiles = sourceFiles |> List.ofArray - ReferencesOnDisk = referencesOnDisk |> List.ofArray - OtherOptions = otherOptions |> List.ofArray - ReferencedProjects = referencedProjects |> List.ofArray - IsIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment - UseScriptResolutionRules = options.UseScriptResolutionRules - LoadTime = options.LoadTime - UnresolvedReferences = options.UnresolvedReferences - OriginalLoadReferences = options.OriginalLoadReferences - Stamp = options.Stamp - } + let! referencedProjects = + options.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProject.FSharpReference (outputName, options) -> + Some( + async { + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, snapshotAccumulator) + return FSharpReferencedProjectSnapshot.FSharpReference(outputName, snapshot) + } + ) + // TODO: other types + | _ -> None) + |> Async.Sequential + + let referencesOnDisk, otherOptions = + options.OtherOptions + |> Array.partition (fun x -> x.StartsWith("-r:")) + |> map1Of2 ( + Array.map (fun x -> + let path = x.Substring(3) + + { + Path = path + LastModified = FileSystem.GetLastWriteTimeShim(path) + }) + ) + + let snapshot = + { + ProjectFileName = options.ProjectFileName + ProjectId = options.ProjectId + SourceFiles = sourceFiles |> List.ofArray + ReferencesOnDisk = referencesOnDisk |> List.ofArray + OtherOptions = otherOptions |> List.ofArray + ReferencedProjects = referencedProjects |> List.ofArray + IsIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = options.UseScriptResolutionRules + LoadTime = options.LoadTime + UnresolvedReferences = options.UnresolvedReferences + OriginalLoadReferences = options.OriginalLoadReferences + Stamp = options.Stamp + } + snapshotAccumulator.Add(options, snapshot) + + return snapshotAccumulator[options] } static member FromOptions(options: FSharpProjectOptions) = diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 0b90939cd5a..ed3c9c519a7 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -3,6 +3,7 @@ namespace FSharp.Compiler.CodeAnalysis open System +open System.Collections.Generic open System.IO open System.Threading open System.Threading.Tasks @@ -314,8 +315,9 @@ type FSharpProjectSnapshot with member ToOptions: unit -> FSharpProjectOptions /// Create snapshot from FSharpProjectOptions using given function to retrieve file contnets. + /// If given a snapshotAccumulator, it will be filled with any snapshots created during the operation, i.e. from this project and any referenced projects static member FromOptions: - options: FSharpProjectOptions * getFileSnapshot: (FSharpProjectOptions -> string -> Async) -> + options: FSharpProjectOptions * getFileSnapshot: (FSharpProjectOptions -> string -> Async) * ?snapshotAccumulator: Dictionary -> Async /// Create snapshot from FSharpProjectOptions using the filesystem to retrieve file contents. diff --git a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs index 1cd71d4228e..0326688f987 100644 --- a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs +++ b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs @@ -1107,6 +1107,16 @@ module CancellableTasks = return! Task.WhenAll (tasks) } + let inline sequential (tasks: CancellableTask<'a> seq) = + cancellableTask { + let! ct = getCancellationToken () + let results = ResizeArray() + for task in tasks do + let! result = start ct task + results.Add(result) + return results + } + let inline ignore ([] ctask: CancellableTask<_>) = toUnit ctask /// diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 9871d042f2f..71515098450 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -3,6 +3,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System.Collections.Concurrent +open System.Collections.Generic open System.Collections.Immutable open System.Threading.Tasks @@ -80,9 +81,15 @@ module internal SymbolHelpers = // TODO: this needs to be a single event with a duration TelemetryReporter.ReportSingleEvent(TelemetryEvents.GetSymbolUsesInProjectsStarted, props) + let snapshotAccumulator = Dictionary() + let! projects = + projects + |> Seq.map (fun project -> project.GetFSharpProjectSnapshot(snapshotAccumulator) |> CancellableTask.map (fun s -> project, s)) + |> CancellableTask.sequential + do! projects - |> Seq.map (fun project -> project.FindFSharpReferencesAsync(symbol, onFound, "getSymbolUsesInProjects")) + |> Seq.map (fun (project, snapshot) -> project.FindFSharpReferencesAsync(symbol, snapshot, onFound, "getSymbolUsesInProjects")) |> CancellableTask.whenAll TelemetryReporter.ReportSingleEvent(TelemetryEvents.GetSymbolUsesInProjectsFinished, props) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index bb0c45d9f13..8f3371dd506 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -2,6 +2,7 @@ module internal Microsoft.VisualStudio.FSharp.Editor.WorkspaceExtensions open System +open System.Collections.Generic open System.Runtime.CompilerServices open System.Threading.Tasks @@ -16,12 +17,112 @@ open CancellableTasks open Internal.Utilities.Collections + +[] +module internal ProjectCache = + + /// This is a cache to maintain FSharpParsingOptions and FSharpProjectOptions per Roslyn Project. + /// The Roslyn Project is held weakly meaning when it is cleaned up by the GC, the FSharParsingOptions and FSharpProjectOptions will be cleaned up by the GC. + /// At some point, this will be the main caching mechanism for FCS projects instead of FCS itself. + let Projects = + ConditionalWeakTable() + + +type Solution with + + /// Get the instance of IFSharpWorkspaceService. + member internal this.GetFSharpWorkspaceService() = + this.Workspace.Services.GetRequiredService() + + [] module private CheckerExtensions = let snapshotCache = AsyncMemoize(5000, 500, "SnapshotCache") - let getProjectSnapshot (document: Document, options: FSharpProjectOptions) = + let getFSharpOptionsForProject (this: Project) = + if not this.IsFSharp then + raise (OperationCanceledException("Project is not a FSharp project.")) + else + match ProjectCache.Projects.TryGetValue(this) with + | true, result -> CancellableTask.singleton result + | _ -> + cancellableTask { + + let! ct = CancellableTask.getCancellationToken () + + let service = this.Solution.GetFSharpWorkspaceService() + let projectOptionsManager = service.FSharpProjectOptionsManager + + match! projectOptionsManager.TryGetOptionsByProject(this, ct) with + | ValueNone -> return raise (OperationCanceledException("FSharp project options not found.")) + | ValueSome (parsingOptions, projectOptions) -> + let result = + (service.Checker, projectOptionsManager, parsingOptions, projectOptions) + + return ProjectCache.Projects.GetValue(this, ConditionalWeakTable<_, _>.CreateValueCallback (fun _ -> result)) + } + + let getProjectSnapshot (snapshotAccumulatorOpt) (project: Project) = + cancellableTask { + + let! _, _, _, options = getFSharpOptionsForProject project + + let solution = project.Solution + + let projects = + solution.Projects + |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) + |> Map + + let getFileSnapshot (options: FSharpProjectOptions) path = + async { + let project = projects.TryFind options.ProjectFileName + + if project.IsNone then + System.Diagnostics.Trace.TraceError("Could not find project {0} in solution {1}", options.ProjectFileName, solution.FilePath) + + let documentOpt = project |> Option.bind (Map.tryFind path) + + let! version, getSource = + match documentOpt with + | Some document -> + async { + + let! version = document.GetTextVersionAsync() |> Async.AwaitTask + + let getSource () = + task { + let! sourceText = document.GetTextAsync() + return sourceText.ToFSharpSourceText() + } + + return version.ToString(), getSource + + } + | None -> + // This happens with files that are read from /obj + + // Fall back to file system + let version = System.IO.File.GetLastWriteTimeUtc(path) + + let getSource () = + task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceText.ofString } + + async.Return(version.ToString(), getSource) + + return + { + FileName = path + Version = version + GetSource = getSource + } + } + + return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator=snapshotAccumulatorOpt) + } + + let getProjectSnapshotForDocument (document: Document, options: FSharpProjectOptions) = let key = { new ICacheKey<_, _> @@ -101,7 +202,7 @@ module private CheckerExtensions = member checker.ParseDocumentUsingTransparentCompiler(document: Document, options: FSharpProjectOptions, userOpName: string) = cancellableTask { - let! projectSnapshot = getProjectSnapshot (document, options) + let! projectSnapshot = getProjectSnapshotForDocument (document, options) return! checker.ParseFile(document.FilePath, projectSnapshot, userOpName = userOpName) } @@ -112,7 +213,7 @@ module private CheckerExtensions = userOpName: string ) = cancellableTask { - let! projectSnapshot = getProjectSnapshot (document, options) + let! projectSnapshot = getProjectSnapshotForDocument (document, options) let! (parseResults, checkFileAnswer) = checker.ParseAndCheckFileInProject(document.FilePath, projectSnapshot, userOpName) @@ -216,21 +317,6 @@ module private CheckerExtensions = checker.ParseAndCheckDocumentWithPossibleStaleResults(document, options, allowStaleResults, userOpName = userOpName) } -[] -module internal ProjectCache = - - /// This is a cache to maintain FSharpParsingOptions and FSharpProjectOptions per Roslyn Project. - /// The Roslyn Project is held weakly meaning when it is cleaned up by the GC, the FSharParsingOptions and FSharpProjectOptions will be cleaned up by the GC. - /// At some point, this will be the main caching mechanism for FCS projects instead of FCS itself. - let Projects = - ConditionalWeakTable() - -type Solution with - - /// Get the instance of IFSharpWorkspaceService. - member internal this.GetFSharpWorkspaceService() = - this.Workspace.Services.GetRequiredService() - type Document with /// Get the FSharpParsingOptions and FSharpProjectOptions from the F# project that is associated with the given F# document. @@ -324,7 +410,7 @@ type Document with let! result = if this.Project.UseTransparentCompiler then async { - let! projectSnapshot = getProjectSnapshot (this, projectOptions) + let! projectSnapshot = getProjectSnapshotForDocument (this, projectOptions) return! checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectSnapshot) } else @@ -336,17 +422,14 @@ type Document with } /// Find F# references in the given F# document. - member inline this.FindFSharpReferencesAsync(symbol, [] onFound, userOpName) = + member inline this.FindFSharpReferencesAsync(symbol, projectSnapshot: FSharpProjectSnapshot, [] onFound, userOpName) = cancellableTask { let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync(userOpName) let! symbolUses = if this.Project.UseTransparentCompiler then - async { - let! projectSnapshot = getProjectSnapshot (this, projectOptions) - return! checker.FindBackgroundReferencesInFile(this.FilePath, projectSnapshot, symbol) - } + checker.FindBackgroundReferencesInFile(this.FilePath, projectSnapshot, symbol) else checker.FindBackgroundReferencesInFile( this.FilePath, @@ -389,7 +472,7 @@ type Document with type Project with /// Find F# references in the given project. - member this.FindFSharpReferencesAsync(symbol: FSharpSymbol, onFound, userOpName) = + member this.FindFSharpReferencesAsync(symbol: FSharpSymbol, projectSnapshot, onFound, userOpName) = cancellableTask { let declarationLocation = @@ -429,32 +512,15 @@ type Project with if this.IsFastFindReferencesEnabled then do! documents - |> Seq.map (fun doc -> doc.FindFSharpReferencesAsync(symbol, (fun range -> onFound doc range), userOpName)) + |> Seq.map (fun doc -> doc.FindFSharpReferencesAsync(symbol, projectSnapshot, (fun range -> onFound doc range), userOpName)) |> CancellableTask.whenAll else for doc in documents do - do! doc.FindFSharpReferencesAsync(symbol, (onFound doc), userOpName) + do! doc.FindFSharpReferencesAsync(symbol, projectSnapshot, (onFound doc), userOpName) } member this.GetFSharpCompilationOptionsAsync() = - if not this.IsFSharp then - raise (OperationCanceledException("Project is not a FSharp project.")) - else - match ProjectCache.Projects.TryGetValue(this) with - | true, result -> CancellableTask.singleton result - | _ -> - cancellableTask { + this |> getFSharpOptionsForProject - let! ct = CancellableTask.getCancellationToken () - - let service = this.Solution.GetFSharpWorkspaceService() - let projectOptionsManager = service.FSharpProjectOptionsManager - - match! projectOptionsManager.TryGetOptionsByProject(this, ct) with - | ValueNone -> return raise (OperationCanceledException("FSharp project options not found.")) - | ValueSome (parsingOptions, projectOptions) -> - let result = - (service.Checker, projectOptionsManager, parsingOptions, projectOptions) - - return ProjectCache.Projects.GetValue(this, ConditionalWeakTable<_, _>.CreateValueCallback (fun _ -> result)) - } + member this.GetFSharpProjectSnapshot(?snapshotAccumulator) = + this |> getProjectSnapshot snapshotAccumulator \ No newline at end of file From 6c47396005358768141b512e73e88ead76452315 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 5 Oct 2023 14:21:24 +0200 Subject: [PATCH 122/222] Configurable cache size factor --- src/Compiler/Facilities/AsyncMemoize.fs | 9 ++- src/Compiler/Service/TransparentCompiler.fs | 40 ++++++++----- .../LanguageService/WorkspaceExtensions.fs | 3 + .../FSharp.Editor/Options/EditorOptions.fs | 4 ++ ...nguageServicePerformanceOptionControl.xaml | 24 +++++++- .../FSharp.UIResources/Strings.Designer.cs | 18 ++++++ .../src/FSharp.UIResources/Strings.resx | 60 ++++++++++--------- .../src/FSharp.UIResources/xlf/Strings.cs.xlf | 10 ++++ .../src/FSharp.UIResources/xlf/Strings.de.xlf | 10 ++++ .../src/FSharp.UIResources/xlf/Strings.es.xlf | 10 ++++ .../src/FSharp.UIResources/xlf/Strings.fr.xlf | 10 ++++ .../src/FSharp.UIResources/xlf/Strings.it.xlf | 10 ++++ .../src/FSharp.UIResources/xlf/Strings.ja.xlf | 10 ++++ .../src/FSharp.UIResources/xlf/Strings.ko.xlf | 10 ++++ .../src/FSharp.UIResources/xlf/Strings.pl.xlf | 10 ++++ .../FSharp.UIResources/xlf/Strings.pt-BR.xlf | 10 ++++ .../src/FSharp.UIResources/xlf/Strings.ru.xlf | 10 ++++ .../src/FSharp.UIResources/xlf/Strings.tr.xlf | 10 ++++ .../xlf/Strings.zh-Hans.xlf | 10 ++++ .../xlf/Strings.zh-Hant.xlf | 10 ++++ 20 files changed, 239 insertions(+), 49 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 73945632ccb..d827436d441 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -576,8 +576,11 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T () //System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" + // Probably in some cases cancellation can be fired off even after we just unregistered it | CancelRequest, None - | CancelRequest, Some (Completed _) -> () + | CancelRequest, Some (Completed _) + | OriginatorCanceled, None + | OriginatorCanceled, Some (Completed _) -> () | JobFailed ex, Some (Running (tcs, _cts, _c, _ts)) -> cancelRegistration key @@ -602,15 +605,11 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T // Job can't be evicted from cache while it's running because then subsequent requesters would be waiting forever | JobFailed _, None -> internalError key.Label "Invalid state: Running job missing in cache (failed)" - | OriginatorCanceled, None -> internalError key.Label "Invalid state: Running job missing in cache (canceled)" - | JobCompleted _, None -> internalError key.Label "Invalid state: Running job missing in cache (completed)" | JobFailed ex, Some (Completed _job) -> internalError key.Label $"Invalid state: Failed Completed job \n%A{ex}" | JobCompleted _result, Some (Completed _job) -> internalError key.Label "Invalid state: Double-Completed job" - - | OriginatorCanceled, Some (Completed _result) -> internalError key.Label "Invalid state: Canceled Completed job" }) } diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 4bbdf5679c6..dda60cecad2 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -259,37 +259,41 @@ module private TypeCheckingGraphProcessing = return finalFileResults, state } -type internal CompilerCaches() = +type internal CompilerCaches(sizeFactor: int) = // member val Source = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "Source") - member val ParseFile = AsyncMemoize(keepStrongly = 5000, keepWeakly = 2000, name = "ParseFile") + let sf = sizeFactor - member val ParseAndCheckFileInProject = AsyncMemoize(name = "ParseAndCheckFileInProject") + member _.SizeFactor = sf - member val ParseAndCheckAllFilesInProject = AsyncMemoize(name = "ParseAndCheckFullProject") + member val ParseFile = AsyncMemoize(keepStrongly = 50 * sf, keepWeakly = 20 * sf, name = "ParseFile") - member val ParseAndCheckProject = AsyncMemoize(name = "ParseAndCheckProject") + member val ParseAndCheckFileInProject = AsyncMemoize(sf, 2 * sf, name = "ParseAndCheckFileInProject") - member val FrameworkImports = AsyncMemoize(name = "FrameworkImports") + member val ParseAndCheckAllFilesInProject = AsyncMemoize(sf, 2 * sf, name = "ParseAndCheckFullProject") - member val BootstrapInfoStatic = AsyncMemoize(name = "BootstrapInfoStatic") + member val ParseAndCheckProject = AsyncMemoize(sf, 2 * sf, name = "ParseAndCheckProject") - member val BootstrapInfo = AsyncMemoize(name = "BootstrapInfo") + member val FrameworkImports = AsyncMemoize(sf, 2 * sf, name = "FrameworkImports") + + member val BootstrapInfoStatic = AsyncMemoize(sf, 2 * sf, name = "BootstrapInfoStatic") + + member val BootstrapInfo = AsyncMemoize(sf, 2 * sf, name = "BootstrapInfo") // member val TcLastFile = AsyncMemoize(name = "TcLastFile") - member val TcIntermediate = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "TcIntermediate") + member val TcIntermediate = AsyncMemoize(20 * sf, 20 * sf, name = "TcIntermediate") - member val DependencyGraph = AsyncMemoize(name = "DependencyGraph") + member val DependencyGraph = AsyncMemoize(sf, 2 * sf, name = "DependencyGraph") - member val ProjectExtras = AsyncMemoize(name = "ProjectExtras") + member val ProjectExtras = AsyncMemoize(sf, 2 * sf, name = "ProjectExtras") - member val AssemblyData = AsyncMemoize(name = "AssemblyData") + member val AssemblyData = AsyncMemoize(sf, 2 * sf, name = "AssemblyData") - member val SemanticClassification = AsyncMemoize(name = "SemanticClassification") + member val SemanticClassification = AsyncMemoize(sf, 2 * sf, name = "SemanticClassification") - member val ItemKeyStore = AsyncMemoize(name = "ItemKeyStore") + member val ItemKeyStore = AsyncMemoize(sf, 2 * sf, name = "ItemKeyStore") type internal TransparentCompiler ( @@ -313,7 +317,7 @@ type internal TransparentCompiler let lexResourceManager = Lexhelp.LexResourceManager() // Mutable so we can easily clear them by creating a new instance - let mutable caches = CompilerCaches() + let mutable caches = CompilerCaches(100) let maxTypeCheckingParallelism = max 1 (Environment.ProcessorCount / 2) @@ -1758,6 +1762,10 @@ type internal TransparentCompiler member _.Caches = caches + member _.SetCacheSizeFactor(sizeFactor: int) = + if sizeFactor <> caches.SizeFactor then + caches <- CompilerCaches(sizeFactor) + interface IBackgroundCompiler with member this.BeforeBackgroundFileCheck: IEvent = @@ -1814,7 +1822,7 @@ type internal TransparentCompiler member _.ClearCaches() : unit = backgroundCompiler.ClearCaches() - caches <- CompilerCaches() + caches <- CompilerCaches(100) // TODO: check member _.DownsizeCaches() : unit = backgroundCompiler.DownsizeCaches() diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 8f3371dd506..1ac58b2e6b8 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -213,6 +213,9 @@ module private CheckerExtensions = userOpName: string ) = cancellableTask { + + checker.TransparentCompiler.SetCacheSizeFactor(document.Project.TransparentCompilerCacheFactor) + let! projectSnapshot = getProjectSnapshotForDocument (document, options) let! (parseResults, checkFileAnswer) = checker.ParseAndCheckFileInProject(document.FilePath, projectSnapshot, userOpName) diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index da6c43b1bb4..d6720cc4046 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -79,6 +79,7 @@ type CodeFixesOptions = type LanguageServicePerformanceOptions = { EnableInMemoryCrossProjectReferences: bool + TransparentCompilerCacheFactor: int AllowStaleCompletionResults: bool TimeUntilStaleCompletion: int EnableParallelReferenceResolution: bool @@ -93,6 +94,7 @@ type LanguageServicePerformanceOptions = static member Default = { EnableInMemoryCrossProjectReferences = true + TransparentCompilerCacheFactor = 100 AllowStaleCompletionResults = true TimeUntilStaleCompletion = 2000 // In ms, so this is 2 seconds EnableParallelReferenceResolution = false @@ -262,3 +264,5 @@ module EditorOptionsExtensions = this.EditorOptions.LanguageServicePerformance.EnableFastFindReferencesAndRename member this.UseTransparentCompiler = this.EditorOptions.Advanced.UseTransparentCompiler + + member this.TransparentCompilerCacheFactor = this.EditorOptions.LanguageServicePerformance.TransparentCompilerCacheFactor diff --git a/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml b/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml index 96b8915b80e..a5c5f1c0b6f 100644 --- a/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml +++ b/vsintegration/src/FSharp.UIResources/LanguageServicePerformanceOptionControl.xaml @@ -23,6 +23,28 @@ IsChecked="{Binding EnableInMemoryCrossProjectReferences}" Content="{x:Static local:Strings.Enable_in_memory_cross_project_references}" ToolTip="{x:Static local:Strings.Tooltip_in_memory_cross_project_references}"/> + + + + + + @@ -46,7 +68,7 @@ Content="{x:Static local:Strings.Time_until_stale_completion}" Margin="15 0 0 0"/> diff --git a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs index aa497875594..5ac8d538bce 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs +++ b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs @@ -537,6 +537,24 @@ public static string Tooltip_preferred_description_width_in_characters { } } + /// + /// Looks up a localized string similar to Transparent Compiler Cache Factor. + /// + public static string Transparent_Compiler_Cache_Factor { + get { + return ResourceManager.GetString("Transparent_Compiler_Cache_Factor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Higher number means more memory will be used for caching. Changing the value wipes cache.. + /// + public static string Transparent_Compiler_Cache_Factor_Tooltip { + get { + return ResourceManager.GetString("Transparent_Compiler_Cache_Factor_Tooltip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Transparent Compiler (experimental). /// diff --git a/vsintegration/src/FSharp.UIResources/Strings.resx b/vsintegration/src/FSharp.UIResources/Strings.resx index 0ada09745ff..f9f06aad13c 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.resx +++ b/vsintegration/src/FSharp.UIResources/Strings.resx @@ -1,17 +1,17 @@  - @@ -291,4 +291,10 @@ Background analysis + + Transparent Compiler Cache Factor + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf index 6f165e34632..a1f8c1c28f3 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements Odebrat nepoužívané otevřené výkazy diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf index 56e3c163cbf..fea7c8482ca 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements Nicht verwendete "open"-Anweisungen entfernen diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf index 96d0af57fd1..06f7bd62909 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements Quitar instrucciones open no usadas diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf index 06372fc89ee..65237c3ffdf 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements Supprimer les instructions open inutilisées diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf index 31648bcac71..2829b3b6386 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements Rimuovi istruzioni OPEN inutilizzate diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf index 09d7cf75414..effef0f6cb8 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements 未使用の Open ステートメントを削除する diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf index 316f3d01f74..0dcb891d3e8 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements 사용되지 않는 open 문 제거 diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf index d2561e9bace..2d74ed9c95f 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements Usuń nieużywane otwarte instrukcje diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf index 4cff5cb18ec..b5f8861bb5b 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements Remover instruções abertas não usadas diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf index b05a52b2624..0cf34b9f8e9 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements Удалить неиспользуемые открытые операторы diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf index 2e3cd2c254f..1be1aefe868 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements Kullanılmayan açık deyimleri kaldır diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf index 45e508affb6..93699cb4355 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements 删除未使用的 open 语句 diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf index 2bf68b9629b..b73199db681 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf @@ -197,6 +197,16 @@ Transparent Compiler (experimental) + + Transparent Compiler Cache Factor + Transparent Compiler Cache Factor + + + + Higher number means more memory will be used for caching. Changing the value wipes cache. + Higher number means more memory will be used for caching. Changing the value wipes cache. + + Remove unused open statements 移除未使用的 open 陳述式 From 5905122f16589f867785c49cc9a7404cbc7d8155 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 6 Oct 2023 15:46:38 +0200 Subject: [PATCH 123/222] snapshot cache cleanup --- .../LanguageService/LanguageService.fs | 10 +-- .../LanguageService/SymbolHelpers.fs | 4 +- .../LanguageService/WorkspaceExtensions.fs | 62 ++----------------- 3 files changed, 12 insertions(+), 64 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 65138368781..d95fa3df290 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -491,10 +491,10 @@ type internal HackCpsCommandLineChanges [] /// This handles commandline change notifications from the Dotnet Project-system /// Prior to VS 15.7 path contained path to project file, post 15.7 contains target binpath /// binpath is more accurate because a project file can have multiple in memory projects based on configuration + [] member _.HandleCommandLineChanges ( path: string, @@ -537,10 +537,10 @@ type internal HackCpsCommandLineChanges [ Seq.map (fun s -> getFullPath s.Path) |> Seq.toArray - /// Due to an issue in project system, when we close and reopen solution, it sends the CommandLineChanges twice for every project. - /// First time it sends a correct path, sources, references and options. - /// Second time it sends a correct path, empty sources, empty references and empty options, and we rewrite our cache, and fail to colourize the document later. - /// As a workaround, until we have a fix from PS or will move to Roslyn as a source of truth, we will not overwrite the cache in case of empty lists. + // Due to an issue in project system, when we close and reopen solution, it sends the CommandLineChanges twice for every project. + // First time it sends a correct path, sources, references and options. + // Second time it sends a correct path, empty sources, empty references and empty options, and we rewrite our cache, and fail to colourize the document later. + // As a workaround, until we have a fix from PS or will move to Roslyn as a source of truth, we will not overwrite the cache in case of empty lists. if not (sources.IsEmpty && references.IsEmpty && options.IsEmpty) then let workspaceService = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 71515098450..963aa5aec58 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -83,10 +83,10 @@ module internal SymbolHelpers = let snapshotAccumulator = Dictionary() let! projects = - projects + projects |> Seq.map (fun project -> project.GetFSharpProjectSnapshot(snapshotAccumulator) |> CancellableTask.map (fun s -> project, s)) |> CancellableTask.sequential - + do! projects |> Seq.map (fun (project, snapshot) -> project.FindFSharpReferencesAsync(symbol, snapshot, onFound, "getSymbolUsesInProjects")) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 1ac58b2e6b8..9d34cd1d0b0 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -126,67 +126,15 @@ module private CheckerExtensions = let key = { new ICacheKey<_, _> - with member _.GetKey() = document.Id, options.ProjectFileName - member _.GetVersion() = document - member _.GetLabel() = document.FilePath } + with member _.GetKey() = document.Project.Id + member _.GetVersion() = document.Project + member _.GetLabel() = options.ProjectFileName } snapshotCache.Get( key, async { - - let project = document.Project - let solution = project.Solution - - let projects = - solution.Projects - |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) - |> Map - - let getFileSnapshot (options: FSharpProjectOptions) path = - async { - let project = projects.TryFind options.ProjectFileName - - if project.IsNone then - System.Diagnostics.Trace.TraceError("Could not find project {0} in solution {1}", options.ProjectFileName, solution.FilePath) - - let documentOpt = project |> Option.bind (Map.tryFind path) - - let! version, getSource = - match documentOpt with - | Some document -> - async { - - let! version = document.GetTextVersionAsync() |> Async.AwaitTask - - let getSource () = - task { - let! sourceText = document.GetTextAsync() - return sourceText.ToFSharpSourceText() - } - - return version.ToString(), getSource - - } - | None -> - // This happens with files that are read from /obj - - // Fall back to file system - let version = System.IO.File.GetLastWriteTimeUtc(path) - - let getSource () = - task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceText.ofString } - - async.Return(version.ToString(), getSource) - - return - { - FileName = path - Version = version - GetSource = getSource - } - } - - return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + let! ct = Async.CancellationToken + return! getProjectSnapshot None document.Project ct |> Async.AwaitTask }) type FSharpChecker with From 297da99f756c597b271bfdd1dd9ab7d978f9ec4e Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 6 Oct 2023 16:26:22 +0200 Subject: [PATCH 124/222] don't store all cache durations --- src/Compiler/Facilities/AsyncMemoize.fs | 19 ++++++++++--------- src/Compiler/Service/FSharpCheckerResults.fs | 14 +++++++++++--- src/Compiler/Service/FSharpCheckerResults.fsi | 9 ++++++--- src/Compiler/Service/TransparentCompiler.fs | 10 ++++++---- src/Compiler/Utilities/Activity.fsi | 1 - 5 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index d827436d441..46117b05792 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -396,7 +396,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let mutable collected = 0 let failures = ResizeArray() - let durations = ResizeArray() + let mutable avgDurationMs = 0.0 let cache = LruCache<'TKey, 'TVersion, Job<'TValue>>( @@ -591,13 +591,19 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T failures.Add(key.Label, ex) tcs.TrySetException ex |> ignore - | JobCompleted result, Some (Running (tcs, _cts, _c, _ts)) -> + | JobCompleted result, Some (Running (tcs, _cts, _c, started)) -> cancelRegistration key cache.Set(key.Key, key.Version, key.Label, (Completed result)) requestCounts.Remove key |> ignore log (Finished, key) Interlocked.Increment &completed |> ignore - durations.Add(DateTime.Now - _ts) + let duration = float (DateTime.Now - started).Milliseconds + + avgDurationMs <- + if completed < 2 then + duration + else + avgDurationMs + (duration - avgDurationMs) / float completed if tcs.TrySetResult result = false then internalError key.Label "Invalid state: Completed job already completed" @@ -697,12 +703,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T |> Option.defaultValue "" let avgDuration = - match durations.Count with - | 0 -> "" - | _ -> - durations - |> Seq.averageBy (fun x -> x.TotalMilliseconds) - |> sprintf "| Avg: %.0f ms" + avgDurationMs |> sprintf "| Avg: %.0f ms" let stats = [| diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 79a26a5c4a5..06db489eb9f 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -202,7 +202,10 @@ type FSharpProjectSnapshot = member po.ProjectDirectory = Path.GetDirectoryName(po.ProjectFileName) - member this.OutputFileName = this.OtherOptions |> List.tryFind (fun x -> x.StartsWith("-o:")) |> Option.map (fun x -> x.Substring(3)) + member this.OutputFileName = + this.OtherOptions + |> List.tryFind (fun x -> x.StartsWith("-o:")) + |> Option.map (fun x -> x.Substring(3)) member this.IndexOf fileName = this.SourceFiles @@ -332,7 +335,10 @@ type FSharpProjectSnapshot = interface ICacheKey with member this.GetLabel() = this.ToString() - member this.GetKey() = this.ProjectFileName, this.OutputFileName |> Option.defaultValue "" + + member this.GetKey() = + this.ProjectFileName, this.OutputFileName |> Option.defaultValue "" + member this.GetVersion() = this.GetDebugVersion() and FSharpProjectSnapshotWithSources = @@ -575,6 +581,7 @@ type FSharpProjectSnapshot with static member FromOptions(options: FSharpProjectOptions, getFileSnapshot, ?snapshotAccumulator) = let snapshotAccumulator = defaultArg snapshotAccumulator (Dictionary()) + async { // TODO: check if options is a good key here @@ -609,7 +616,7 @@ type FSharpProjectSnapshot with }) ) - let snapshot = + let snapshot = { ProjectFileName = options.ProjectFileName ProjectId = options.ProjectId @@ -624,6 +631,7 @@ type FSharpProjectSnapshot with OriginalLoadReferences = options.OriginalLoadReferences Stamp = options.Stamp } + snapshotAccumulator.Add(options, snapshot) return snapshotAccumulator[options] diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index ed3c9c519a7..06aaf75974e 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -174,7 +174,8 @@ and FSharpProjectSnapshotWithSources = member WithoutImplFilesThatHaveSignatures: FSharpProjectSnapshotWithSources member WithoutImplFilesThatHaveSignaturesExceptLastOne: FSharpProjectSnapshotWithSources - member internal FileKey: fileName: string -> ICacheKey<(string * ProjectSnapshotKey), FSharpProjectSnapshotWithSourcesVersion> + member internal FileKey: + fileName: string -> ICacheKey<(string * ProjectSnapshotKey), FSharpProjectSnapshotWithSourcesVersion> member internal Key: ICacheKey interface ICacheKey @@ -315,9 +316,11 @@ type FSharpProjectSnapshot with member ToOptions: unit -> FSharpProjectOptions /// Create snapshot from FSharpProjectOptions using given function to retrieve file contnets. - /// If given a snapshotAccumulator, it will be filled with any snapshots created during the operation, i.e. from this project and any referenced projects + /// If given a snapshotAccumulator, it will be filled with any snapshots created during the operation, i.e. from this project and any referenced projects static member FromOptions: - options: FSharpProjectOptions * getFileSnapshot: (FSharpProjectOptions -> string -> Async) * ?snapshotAccumulator: Dictionary -> + options: FSharpProjectOptions * + getFileSnapshot: (FSharpProjectOptions -> string -> Async) * + ?snapshotAccumulator: Dictionary -> Async /// Create snapshot from FSharpProjectOptions using the filesystem to retrieve file contents. diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index dda60cecad2..a1120d5f9db 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -319,8 +319,8 @@ type internal TransparentCompiler // Mutable so we can easily clear them by creating a new instance let mutable caches = CompilerCaches(100) - let maxTypeCheckingParallelism = max 1 (Environment.ProcessorCount / 2) - + // TODO: do we need this? + let maxTypeCheckingParallelism = Environment.ProcessorCount let maxParallelismSemaphore = new SemaphoreSlim(maxTypeCheckingParallelism) // We currently share one global dependency provider for all scripts for the FSharpChecker. @@ -531,7 +531,8 @@ type internal TransparentCompiler let defaultFSharpBinariesDir = FSharpCheckerResultsSettings.defaultFSharpBinariesDir let useScriptResolutionRules = projectSnapshot.UseScriptResolutionRules - let projectReferences = getProjectReferences projectSnapshot "ComputeTcConfigBuilder" + let projectReferences = + getProjectReferences projectSnapshot "ComputeTcConfigBuilder" // TODO: script support let loadClosureOpt: LoadClosure option = None @@ -890,7 +891,8 @@ type internal TransparentCompiler member _.GetKey() = projectKey.GetKey(), file.FileName member _.GetVersion() = - projectKey.GetVersion(), file.SourceHash, + projectKey.GetVersion(), + file.SourceHash, // TODO: is there a situation where this is not enough and we need to have them separate? file.IsLastCompiland && file.IsExe } diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index b05f0c4ba54..afce0f3b554 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -1,5 +1,4 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - namespace FSharp.Compiler.Diagnostics open System From 29b7c4b828ae0a1a0ae92c2005f882e4c13e5c5a Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 9 Oct 2023 12:12:27 +0200 Subject: [PATCH 125/222] wip --- .../CodeFixes/AddOpenCodeFixProvider.fs | 31 +++++++++++-------- .../LanguageService/LanguageService.fs | 3 +- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs index 8b507c6fbeb..ac0b952f65f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs @@ -55,21 +55,26 @@ type internal AddOpenCodeFixProvider [] (assemblyContentPr // attribute, shouldn't be here | line when line.StartsWith "[<" && line.EndsWith ">]" -> - let moduleDeclLineNumber = + let moduleDeclLineNumberOpt = sourceText.Lines |> Seq.skip insertionLineNumber - |> Seq.findIndex (fun line -> line.ToString().Contains "module") - // add back the skipped lines - |> fun i -> insertionLineNumber + i + |> Seq.tryFindIndex (fun line -> line.ToString().Contains "module") - let moduleDeclLineText = sourceText.Lines[ moduleDeclLineNumber ].ToString().Trim() + match moduleDeclLineNumberOpt with + // implicit top level module + | None -> insertionLineNumber, $"{margin}open {ns}{br}{br}" + // explicit top level module + | Some number -> + // add back the skipped lines + let moduleDeclLineNumber = insertionLineNumber + number + let moduleDeclLineText = sourceText.Lines[ moduleDeclLineNumber ].ToString().Trim() - if moduleDeclLineText.EndsWith "=" then - insertionLineNumber, $"{margin}open {ns}{br}{br}" - else - moduleDeclLineNumber + 2, $"{margin}open {ns}{br}{br}" + if moduleDeclLineText.EndsWith "=" then + insertionLineNumber, $"{margin}open {ns}{br}{br}" + else + moduleDeclLineNumber + 2, $"{margin}open {ns}{br}{br}" - // something else, shot in the dark + // implicit top level module | _ -> insertionLineNumber, $"{margin}open {ns}{br}{br}" | ScopeKind.Namespace -> insertionLineNumber + 3, $"{margin}open {ns}{br}{br}" @@ -183,8 +188,8 @@ type internal AddOpenCodeFixProvider [] (assemblyContentPr let entities = assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies checkResults - |> Array.collect (fun s -> - [| + |> List.collect (fun s -> + [ yield s.TopRequireQualifiedAccessParent, s.AutoOpenParent, s.Namespace, s.CleanedIdents if isAttribute then let lastIdent = s.CleanedIdents.[s.CleanedIdents.Length - 1] @@ -199,7 +204,7 @@ type internal AddOpenCodeFixProvider [] (assemblyContentPr s.Namespace, s.CleanedIdents |> Array.replace (s.CleanedIdents.Length - 1) (lastIdent.Substring(0, lastIdent.Length - 9)) - |]) + ]) ParsedInput.GetLongIdentAt parseResults.ParseTree unresolvedIdentRange.End |> Option.bind (fun longIdent -> diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index e77ce6424b5..b22c772fc00 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -94,9 +94,10 @@ type internal FSharpWorkspaceServiceFactory [] let getSource filename = async { + let! ct = Async.CancellationToken match workspace.CurrentSolution.TryGetDocumentFromPath filename with | ValueSome document -> - let! text = document.GetTextAsync() |> Async.AwaitTask + let! text = document.GetTextAsync(ct) |> Async.AwaitTask return Some(text.ToFSharpSourceText()) | ValueNone -> return None } From 732d43a28e263664d11b076fef8478dfaec301f2 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 9 Oct 2023 14:48:16 +0200 Subject: [PATCH 126/222] fix --- .../src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs index ac0b952f65f..bb7450e220e 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/AddOpenCodeFixProvider.fs @@ -188,7 +188,7 @@ type internal AddOpenCodeFixProvider [] (assemblyContentPr let entities = assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies checkResults - |> List.collect (fun s -> + |> Seq.collect (fun s -> [ yield s.TopRequireQualifiedAccessParent, s.AutoOpenParent, s.Namespace, s.CleanedIdents if isAttribute then From a87b217d2ff7ab1cd5e743c090448dcf18f924a2 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 9 Oct 2023 15:05:38 +0200 Subject: [PATCH 127/222] f --- src/Compiler/Facilities/AsyncMemoize.fs | 3 +- .../LanguageService/LanguageService.fs | 1 + .../LanguageService/SymbolHelpers.fs | 8 +++-- .../LanguageService/WorkspaceExtensions.fs | 35 ++++++++++--------- .../FSharp.Editor/Options/EditorOptions.fs | 3 +- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 46117b05792..2c319b6287f 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -702,8 +702,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T |> Option.map (sprintf " Running: %d") |> Option.defaultValue "" - let avgDuration = - avgDurationMs |> sprintf "| Avg: %.0f ms" + let avgDuration = avgDurationMs |> sprintf "| Avg: %.0f ms" let stats = [| diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 0a8eb20736d..3d21301dfe3 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -95,6 +95,7 @@ type internal FSharpWorkspaceServiceFactory [] let getSource filename = async { let! ct = Async.CancellationToken + match workspace.CurrentSolution.TryGetDocumentFromPath filename with | ValueSome document -> let! text = document.GetTextAsync(ct) |> Async.AwaitTask diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 963aa5aec58..085b44bb3eb 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -82,14 +82,18 @@ module internal SymbolHelpers = TelemetryReporter.ReportSingleEvent(TelemetryEvents.GetSymbolUsesInProjectsStarted, props) let snapshotAccumulator = Dictionary() + let! projects = projects - |> Seq.map (fun project -> project.GetFSharpProjectSnapshot(snapshotAccumulator) |> CancellableTask.map (fun s -> project, s)) + |> Seq.map (fun project -> + project.GetFSharpProjectSnapshot(snapshotAccumulator) + |> CancellableTask.map (fun s -> project, s)) |> CancellableTask.sequential do! projects - |> Seq.map (fun (project, snapshot) -> project.FindFSharpReferencesAsync(symbol, snapshot, onFound, "getSymbolUsesInProjects")) + |> Seq.map (fun (project, snapshot) -> + project.FindFSharpReferencesAsync(symbol, snapshot, onFound, "getSymbolUsesInProjects")) |> CancellableTask.whenAll TelemetryReporter.ReportSingleEvent(TelemetryEvents.GetSymbolUsesInProjectsFinished, props) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 9d34cd1d0b0..b571ac1fe80 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -17,7 +17,6 @@ open CancellableTasks open Internal.Utilities.Collections - [] module internal ProjectCache = @@ -27,20 +26,18 @@ module internal ProjectCache = let Projects = ConditionalWeakTable() - type Solution with /// Get the instance of IFSharpWorkspaceService. member internal this.GetFSharpWorkspaceService() = this.Workspace.Services.GetRequiredService() - [] module private CheckerExtensions = let snapshotCache = AsyncMemoize(5000, 500, "SnapshotCache") - let getFSharpOptionsForProject (this: Project) = + let getFSharpOptionsForProject (this: Project) = if not this.IsFSharp then raise (OperationCanceledException("Project is not a FSharp project.")) else @@ -80,7 +77,11 @@ module private CheckerExtensions = let project = projects.TryFind options.ProjectFileName if project.IsNone then - System.Diagnostics.Trace.TraceError("Could not find project {0} in solution {1}", options.ProjectFileName, solution.FilePath) + System.Diagnostics.Trace.TraceError( + "Could not find project {0} in solution {1}", + options.ProjectFileName, + solution.FilePath + ) let documentOpt = project |> Option.bind (Map.tryFind path) @@ -119,23 +120,25 @@ module private CheckerExtensions = } } - return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator=snapshotAccumulatorOpt) + return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) } let getProjectSnapshotForDocument (document: Document, options: FSharpProjectOptions) = let key = - { new ICacheKey<_, _> - with member _.GetKey() = document.Project.Id - member _.GetVersion() = document.Project - member _.GetLabel() = options.ProjectFileName } + { new ICacheKey<_, _> with + member _.GetKey() = document.Project.Id + member _.GetVersion() = document.Project + member _.GetLabel() = options.ProjectFileName + } snapshotCache.Get( key, async { let! ct = Async.CancellationToken return! getProjectSnapshot None document.Project ct |> Async.AwaitTask - }) + } + ) type FSharpChecker with @@ -463,15 +466,15 @@ type Project with if this.IsFastFindReferencesEnabled then do! documents - |> Seq.map (fun doc -> doc.FindFSharpReferencesAsync(symbol, projectSnapshot, (fun range -> onFound doc range), userOpName)) + |> Seq.map (fun doc -> + doc.FindFSharpReferencesAsync(symbol, projectSnapshot, (fun range -> onFound doc range), userOpName)) |> CancellableTask.whenAll else for doc in documents do do! doc.FindFSharpReferencesAsync(symbol, projectSnapshot, (onFound doc), userOpName) } - member this.GetFSharpCompilationOptionsAsync() = - this |> getFSharpOptionsForProject + member this.GetFSharpCompilationOptionsAsync() = this |> getFSharpOptionsForProject - member this.GetFSharpProjectSnapshot(?snapshotAccumulator) = - this |> getProjectSnapshot snapshotAccumulator \ No newline at end of file + member this.GetFSharpProjectSnapshot(?snapshotAccumulator) = + this |> getProjectSnapshot snapshotAccumulator diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index d6720cc4046..585cc161f1e 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -265,4 +265,5 @@ module EditorOptionsExtensions = member this.UseTransparentCompiler = this.EditorOptions.Advanced.UseTransparentCompiler - member this.TransparentCompilerCacheFactor = this.EditorOptions.LanguageServicePerformance.TransparentCompilerCacheFactor + member this.TransparentCompilerCacheFactor = + this.EditorOptions.LanguageServicePerformance.TransparentCompilerCacheFactor From 1a399230896413923b2eb4f1103ef88d19bba6ad Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 9 Oct 2023 17:40:55 +0200 Subject: [PATCH 128/222] move LruCache to separate file --- src/Compiler/FSharp.Compiler.Service.fsproj | 1 + src/Compiler/Facilities/AsyncMemoize.fs | 238 ----------------- src/Compiler/Service/TransparentCompiler.fs | 3 +- src/Compiler/Utilities/LruCache.fs | 247 ++++++++++++++++++ .../CompilerService/AsyncMemoize.fs | 1 - 5 files changed, 250 insertions(+), 240 deletions(-) create mode 100644 src/Compiler/Utilities/LruCache.fs diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index f4950034765..fb468a44cae 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -124,6 +124,7 @@ + diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 2c319b6287f..879c114e149 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -7,7 +7,6 @@ open System.IO open System.Threading open System.Threading.Tasks -open Internal.Utilities.Library.Extras open FSharp.Compiler.Diagnostics [] @@ -56,12 +55,6 @@ type internal Job<'TValue> = $"Running since {ts.ToShortTimeString()}{cancellation}" | Completed value -> $"Completed {value}" -type internal CacheEvent = - | Evicted - | Collected - | Weakened - | Strengthened - type internal JobEvent = | Started | Finished @@ -72,237 +65,6 @@ type internal JobEvent = | Strengthened | Failed -[] -type internal ValueLink<'T when 'T: not struct> = - | Strong of 'T - | Weak of WeakReference<'T> - -[] -type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality and 'TValue: not struct> - ( - keepStrongly, - ?keepWeakly, - ?requiredToKeep, - ?event - ) = - - let keepWeakly = defaultArg keepWeakly 100 - let requiredToKeep = defaultArg requiredToKeep (fun _ -> false) - let event = defaultArg event (fun _ _ -> ()) - - let dictionary = Dictionary<'TKey, Dictionary<'TVersion, _>>() - - // Lists to keep track of when items were last accessed. First item is most recently accessed. - let strongList = LinkedList<'TKey * 'TVersion * string * ValueLink<'TValue>>() - let weakList = LinkedList<'TKey * 'TVersion * string * ValueLink<'TValue>>() - - let rec removeCollected (node: LinkedListNode<_>) = - if node <> null then - let key, version, label, value = node.Value - - match value with - | Weak w -> - let next = node.Next - - match w.TryGetTarget() with - | false, _ -> - weakList.Remove node - dictionary[ key ].Remove version |> ignore - - if dictionary[key].Count = 0 then - dictionary.Remove key |> ignore - - event CacheEvent.Collected (label, key, version) - | _ -> () - - removeCollected next - | _ -> failwith "Illegal state, strong reference in weak list" - - let cutWeakListIfTooLong () = - if weakList.Count > keepWeakly then - removeCollected weakList.First - - let mutable node = weakList.Last - - while weakList.Count > keepWeakly && node <> null do - let previous = node.Previous - let key, version, label, _ = node.Value - weakList.Remove node - dictionary[ key ].Remove version |> ignore - - if dictionary[key].Count = 0 then - dictionary.Remove key |> ignore - - event CacheEvent.Evicted (label, key, version) - node <- previous - - let cutStrongListIfTooLong () = - let mutable node = strongList.Last - - while strongList.Count > keepStrongly && node <> null do - let previous = node.Previous - - match node.Value with - | _, _, _, Strong v when requiredToKeep v -> () - | key, version, label, Strong v -> - strongList.Remove node - node.Value <- key, version, label, Weak(WeakReference<_> v) - weakList.AddFirst node - event CacheEvent.Weakened (label, key, version) - | _key, _version, _label, _ -> failwith "Invalid state, weak reference in strong list" - - node <- previous - - cutWeakListIfTooLong () - - let pushNodeToTop (node: LinkedListNode<_>) = - match node.Value with - | _, _, _, Strong _ -> - strongList.AddFirst node - cutStrongListIfTooLong () - | _, _, _, Weak _ -> failwith "Invalid operation, pushing weak reference to strong list" - - let pushValueToTop key version label value = - let node = strongList.AddFirst(value = (key, version, label, Strong value)) - cutStrongListIfTooLong () - node - - member _.DebuggerDisplay = $"Cache(S:{strongList.Count} W:{weakList.Count})" - - member _.Set(key, version, label, value) = - match dictionary.TryGetValue key with - | true, versionDict -> - - if versionDict.ContainsKey version then - // TODO this is normal for unversioned cache; - // failwith "Suspicious - overwriting existing version" - - let node: LinkedListNode<_> = versionDict[version] - - match node.Value with - | _, _, _, Strong _ -> strongList.Remove node - | _, _, _, Weak _ -> - weakList.Remove node - event CacheEvent.Strengthened (label, key, version) - - node.Value <- key, version, label, Strong value - pushNodeToTop node - - else - let node = pushValueToTop key version label value - versionDict[version] <- node - // weaken all other versions (unless they're required to be kept) - for otherVersion in versionDict.Keys do - if otherVersion <> version then - let node = versionDict[otherVersion] - - match node.Value with - | _, _, _, Strong value when not (requiredToKeep value) -> - strongList.Remove node - node.Value <- key, otherVersion, label, Weak(WeakReference<_> value) - weakList.AddFirst node - event CacheEvent.Weakened (label, key, otherVersion) - cutWeakListIfTooLong () - | _ -> () - - | false, _ -> - let node = pushValueToTop key version label value - dictionary[key] <- Dictionary() - dictionary[key][version] <- node - - member this.Set(key, version, value) = - this.Set(key, version, "[no label]", value) - - member _.TryGet(key, version) = - - match dictionary.TryGetValue key with - | false, _ -> None - | true, versionDict -> - match versionDict.TryGetValue version with - | false, _ -> None - | true, node -> - match node.Value with - | _, _, _, Strong v -> - strongList.Remove node - pushNodeToTop node - Some v - - | _, _, label, Weak w -> - match w.TryGetTarget() with - | true, value -> - weakList.Remove node - let node = pushValueToTop key version label value - event CacheEvent.Strengthened (label, key, version) - versionDict[version] <- node - Some value - | _ -> - weakList.Remove node - versionDict.Remove version |> ignore - - if versionDict.Count = 0 then - dictionary.Remove key |> ignore - - event CacheEvent.Collected (label, key, version) - None - - /// Returns an option of a value for given key and version, and also a list of all other versions for given key - member this.GetAll(key, version) = - this.TryGet(key, version), - - match dictionary.TryGetValue key with - | false, _ -> [] - | true, versionDict -> - versionDict.Values - |> Seq.map (fun node -> node.Value) - |> Seq.filter (p24 >> ((<>) version)) - |> Seq.choose (function - | _, ver, _, Strong v -> Some(ver, v) - | _, ver, _, Weak r -> - match r.TryGetTarget() with - | true, x -> Some(ver, x) - | _ -> None) - |> Seq.toList - - member _.Remove(key, version) = - match dictionary.TryGetValue key with - | false, _ -> () - | true, versionDict -> - match versionDict.TryGetValue version with - | true, node -> - versionDict.Remove version |> ignore - - if versionDict.Count = 0 then - dictionary.Remove key |> ignore - - match node.Value with - | _, _, _, Strong _ -> strongList.Remove node - | _, _, _, Weak _ -> weakList.Remove node - | _ -> () - - member this.Set(key, value) = - this.Set(key, Unchecked.defaultof<_>, value) - - member this.TryGet(key) = - this.TryGet(key, Unchecked.defaultof<_>) - - member this.Remove(key) = - this.Remove(key, Unchecked.defaultof<_>) - - member _.Clear() = - dictionary.Clear() - strongList.Clear() - weakList.Clear() - - member _.GetValues() = - strongList - |> Seq.append weakList - |> Seq.choose (function - | _k, version, label, Strong value -> Some(label, version, value) - | _k, version, label, Weak w -> - match w.TryGetTarget() with - | true, value -> Some(label, version, value) - | _ -> None) - type internal ICacheKey<'TKey, 'TVersion> = abstract member GetKey: unit -> 'TKey abstract member GetVersion: unit -> 'TVersion diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index a1120d5f9db..59a757fa809 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -320,7 +320,7 @@ type internal TransparentCompiler let mutable caches = CompilerCaches(100) // TODO: do we need this? - let maxTypeCheckingParallelism = Environment.ProcessorCount + let maxTypeCheckingParallelism = max 1 (Environment.ProcessorCount / 2) let maxParallelismSemaphore = new SemaphoreSlim(maxTypeCheckingParallelism) // We currently share one global dependency provider for all scripts for the FSharpChecker. @@ -1179,6 +1179,7 @@ type internal TransparentCompiler tcInfo.stateContainsNodes |> Set.contains (NodeToTypeCheck.ArtificialImplFile(index - 1)) then + // TODO: this can actually happen, probably related to adding new files or moving files around failwith $"Oops???" let partialResult, tcState = finisher tcInfo.tcState diff --git a/src/Compiler/Utilities/LruCache.fs b/src/Compiler/Utilities/LruCache.fs new file mode 100644 index 00000000000..ab668b3a7ef --- /dev/null +++ b/src/Compiler/Utilities/LruCache.fs @@ -0,0 +1,247 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Internal.Utilities.Collections + +open System +open System.Collections.Generic +open System.Diagnostics + +open Internal.Utilities.Library.Extras + +[] +type internal CacheEvent = + | Evicted + | Collected + | Weakened + | Strengthened + +[] +type internal ValueLink<'T when 'T: not struct> = + | Strong of 'T + | Weak of WeakReference<'T> + +[] +type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality and 'TValue: not struct> + ( + keepStrongly, + ?keepWeakly, + ?requiredToKeep, + ?event + ) = + + let keepWeakly = defaultArg keepWeakly 100 + let requiredToKeep = defaultArg requiredToKeep (fun _ -> false) + let event = defaultArg event (fun _ _ -> ()) + + let dictionary = Dictionary<'TKey, Dictionary<'TVersion, _>>() + + // Lists to keep track of when items were last accessed. First item is most recently accessed. + let strongList = LinkedList<'TKey * 'TVersion * string * ValueLink<'TValue>>() + let weakList = LinkedList<'TKey * 'TVersion * string * ValueLink<'TValue>>() + + let rec removeCollected (node: LinkedListNode<_>) = + if node <> null then + let key, version, label, value = node.Value + + match value with + | Weak w -> + let next = node.Next + + match w.TryGetTarget() with + | false, _ -> + weakList.Remove node + dictionary[ key ].Remove version |> ignore + + if dictionary[key].Count = 0 then + dictionary.Remove key |> ignore + + event CacheEvent.Collected (label, key, version) + | _ -> () + + removeCollected next + | _ -> failwith "Illegal state, strong reference in weak list" + + let cutWeakListIfTooLong () = + if weakList.Count > keepWeakly then + removeCollected weakList.First + + let mutable node = weakList.Last + + while weakList.Count > keepWeakly && node <> null do + let previous = node.Previous + let key, version, label, _ = node.Value + weakList.Remove node + dictionary[ key ].Remove version |> ignore + + if dictionary[key].Count = 0 then + dictionary.Remove key |> ignore + + event CacheEvent.Evicted (label, key, version) + node <- previous + + let cutStrongListIfTooLong () = + let mutable node = strongList.Last + + while strongList.Count > keepStrongly && node <> null do + let previous = node.Previous + + match node.Value with + | _, _, _, Strong v when requiredToKeep v -> () + | key, version, label, Strong v -> + strongList.Remove node + node.Value <- key, version, label, Weak(WeakReference<_> v) + weakList.AddFirst node + event CacheEvent.Weakened (label, key, version) + | _key, _version, _label, _ -> failwith "Invalid state, weak reference in strong list" + + node <- previous + + cutWeakListIfTooLong () + + let pushNodeToTop (node: LinkedListNode<_>) = + match node.Value with + | _, _, _, Strong _ -> + strongList.AddFirst node + cutStrongListIfTooLong () + | _, _, _, Weak _ -> failwith "Invalid operation, pushing weak reference to strong list" + + let pushValueToTop key version label value = + let node = strongList.AddFirst(value = (key, version, label, Strong value)) + cutStrongListIfTooLong () + node + + member _.DebuggerDisplay = $"Cache(S:{strongList.Count} W:{weakList.Count})" + + member _.Set(key, version, label, value) = + match dictionary.TryGetValue key with + | true, versionDict -> + + if versionDict.ContainsKey version then + // TODO this is normal for unversioned cache; + // failwith "Suspicious - overwriting existing version" + + let node: LinkedListNode<_> = versionDict[version] + + match node.Value with + | _, _, _, Strong _ -> strongList.Remove node + | _, _, _, Weak _ -> + weakList.Remove node + event CacheEvent.Strengthened (label, key, version) + + node.Value <- key, version, label, Strong value + pushNodeToTop node + + else + let node = pushValueToTop key version label value + versionDict[version] <- node + // weaken all other versions (unless they're required to be kept) + for otherVersion in versionDict.Keys do + if otherVersion <> version then + let node = versionDict[otherVersion] + + match node.Value with + | _, _, _, Strong value when not (requiredToKeep value) -> + strongList.Remove node + node.Value <- key, otherVersion, label, Weak(WeakReference<_> value) + weakList.AddFirst node + event CacheEvent.Weakened (label, key, otherVersion) + cutWeakListIfTooLong () + | _ -> () + + | false, _ -> + let node = pushValueToTop key version label value + dictionary[key] <- Dictionary() + dictionary[key][version] <- node + + member this.Set(key, version, value) = + this.Set(key, version, "[no label]", value) + + member _.TryGet(key, version) = + + match dictionary.TryGetValue key with + | false, _ -> None + | true, versionDict -> + match versionDict.TryGetValue version with + | false, _ -> None + | true, node -> + match node.Value with + | _, _, _, Strong v -> + strongList.Remove node + pushNodeToTop node + Some v + + | _, _, label, Weak w -> + match w.TryGetTarget() with + | true, value -> + weakList.Remove node + let node = pushValueToTop key version label value + event CacheEvent.Strengthened (label, key, version) + versionDict[version] <- node + Some value + | _ -> + weakList.Remove node + versionDict.Remove version |> ignore + + if versionDict.Count = 0 then + dictionary.Remove key |> ignore + + event CacheEvent.Collected (label, key, version) + None + + /// Returns an option of a value for given key and version, and also a list of all other versions for given key + member this.GetAll(key, version) = + this.TryGet(key, version), + + match dictionary.TryGetValue key with + | false, _ -> [] + | true, versionDict -> + versionDict.Values + |> Seq.map (fun node -> node.Value) + |> Seq.filter (p24 >> ((<>) version)) + |> Seq.choose (function + | _, ver, _, Strong v -> Some(ver, v) + | _, ver, _, Weak r -> + match r.TryGetTarget() with + | true, x -> Some(ver, x) + | _ -> None) + |> Seq.toList + + member _.Remove(key, version) = + match dictionary.TryGetValue key with + | false, _ -> () + | true, versionDict -> + match versionDict.TryGetValue version with + | true, node -> + versionDict.Remove version |> ignore + + if versionDict.Count = 0 then + dictionary.Remove key |> ignore + + match node.Value with + | _, _, _, Strong _ -> strongList.Remove node + | _, _, _, Weak _ -> weakList.Remove node + | _ -> () + + member this.Set(key, value) = + this.Set(key, Unchecked.defaultof<_>, value) + + member this.TryGet(key) = + this.TryGet(key, Unchecked.defaultof<_>) + + member this.Remove(key) = + this.Remove(key, Unchecked.defaultof<_>) + + member _.Clear() = + dictionary.Clear() + strongList.Clear() + weakList.Clear() + + member _.GetValues() = + strongList + |> Seq.append weakList + |> Seq.choose (function + | _k, version, label, Strong value -> Some(label, version, value) + | _k, version, label, Weak w -> + match w.TryGetTarget() with + | true, value -> Some(label, version, value) + | _ -> None) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 627edadd5e4..c505e5da091 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -3,7 +3,6 @@ module CompilerService.AsyncMemoize open System open System.Threading open Xunit -open FSharp.Test open Internal.Utilities.Collections open System.Threading.Tasks open System.Diagnostics From 0958b35351518695abd5ec51cf5379966128a714 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 9 Oct 2023 17:55:38 +0200 Subject: [PATCH 129/222] re-enable hashed versions --- src/Compiler/Service/FSharpCheckerResults.fs | 47 +++++++++++-------- src/Compiler/Service/FSharpCheckerResults.fsi | 4 +- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 06db489eb9f..30d4feb340a 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -307,17 +307,18 @@ type FSharpProjectSnapshot = // TODO: DateTime.Now - member this.GetMd5Version() = Md5Hasher.empty - //|> Md5Hasher.addString this.ProjectFileName - //|> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) - //|> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) - //|> Md5Hasher.addStrings this.OtherOptions - //|> Md5Hasher.addVersions ( - // this.ReferencedProjects - // |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key) - //) - //|> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment - //|> Md5Hasher.addBool this.UseScriptResolutionRules + member this.GetMd5Version() = + Md5Hasher.empty + |> Md5Hasher.addString this.ProjectFileName + |> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) + |> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) + |> Md5Hasher.addStrings this.OtherOptions + |> Md5Hasher.addVersions ( + this.ReferencedProjects + |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key) + ) + |> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment + |> Md5Hasher.addBool this.UseScriptResolutionRules member this.GetDebugVersion() : FSharpProjectSnapshotDebugVersion = { @@ -333,13 +334,13 @@ type FSharpProjectSnapshot = UseScriptResolutionRules = this.UseScriptResolutionRules } - interface ICacheKey with + interface ICacheKey with member this.GetLabel() = this.ToString() member this.GetKey() = this.ProjectFileName, this.OutputFileName |> Option.defaultValue "" - member this.GetVersion() = this.GetDebugVersion() + member this.GetVersion() = this.GetMd5Version() and FSharpProjectSnapshotWithSources = { @@ -408,15 +409,21 @@ and FSharpProjectSnapshotWithSources = .WithoutImplFilesThatHaveSignaturesExceptLastOne.Key.GetVersion() } + member this.GetDebugVersion() = + { + ProjectSnapshotVersion = this.ProjectSnapshot.WithoutFileVersions.GetDebugVersion() + SourceVersions = this.SourceFiles |> List.map (fun x -> x.SourceHash) + } + + member this.GetMd5Version() = + this.ProjectSnapshot.GetMd5Version() + |> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.SourceHash)) + interface ICacheKey with member this.GetLabel() = this.ProjectSnapshot.Key.ToString() member this.GetKey() = this.ProjectSnapshot.Key.GetKey() - member this.GetVersion() = - { - ProjectSnapshotVersion = this.ProjectSnapshot.WithoutFileVersions.Key.GetVersion() - SourceVersions = this.SourceFiles |> List.map (fun x -> x.SourceHash) - } + member this.GetVersion() = this.GetMd5Version() and FSharpProjectSnapshotWithSourcesDebugVersion = { @@ -424,7 +431,7 @@ and FSharpProjectSnapshotWithSourcesDebugVersion = SourceVersions: string list } -and FSharpProjectSnapshotWithSourcesVersion = FSharpProjectSnapshotWithSourcesDebugVersion +and FSharpProjectSnapshotWithSourcesVersion = string and FSharpProjectSnapshotDebugVersion = { @@ -437,7 +444,7 @@ and FSharpProjectSnapshotDebugVersion = UseScriptResolutionRules: bool } -and FSharpProjectSnapshotVersion = FSharpProjectSnapshotDebugVersion +and FSharpProjectSnapshotVersion = string and [] public FSharpReferencedProjectSnapshot = internal diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 06aaf75974e..5744a152456 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -184,7 +184,7 @@ and FSharpProjectSnapshotWithSourcesDebugVersion = { ProjectSnapshotVersion: FSharpProjectSnapshotDebugVersion SourceVersions: string list } -and FSharpProjectSnapshotWithSourcesVersion = FSharpProjectSnapshotWithSourcesDebugVersion +and FSharpProjectSnapshotWithSourcesVersion = string and FSharpProjectSnapshotDebugVersion = { ProjectFileName: string @@ -195,7 +195,7 @@ and FSharpProjectSnapshotDebugVersion = IsIncompleteTypeCheckEnvironment: bool UseScriptResolutionRules: bool } -and FSharpProjectSnapshotVersion = FSharpProjectSnapshotDebugVersion +and FSharpProjectSnapshotVersion = string and [] public FSharpReferencedProjectSnapshot = internal From 5da210c19199bdcada9750cde06af5377f08f1ed Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 13 Nov 2023 13:49:39 +0100 Subject: [PATCH 130/222] Fixed cache bug --- src/Compiler/Facilities/AsyncMemoize.fs | 10 ++++++- src/Compiler/Utilities/LruCache.fs | 25 +++++++++--------- .../CompilerService/LruCache.fs | 26 +++++++++++++++++-- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 879c114e149..62548e00aba 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -156,6 +156,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let mutable failed = 0 let mutable evicted = 0 let mutable collected = 0 + let mutable strengthened = 0 let failures = ResizeArray() let mutable avgDurationMs = 0.0 @@ -179,7 +180,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T Interlocked.Increment &collected |> ignore event.Trigger(JobEvent.Collected, k)) | CacheEvent.Weakened -> (fun k -> event.Trigger(JobEvent.Weakened, k)) - | CacheEvent.Strengthened -> (fun k -> event.Trigger(JobEvent.Strengthened, k))) + | CacheEvent.Strengthened -> + (fun k -> + Interlocked.Increment &strengthened |> ignore + event.Trigger(JobEvent.Strengthened, k))) ) let requestCounts = Dictionary, int>() @@ -477,6 +481,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T if failed > 0 then $"| failed: {failed} " else "" if evicted > 0 then $"| evicted: {evicted} " else "" if collected > 0 then $"| collected: {collected} " else "" + if strengthened > 0 then + $"| strengthened: {strengthened} " + else + "" |] |> String.concat "" diff --git a/src/Compiler/Utilities/LruCache.fs b/src/Compiler/Utilities/LruCache.fs index ab668b3a7ef..1dfa3e65780 100644 --- a/src/Compiler/Utilities/LruCache.fs +++ b/src/Compiler/Utilities/LruCache.fs @@ -135,18 +135,19 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers let node = pushValueToTop key version label value versionDict[version] <- node // weaken all other versions (unless they're required to be kept) - for otherVersion in versionDict.Keys do - if otherVersion <> version then - let node = versionDict[otherVersion] - - match node.Value with - | _, _, _, Strong value when not (requiredToKeep value) -> - strongList.Remove node - node.Value <- key, otherVersion, label, Weak(WeakReference<_> value) - weakList.AddFirst node - event CacheEvent.Weakened (label, key, otherVersion) - cutWeakListIfTooLong () - | _ -> () + let versionsToWeaken = versionDict.Keys |> Seq.filter ((<>) version) |> Seq.toList + + for otherVersion in versionsToWeaken do + let node = versionDict[otherVersion] + + match node.Value with + | _, _, _, Strong value when not (requiredToKeep value) -> + strongList.Remove node + node.Value <- key, otherVersion, label, Weak(WeakReference<_> value) + weakList.AddFirst node + event CacheEvent.Weakened (label, key, otherVersion) + cutWeakListIfTooLong () + | _ -> () | false, _ -> let node = pushValueToTop key version label value diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs index 00bcd48d61d..9fef3a32606 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs @@ -3,8 +3,6 @@ open Internal.Utilities.Collections open Xunit open System -open System.Threading -open System.Runtime.CompilerServices [] let ``Adding an item to the cache should make it retrievable``() = @@ -115,3 +113,27 @@ let ``When a new version is added other versions don't get weakened when they're ] Assert.Equal>(expected, eventLog |> Seq.toList) + +[] +let ``Looking up a weakened item will strengthen it`` () = + let eventLog = ResizeArray() + + let cache = new LruCache<_, int, _>(keepStrongly = 2, keepWeakly = 2, event = (fun e v -> eventLog.Add(e, v))) + + cache.Set(1, 1, "one1") + cache.Set(1, 2, "one2") + cache.Set(1, 3, "one3") + cache.Set(1, 4, "one4") + + let result = cache.TryGet(1, 2) + Assert.Equal("one2", result.Value) + + let expected = [ + CacheEvent.Weakened, ("[no label]", 1, 1) + CacheEvent.Weakened, ("[no label]", 1, 2) + CacheEvent.Weakened, ("[no label]", 1, 3) + CacheEvent.Evicted, ("[no label]", 1, 1) + CacheEvent.Strengthened, ("[no label]", 1, 2) + ] + + Assert.Equal>(expected, eventLog |> Seq.toList) From 8772573e4a4a170233bb5720512b8d08aa96427f Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 15 Nov 2023 14:55:10 +0100 Subject: [PATCH 131/222] Fix finding references testing --- tests/FSharp.Test.Utilities/ProjectGeneration.fs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index ed8838e24a0..c5b75165b6e 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -1130,7 +1130,10 @@ type ProjectWorkflowBuilder [ for p, f in ctx.Project.GetAllFiles() do let options = p.GetProjectOptions checker for fileName in [getFilePath p f; if f.SignatureFile <> No then getSignatureFilePath p f] do - checker.FindBackgroundReferencesInFile(fileName, options, symbolUse.Symbol, fastCheck = true) ] + async { + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot ctx.Project) + return! checker.FindBackgroundReferencesInFile(fileName, snapshot, symbolUse.Symbol) + } ] |> Async.Parallel results |> Seq.collect id |> Seq.toList |> processResults From 708e679b3c0d401065231e1dcd5c1d432ea91037 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 15 Nov 2023 15:59:09 +0100 Subject: [PATCH 132/222] added cache hit ratio, symbol env for diagnostics --- src/Compiler/Facilities/AsyncMemoize.fs | 8 ++- src/Compiler/Service/TransparentCompiler.fs | 54 +++++++++++---------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 62548e00aba..94da5d598bf 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -470,10 +470,16 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let avgDuration = avgDurationMs |> sprintf "| Avg: %.0f ms" + let hitRatio = + if started > 0 then + $" (%.0f{float hits / (float (started + hits)) * 100.0} %%)" + else + "" + let stats = [| if errors > 0 then $"| errors: {errors} " else "" - if hits > 0 then $"| hits: {hits} " else "" + $"| hits: {hits}{hitRatio} " if started > 0 then $"| started: {started} " else "" if completed > 0 then $"| completed: {completed} " else "" if canceled > 0 then $"| canceled: {canceled} " else "" diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 59a757fa809..0ef0ca716a6 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -281,7 +281,7 @@ type internal CompilerCaches(sizeFactor: int) = member val BootstrapInfo = AsyncMemoize(sf, 2 * sf, name = "BootstrapInfo") - // member val TcLastFile = AsyncMemoize(name = "TcLastFile") + member val TcLastFile = AsyncMemoize(sf, 2 * sf, name = "TcLastFile") member val TcIntermediate = AsyncMemoize(20 * sf, 20 * sf, name = "TcIntermediate") @@ -1262,35 +1262,35 @@ type internal TransparentCompiler // Type check file and all its dependencies let ComputeTcLastFile (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = - //let fileName = projectSnapshot.SourceFiles |> List.last |> (fun f -> f.FileName) - //caches.TcLastFile.Get( - // projectSnapshot.FileKey fileName, - async { - let file = projectSnapshot.SourceFiles |> List.last + let fileName = projectSnapshot.SourceFiles |> List.last |> (fun f -> f.FileName) + caches.TcLastFile.Get( + projectSnapshot.FileKey fileName, + async { + let file = projectSnapshot.SourceFiles |> List.last - use _ = - Activity.start "ComputeTcLastFile" [| Activity.Tags.fileName, file.FileName |> Path.GetFileName |] + use _ = + Activity.start "ComputeTcLastFile" [| Activity.Tags.fileName, file.FileName |> Path.GetFileName |] - let! parsedInputs = - projectSnapshot.SourceFiles - |> Seq.map (ComputeParseFile projectSnapshot.ProjectSnapshot) - |> Async.Parallel + let! parsedInputs = + projectSnapshot.SourceFiles + |> Seq.map (ComputeParseFile projectSnapshot.ProjectSnapshot) + |> Async.Parallel - let! graph, dependencyFiles = - ComputeDependencyGraphForFile bootstrapInfo.TcConfig projectSnapshot (parsedInputs |> Array.map p14) - //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) + let! graph, dependencyFiles = + ComputeDependencyGraphForFile bootstrapInfo.TcConfig projectSnapshot (parsedInputs |> Array.map p14) + //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) - let! results, tcInfo = - processTypeCheckingGraph - graph - (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles false) - bootstrapInfo.InitialTcInfo + let! results, tcInfo = + processTypeCheckingGraph + graph + (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles false) + bootstrapInfo.InitialTcInfo - let lastResult = results |> List.head |> snd + let lastResult = results |> List.head |> snd - return lastResult, tcInfo - } - //) + return lastResult, tcInfo + } + ) let getParseResult (projectSnapshot: FSharpProjectSnapshot) creationDiags file = async { @@ -1591,6 +1591,10 @@ type internal TransparentCompiler let tcDiagnostics = tcInfo.TcDiagnostics let tcDependencyFiles = tcInfo.tcDependencyFiles + let symbolEnv = + SymbolEnv(bootstrapInfo.TcGlobals, tcInfo.tcState.Ccu, Some tcInfo.tcState.CcuSig, bootstrapInfo.TcImports) + |> Some + let tcDiagnostics = DiagnosticHelpers.CreateDiagnostics( diagnosticsOptions, @@ -1599,7 +1603,7 @@ type internal TransparentCompiler tcDiagnostics, suggestNamesForErrors, bootstrapInfo.TcConfig.flatErrors, - None + symbolEnv ) let diagnostics = [| yield! creationDiags; yield! tcDiagnostics |] From 0b2897d6582648e7fe3bfae9994a7fbad35b3ccf Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 15 Nov 2023 17:12:50 +0100 Subject: [PATCH 133/222] test todos --- .../FSharpChecker/CommonWorkflows.fs | 9 +++++---- .../FSharpChecker/TransparentCompiler.fs | 7 +++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs index ce9846a0ee5..3fc6d6c15e4 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs @@ -101,12 +101,13 @@ let ``Changes in a referenced project`` () = } [] -let ``Language service works if the same file is listed twice`` () = +// TODO: This will probably require some special care in TransparentCompiler... +let ``Language service works if the same file is listed twice`` () = let file = sourceFile "First" [] - let project = SyntheticProject.Create(file) + let project = SyntheticProject.Create(file, file) project.Workflow { - checkFile "First" expectOk - addFileAbove "First" file + // checkFile "First" expectOk + // addFileAbove "First" file checkFile "First" (expectSingleWarningAndNoErrors "Please verify that it is included only once in the project file.") } diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 6d07475d954..4b3e88a19b0 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -261,8 +261,7 @@ let ``If a file is checked as a dependency it's not re-checked later`` () = Assert.Equal([Weakened; Started; Finished], intermediateTypeChecks["FileThird.fs"]) - -[] +// [] TODO: differentiate complete and minimal checking requests let ``We don't check files that are not depended on`` () = let project = SyntheticProject.Create( sourceFile "First" [], @@ -292,7 +291,7 @@ let ``We don't check files that are not depended on`` () = Assert.Equal([Started; Finished], intermediateTypeChecks["FileThird.fs"]) Assert.False (intermediateTypeChecks.ContainsKey "FileSecond.fs") -[] +// [] TODO: differentiate complete and minimal checking requests let ``Files that are not depended on don't invalidate cache`` () = let project = SyntheticProject.Create( sourceFile "First" [], @@ -333,7 +332,7 @@ let ``Files that are not depended on don't invalidate cache`` () = Assert.Equal([], intermediateTypeChecks |> Map.toList) -[] +// [] TODO: differentiate complete and minimal checking requests let ``Files that are not depended on don't invalidate cache part 2`` () = let project = SyntheticProject.Create( sourceFile "A" [], From a2c14b89debd1f955f66fcb4f8a368a882dc870d Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 15 Nov 2023 19:58:21 +0100 Subject: [PATCH 134/222] enabling some tests --- src/Compiler/Service/TransparentCompiler.fs | 16 +- tests/service/Common.fs | 4 +- tests/service/MultiProjectAnalysisTests.fs | 50 ++--- tests/service/ProjectAnalysisTests.fs | 197 ++++++++++---------- 4 files changed, 140 insertions(+), 127 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 0ef0ca716a6..d00a06d8909 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -769,7 +769,18 @@ type internal TransparentCompiler fileSnapshots.TryFind fileName |> Option.defaultWith (fun () -> // TODO: does this commonly happen? - failwith "Let's find out..." + + // It can happen when source files are inferred from command line options and are not part of FSharpProjectOptions.SourceFiles - which we use to create the Snapshot + + let snapshotFileSummary = + match projectSnapshot.SourceFiles with + | [] -> "The project snapshot has no source files." + | files -> + "The project snapshot contains the following files:\n" + + (files |> Seq.map (fun x -> x.FileName |> shortPath) |> String.concat "\n") + + failwith + $"Trying to check a file ({shortPath fileName}) which is not part of the project snapshot. {snapshotFileSummary}" { FileName = fileName @@ -1263,6 +1274,7 @@ type internal TransparentCompiler // Type check file and all its dependencies let ComputeTcLastFile (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = let fileName = projectSnapshot.SourceFiles |> List.last |> (fun f -> f.FileName) + caches.TcLastFile.Get( projectSnapshot.FileKey fileName, async { @@ -1290,7 +1302,7 @@ type internal TransparentCompiler return lastResult, tcInfo } - ) + ) let getParseResult (projectSnapshot: FSharpProjectSnapshot) creationDiags file = async { diff --git a/tests/service/Common.fs b/tests/service/Common.fs index ca72dabedad..6912dd12b31 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -138,7 +138,7 @@ let mkTestFileAndOptions source additionalArgs = FileSystem.OpenFileForWriteShim(fileName).Write(fileSource1) let args = Array.append (mkProjectCommandLineArgs (dllName, [fileName])) additionalArgs - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = [| fileName |] } fileName, options let parseAndCheckFile fileName source options = @@ -160,7 +160,7 @@ let parseAndCheckScriptWithOptions (file:string, input, opts) = let projName = Path.ChangeExtension(fname, ".fsproj") let args = mkProjectCommandLineArgsForScript (dllName, [file]) printfn "file = %A, args = %A" file args - checker.GetProjectOptionsFromCommandLineArgs (projName, args) + { checker.GetProjectOptionsFromCommandLineArgs (projName, args) with SourceFiles = [| file |] } finally if Directory.Exists(path) then diff --git a/tests/service/MultiProjectAnalysisTests.fs b/tests/service/MultiProjectAnalysisTests.fs index 52c294c0ad8..9433e7393ae 100644 --- a/tests/service/MultiProjectAnalysisTests.fs +++ b/tests/service/MultiProjectAnalysisTests.fs @@ -68,9 +68,9 @@ type U = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -96,9 +96,9 @@ let x = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } // A project referencing two sub-projects @@ -121,11 +121,12 @@ let u = Case1 3 """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) let options = - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with + SourceFiles = fileNames OtherOptions = Array.append options.OtherOptions [| ("-r:" + Project1A.dllName); ("-r:" + Project1B.dllName) |] ReferencedProjects = [| FSharpReferencedProject.FSharpReference(Project1A.dllName, Project1A.options); FSharpReferencedProject.FSharpReference(Project1B.dllName, Project1B.options); |] } @@ -293,9 +294,9 @@ let p = C.Print() let baseName = tryCreateTemporaryFileName () let dllName = Path.ChangeExtension(baseName, ".dll") let projFileName = Path.ChangeExtension(baseName, ".fsproj") - let fileNames = [fileName1 ] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } yield { ModuleName = moduleName; FileName=fileName1; Options = options; DllName=dllName } ] let jointProject = @@ -314,10 +315,10 @@ let p = (""" + String.concat ",\r\n " [ for p in projects -> p.ModuleName + ".v" ] + ")" FileSystem.OpenFileForWriteShim(fileName).Write(fileSource) - let fileNames = [fileName] + let fileNames = [|fileName|] let args = mkProjectCommandLineArgs (dllName, fileNames) let options = - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with OtherOptions = Array.append options.OtherOptions [| for p in projects -> ("-r:" + p.DllName) |] ReferencedProjects = [| for p in projects -> FSharpReferencedProject.FSharpReference(p.DllName, p.Options); |] } @@ -412,11 +413,11 @@ let x = "F#" let cleanFileName a = if a = fileName1 then "Project1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let getOptions() = let args = mkProjectCommandLineArgs (dllName, fileNames) - checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } module internal MultiProjectDirty2 = @@ -437,11 +438,11 @@ let z = Project1.x let cleanFileName a = if a = fileName1 then "Project2" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let getOptions() = let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with OtherOptions = Array.append options.OtherOptions [| ("-r:" + MultiProjectDirty1.dllName) |] ReferencedProjects = [| FSharpReferencedProject.FSharpReference(MultiProjectDirty1.dllName, MultiProjectDirty1.getOptions()) |] } @@ -633,9 +634,9 @@ type C() = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } //Project2A.fileSource1 // A project referencing Project2A @@ -652,10 +653,10 @@ let v = Project2A.C().InternalMember // access an internal symbol """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) let options = - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with OtherOptions = Array.append options.OtherOptions [| ("-r:" + Project2A.dllName); |] ReferencedProjects = [| FSharpReferencedProject.FSharpReference(Project2A.dllName, Project2A.options); |] } @@ -676,10 +677,10 @@ let v = Project2A.C().InternalMember // access an internal symbol """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) let options = - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with OtherOptions = Array.append options.OtherOptions [| ("-r:" + Project2A.dllName); |] ReferencedProjects = [| FSharpReferencedProject.FSharpReference(Project2A.dllName, Project2A.options); |] } @@ -748,9 +749,9 @@ let (|DivisibleBy|_|) by n = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } // A project referencing a sub-project @@ -773,11 +774,12 @@ let fizzBuzz = function """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) let options = - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with + SourceFiles = fileNames OtherOptions = Array.append options.OtherOptions [| ("-r:" + Project3A.dllName) |] ReferencedProjects = [| FSharpReferencedProject.FSharpReference(Project3A.dllName, Project3A.options) |] } let cleanFileName a = if a = fileName1 then "file1" else "??" diff --git a/tests/service/ProjectAnalysisTests.fs b/tests/service/ProjectAnalysisTests.fs index 3ca3582deef..7cb3bc42f9f 100644 --- a/tests/service/ProjectAnalysisTests.fs +++ b/tests/service/ProjectAnalysisTests.fs @@ -91,7 +91,7 @@ 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) with SourceFiles = fileNames |> List.toArray } let parsingOptions, _ = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) let cleanFileName a = if a = fileName1 then "file1" else if a = fileName2 then "file2" else "??" @@ -675,9 +675,9 @@ let _ = GenericFunction(3, 4) """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -917,9 +917,9 @@ let getM (foo: IFoo) = foo.InterfaceMethod("d") """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -1286,9 +1286,9 @@ let inline twice(x : ^U, y : ^U) = x + y """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -1460,9 +1460,9 @@ let parseNumeric str = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -1672,9 +1672,9 @@ let f () = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -1728,9 +1728,9 @@ let x2 = C.M(arg1 = 3, arg2 = 4, ?arg3 = Some 5) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -1789,9 +1789,9 @@ let x = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -1869,9 +1869,9 @@ let inline check< ^T when ^T : (static member IsInfinity : ^T -> bool)> (num: ^T let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -1948,9 +1948,9 @@ C.M("http://goo", query = 1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2028,9 +2028,9 @@ let fff (x:System.Collections.Generic.Dictionary.Enumerator) = () let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2097,9 +2097,9 @@ let x2 = query { for i in 0 .. 100 do let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2164,9 +2164,9 @@ let x3 = new System.DateTime() let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2323,9 +2323,9 @@ let x2 = S(3) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2390,9 +2390,9 @@ let f x = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2477,9 +2477,9 @@ and G = Case1 | Case2 of int FileSystem.OpenFileForWriteShim(sigFileName1).Write(sigFileSource1Text) let cleanFileName a = if a = fileName1 then "file1" elif a = sigFileName1 then "sig1" else "??" - let fileNames = [sigFileName1; fileName1] + let fileNames = [|sigFileName1; fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2743,9 +2743,9 @@ let f3 (x: System.Exception) = x.HelpLink <- "" // check use of .NET setter prop FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2830,9 +2830,9 @@ let _ = list<_>.Empty FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2886,9 +2886,9 @@ let s = System.DayOfWeek.Monday FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -2961,9 +2961,9 @@ type A<'T>() = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -3022,9 +3022,9 @@ let _ = { new IMyInterface with FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -3096,9 +3096,9 @@ let f5 (x: int[,,]) = () // test a multi-dimensional array FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -3243,9 +3243,9 @@ module Setter = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test Project23 whole project errors`` () = @@ -3414,9 +3414,9 @@ TypeWithProperties.StaticAutoPropGetSet <- 3 FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test Project24 whole project errors`` () = @@ -3683,12 +3683,12 @@ let _ = XmlProvider<"13">.GetSample() FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = [| yield! mkProjectCommandLineArgs (dllName, fileNames) yield @"-r:" + Path.Combine(__SOURCE_DIRECTORY__, Path.Combine("data", "FSharp.Data.dll")) yield @"-r:" + sysLib "System.Xml.Linq" |] - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] #if NETCOREAPP @@ -3823,9 +3823,9 @@ type Class() = let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -3913,9 +3913,9 @@ type CFooImpl() = """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test project27 whole project errors`` () = @@ -3978,9 +3978,9 @@ type Use() = """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } #if !NO_TYPEPROVIDERS [] let ``Test project28 all symbols in signature`` () = @@ -4056,9 +4056,9 @@ let f (x: INotifyPropertyChanged) = failwith "" """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -4115,9 +4115,9 @@ type T() = """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let ``Test project30 whole project errors`` () = @@ -4175,10 +4175,9 @@ let g = Console.ReadKey() """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let ``Test project31 whole project errors`` () = let wholeProjectResults = checker.ParseAndCheckProject(Project31.options) |> Async.RunImmediate @@ -4318,9 +4317,9 @@ val func : int -> int FileSystem.OpenFileForWriteShim(sigFileName1).Write(sigFileSource1) let cleanFileName a = if a = fileName1 then "file1" elif a = sigFileName1 then "sig1" else "??" - let fileNames = [sigFileName1; fileName1] + let fileNames = [|sigFileName1; fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -4386,9 +4385,9 @@ type System.Int32 with FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test Project33 whole project errors`` () = @@ -4425,7 +4424,7 @@ module internal Project34 = FileSystem.OpenFileForWriteShim(sourceFileName).Write(fileSource) let cleanFileName a = if a = sourceFileName then "file1" else "??" - let fileNames = [sourceFileName] + let fileNames = [|sourceFileName|] let args = [| yield! mkProjectCommandLineArgs (dllName, fileNames) @@ -4435,7 +4434,7 @@ module internal Project34 = |] |> Array.filter(fun arg -> not((arg.Contains("System.Data")) && not (arg.Contains(@"service\data\System.Data.dll")))) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test Project34 whole project errors`` () = @@ -4503,9 +4502,9 @@ type Test = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -4575,13 +4574,13 @@ module internal Project35b = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] + let fileNames = [|fileName1|] #if NETCOREAPP let projPath = Path.ChangeExtension(fileName1, ".fsproj") let dllPath = Path.ChangeExtension(fileName1, ".dll") let args = mkProjectCommandLineArgs(dllPath, fileNames) let args2 = Array.append args [| "-r:notexist.dll" |] - let options = checker.GetProjectOptionsFromCommandLineArgs (projPath, args2) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projPath, args2) with SourceFiles = fileNames } #else let options = checker.GetProjectOptionsFromScript(fileName1, fileSource1) |> Async.RunImmediate |> fst #endif @@ -4767,9 +4766,9 @@ namespace AttrTests do () """ FileSystem.OpenFileForWriteShim(fileName2).Write(fileSource2) - let fileNames = [fileName1; fileName2] + let fileNames = [|fileName1; fileName2|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test project37 typeof and arrays in attribute constructor arguments`` () = @@ -4909,9 +4908,9 @@ type A<'XX, 'YY>() = member this.Property = 1 """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test project38 abstract slot information`` () = @@ -4995,9 +4994,9 @@ let uses () = C().CurriedMemberWithIncompleteSignature (failwith "x1") (failwith "x2") (failwith "x3", failwith "x4") """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let cleanFileName a = if a = fileName1 then "file1" else "??" [] @@ -5070,9 +5069,9 @@ let g (x: C) = x.IsItAnA,x.IsItAnAMethod() """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let cleanFileName a = if a = fileName1 then "file1" else "??" [] @@ -5141,9 +5140,9 @@ module M if true then Foo else Bar """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let cleanFileName a = if a = fileName1 then "file1" else "??" [] @@ -5233,9 +5232,9 @@ open File1 let test2() = test() """ FileSystem.OpenFileForWriteShim(fileName2).Write(fileSource2) - let fileNames = [fileName1;fileName2] + let fileNames = [|fileName1;fileName2|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test project42 to ensure cached checked results are invalidated`` () = @@ -5278,11 +5277,11 @@ module internal ProjectBig = // Simplified repro for https://github.com/dotnet/fsharp/issues/2679 let ``add files with same name from different folders`` () = let fileNames = - [ __SOURCE_DIRECTORY__ + "/data/samename/folder1/a.fs" - __SOURCE_DIRECTORY__ + "/data/samename/folder2/a.fs" ] + [| __SOURCE_DIRECTORY__ + "/data/samename/folder1/a.fs" + __SOURCE_DIRECTORY__ + "/data/samename/folder2/a.fs" |] let projFileName = __SOURCE_DIRECTORY__ + "/data/samename/tempet.fsproj" let args = mkProjectCommandLineArgs ("test.dll", fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let wholeProjectResults = checker.ParseAndCheckProject(options) |> Async.RunImmediate let errors = wholeProjectResults.Diagnostics @@ -5315,9 +5314,9 @@ let foo (a: Foo): bool = """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] [] @@ -5359,9 +5358,9 @@ let x = (1 = 3.0) """ let fileSource1 = SourceText.ofString fileSource1Text FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) - let fileNames = [fileName1] + let fileNames = [| fileName1 |] let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] let ``Test diagnostics with line directives active`` () = @@ -5425,10 +5424,10 @@ type A(i:int) = let fileSource1 = SourceText.ofString fileSource1Text FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let fileCheckResults = keepAssemblyContentsChecker.ParseAndCheckFileInProject(fileName1, 0, fileSource1, options) |> Async.RunImmediate @@ -5518,10 +5517,10 @@ type UseTheThings(i:int) = let fileSource1 = SourceText.ofString fileSource1Text FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let fileCheckResults = keepAssemblyContentsChecker.ParseAndCheckFileInProject(fileName1, 0, fileSource1, options) |> Async.RunImmediate @@ -5593,10 +5592,10 @@ type UseTheThings(i:int) = let fileSource1 = SourceText.ofString fileSource1Text FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let fileCheckResults = keepAssemblyContentsChecker.ParseAndCheckFileInProject(fileName1, 0, fileSource1, options) |> Async.RunImmediate @@ -5676,10 +5675,10 @@ module M2 = let fileSource1 = SourceText.ofString fileSource1Text FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) - let fileNames = [fileName1] + let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, fileNames) let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let fileCheckResults = keepAssemblyContentsChecker.ParseAndCheckFileInProject(fileName1, 0, fileSource1, options) |> Async.RunImmediate From 4ec6ce350bcbe8257d299e5f82ed848f9f71a3fe Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 16 Nov 2023 11:59:58 +0100 Subject: [PATCH 135/222] enable some more tests --- src/Compiler/Service/FSharpCheckerResults.fs | 53 +++++++++----------- tests/service/Common.fs | 4 +- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index f2a35add5c4..242659e1ff5 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -638,41 +638,36 @@ type FSharpProjectSnapshot with return snapshotAccumulator[options] } - static member FromOptions(options: FSharpProjectOptions) = + static member GetFileSnapshotFromDisk _ fileName = async { - let getFileSnapshot _ fileName = - async { - let timeStamp = FileSystem.GetLastWriteTimeShim(fileName) - let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() - - return - { - FileName = fileName - Version = timeStamp.Ticks.ToString() - GetSource = fun () -> Task.FromResult(SourceText.ofString contents) - } - } + let timeStamp = FileSystem.GetLastWriteTimeShim(fileName) + let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() - return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + return + { + FileName = fileName + Version = timeStamp.Ticks.ToString() + GetSource = fun () -> Task.FromResult(SourceText.ofString contents) + } } + static member FromOptions(options: FSharpProjectOptions) = + FSharpProjectSnapshot.FromOptions(options, FSharpProjectSnapshot.GetFileSnapshotFromDisk) + static member FromOptions(options: FSharpProjectOptions, fileName: string, fileVersion: int, sourceText: ISourceText) = - async { - let! snapshot = FSharpProjectSnapshot.FromOptions options - return - { snapshot with - SourceFiles = - snapshot.SourceFiles - |> List.map (function - | f when f.FileName = fileName -> - { f with - GetSource = fun () -> Task.FromResult sourceText - Version = $"{fileVersion}{sourceText.GetHashCode().ToString()}" - } - | f -> f) - } - } + let getFileSnapshot _ fName = + if fName = fileName then + async.Return + { + FileName = fileName + GetSource = fun () -> Task.FromResult sourceText + Version = $"{fileVersion}{sourceText.GetHashCode().ToString()}" + } + else + FSharpProjectSnapshot.GetFileSnapshotFromDisk () fName + + FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) [] module internal FSharpCheckerResultsSettings = diff --git a/tests/service/Common.fs b/tests/service/Common.fs index 6912dd12b31..c89da4d9078 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -160,7 +160,7 @@ let parseAndCheckScriptWithOptions (file:string, input, opts) = let projName = Path.ChangeExtension(fname, ".fsproj") let args = mkProjectCommandLineArgsForScript (dllName, [file]) printfn "file = %A, args = %A" file args - { checker.GetProjectOptionsFromCommandLineArgs (projName, args) with SourceFiles = [| file |] } + checker.GetProjectOptionsFromCommandLineArgs (projName, args) finally if Directory.Exists(path) then @@ -171,7 +171,7 @@ let parseAndCheckScriptWithOptions (file:string, input, opts) = //printfn "projectOptions = %A" projectOptions #endif - let projectOptions = { projectOptions with OtherOptions = Array.append opts projectOptions.OtherOptions } + let projectOptions = { projectOptions with OtherOptions = Array.append opts projectOptions.OtherOptions; SourceFiles = [|file|] } let parseResult, typedRes = checker.ParseAndCheckFileInProject(file, 0, SourceText.ofString input, projectOptions) |> Async.RunImmediate // if parseResult.Errors.Length > 0 then From 4dbd76788dcc34ae386c61f1fdd4f3c65a8347e6 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 16 Nov 2023 12:06:38 +0100 Subject: [PATCH 136/222] fixed proj file --- src/Compiler/FSharp.Compiler.Service.fsproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 2488186758f..dfafd545ded 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -126,7 +126,7 @@ - + From e921357c185a54351d9e7e3e824c9e9c9f30890a Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 16 Nov 2023 12:29:24 +0100 Subject: [PATCH 137/222] tests --- .../CompilerService/AsyncMemoize.fs | 15 ++++++--------- .../FSharpChecker/CommonWorkflows.fs | 2 +- .../FSharpChecker/TransparentCompiler.fs | 6 +++--- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index c505e5da091..c93572da68d 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -334,21 +334,18 @@ let ``Stress test`` () = [] [] -[] -let ``Cancel running jobs with the same key`` cancelDuplicate expectCancelled = +[] +let ``Cancel running jobs with the same key`` cancelDuplicate expectFinished = task { let cache = AsyncMemoize(cancelDuplicateRunningJobs=cancelDuplicate) let mutable started = 0 - let mutable cancelled = 0 let mutable finished = 0 let work () = async { Interlocked.Increment &started |> ignore - let! ct = Async.CancellationToken - use _ = ct.Register(fun () -> Interlocked.Increment &cancelled |> ignore) for _ in 1..10 do - do! Async.Sleep 30 + do! Async.Sleep 10 Interlocked.Increment &finished |> ignore } @@ -360,7 +357,7 @@ let ``Cancel running jobs with the same key`` cancelDuplicate expectCancelled = cache.Get(key1, work()) |> Async.Start - do! Task.Delay 100 + do! Task.Delay 50 let key2 = { new ICacheKey<_, _> with @@ -370,7 +367,7 @@ let ``Cancel running jobs with the same key`` cancelDuplicate expectCancelled = cache.Get(key2, work()) |> Async.Start - do! Task.Delay 100 + do! Task.Delay 250 - Assert.Equal((2, expectCancelled, 0), (started, cancelled, finished)) + Assert.Equal((2, expectFinished), (started, finished)) } \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs index 3fc6d6c15e4..f4c5c41770d 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs @@ -100,7 +100,7 @@ let ``Changes in a referenced project`` () = checkFile "Last" expectSignatureChanged } -[] +[] // TODO: This will probably require some special care in TransparentCompiler... let ``Language service works if the same file is listed twice`` () = let file = sourceFile "First" [] diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 4b3e88a19b0..a6aa727a8c4 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -836,9 +836,9 @@ let Fuzzing' signatureFiles = builder.DeleteProjectDir() -[] -[] -[] +//[] +//[] +//[] let GiraffeFuzzing signatureFiles = let seed = System.Random().Next() //let seed = 1044159179 From fe607f27bc2efaff3cd3b429d88b595b21573a51 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 16 Nov 2023 13:35:53 +0100 Subject: [PATCH 138/222] reduce surface area changes --- src/Compiler/Facilities/AsyncMemoize.fs | 6 +- src/Compiler/Facilities/TaskAgent.fs | 2 +- src/Compiler/Service/FSharpCheckerResults.fsi | 22 +++---- src/Compiler/Service/service.fs | 2 +- src/Compiler/Service/service.fsi | 15 ++--- ...ervice.SurfaceArea.netstandard20.debug.bsl | 65 +------------------ ...vice.SurfaceArea.netstandard20.release.bsl | 65 +------------------ .../ProjectGeneration.fs | 2 +- 8 files changed, 26 insertions(+), 153 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 94da5d598bf..cdf46414d33 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -10,7 +10,7 @@ open System.Threading.Tasks open FSharp.Compiler.Diagnostics [] -module Utils = +module internal Utils = /// Return file name with one directory above it let shortPath path = @@ -34,7 +34,7 @@ type internal MemoizeReply<'TValue> = | New of CancellationToken | Existing of Task<'TValue> -type MemoizeRequest<'TValue> = +type internal MemoizeRequest<'TValue> = | GetOrCompute of Async<'TValue> * CancellationToken | Sync @@ -114,7 +114,7 @@ module internal Md5Hasher = let addDateTime (dt: System.DateTime) (s: string) = dt.Ticks.ToString() |> addString <| s -type AsyncLock() = +type internal AsyncLock() = let semaphore = new SemaphoreSlim(1, 1) diff --git a/src/Compiler/Facilities/TaskAgent.fs b/src/Compiler/Facilities/TaskAgent.fs index c39cfba3d36..5fdafa22127 100644 --- a/src/Compiler/Facilities/TaskAgent.fs +++ b/src/Compiler/Facilities/TaskAgent.fs @@ -1,4 +1,4 @@ -namespace Internal.Utilities.TaskAgent +module internal Internal.Utilities.TaskAgent open System.Threading open System.Threading.Tasks diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 5744a152456..7fb99c29b8d 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -46,7 +46,7 @@ type DelayedILModuleReader = type public FSharpUnresolvedReferencesSet = internal FSharpUnresolvedReferencesSet of UnresolvedAssemblyReference list [] -type FSharpFileSnapshot = +type internal FSharpFileSnapshot = { FileName: string Version: string GetSource: unit -> Task } @@ -55,7 +55,7 @@ type FSharpFileSnapshot = interface ICacheKey -type FSharpFileSnapshotWithSource = +type internal FSharpFileSnapshotWithSource = { FileName: string SourceHash: string Source: ISourceText @@ -63,13 +63,13 @@ type FSharpFileSnapshotWithSource = IsExe: bool } /// Referenced assembly on disk. Includes last modified time so we know we need to rebuild when it changes. -type ReferenceOnDisk = +type internal ReferenceOnDisk = { Path: string; LastModified: DateTime } -type ProjectSnapshotKey = string * string +type internal ProjectSnapshotKey = string * string [] -type FSharpProjectSnapshot = +type internal FSharpProjectSnapshot = { // Note that this may not reduce to just the project directory, because there may be two projects in the same directory. ProjectFileName: string @@ -161,7 +161,7 @@ type FSharpProjectSnapshot = interface ICacheKey -and FSharpProjectSnapshotWithSources = +and internal FSharpProjectSnapshotWithSources = { ProjectSnapshot: FSharpProjectSnapshot SourceFiles: FSharpFileSnapshotWithSource list } @@ -180,13 +180,13 @@ and FSharpProjectSnapshotWithSources = member internal Key: ICacheKey interface ICacheKey -and FSharpProjectSnapshotWithSourcesDebugVersion = +and internal FSharpProjectSnapshotWithSourcesDebugVersion = { ProjectSnapshotVersion: FSharpProjectSnapshotDebugVersion SourceVersions: string list } -and FSharpProjectSnapshotWithSourcesVersion = string +and internal FSharpProjectSnapshotWithSourcesVersion = string -and FSharpProjectSnapshotDebugVersion = +and internal FSharpProjectSnapshotDebugVersion = { ProjectFileName: string SourceFiles: (string * string) list ReferencesOnDisk: ReferenceOnDisk list @@ -195,9 +195,9 @@ and FSharpProjectSnapshotDebugVersion = IsIncompleteTypeCheckEnvironment: bool UseScriptResolutionRules: bool } -and FSharpProjectSnapshotVersion = string +and internal FSharpProjectSnapshotVersion = string -and [] public FSharpReferencedProjectSnapshot = +and [] internal FSharpReferencedProjectSnapshot = internal | FSharpReference of projectOutputFile: string * options: FSharpProjectSnapshot //| PEReference of projectOutputFile: string * version: string * delayedReader: DelayedILModuleReader diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 2821a5bfe85..d2d54748dd3 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -172,7 +172,7 @@ type FSharpChecker ) :> IBackgroundCompiler - static let globalInstance = lazy FSharpChecker.Create() + static let globalInstance = lazy FSharpChecker.Create(useTransparentCompiler=false) // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.braceMatchCache. Most recently used cache for brace matching. Accessed on the // background UI thread, not on the compiler thread. diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index c6bb4e2e4ab..a665e96ab8b 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -60,11 +60,10 @@ type public FSharpChecker = ?captureIdentifiersWhenParsing: bool * [] ?documentSource: DocumentSource * [] ?useSyntaxTreeCache: bool * - [] ?useTransparentCompiler: bool -> + [] ?useTransparentCompiler: bool -> FSharpChecker - [] - member UsesTransparentCompiler: bool + member internal UsesTransparentCompiler: bool /// /// Parse a source code file, returning information about brace matching in the file. @@ -106,7 +105,7 @@ type public FSharpChecker = fileName: string * sourceText: ISourceText * options: FSharpParsingOptions * ?cache: bool * ?userOpName: string -> Async - member ParseFile: + member internal ParseFile: fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async /// @@ -202,7 +201,7 @@ type public FSharpChecker = ?userOpName: string -> Async - member ParseAndCheckFileInProject: + member internal ParseAndCheckFileInProject: fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async @@ -216,7 +215,7 @@ type public FSharpChecker = /// An optional string used for tracing compiler operations associated with this request. member ParseAndCheckProject: options: FSharpProjectOptions * ?userOpName: string -> Async - member ParseAndCheckProject: + member internal ParseAndCheckProject: projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async /// @@ -339,7 +338,7 @@ type public FSharpChecker = ?userOpName: string -> Async - member FindBackgroundReferencesInFile: + member internal FindBackgroundReferencesInFile: fileName: string * projectSnapshot: FSharpProjectSnapshot * symbol: FSharpSymbol * ?userOpName: string -> Async @@ -363,7 +362,7 @@ type public FSharpChecker = /// The file name for the file. /// The project snapshot for which we want to get the semantic classification. /// An optional string used for tracing compiler operations associated with this request. - member GetBackgroundSemanticClassificationForFile: + member internal GetBackgroundSemanticClassificationForFile: fileName: string * snapshot: FSharpProjectSnapshot * ?userOpName: string -> Async diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 3b7547863bd..5db446150fd 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2048,8 +2048,6 @@ FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: FSharp.Compiler.Symbols. FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] DependencyFiles FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] get_DependencyFiles() -FSharp.Compiler.CodeAnalysis.FSharpChecker: Boolean UsesTransparentCompiler -FSharp.Compiler.CodeAnalysis.FSharpChecker: Boolean get_UsesTransparentCompiler() FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Instance FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker get_Instance() @@ -2061,19 +2059,14 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualCheckFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualParseFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer] CheckFileInProject(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] GetBackgroundParseResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpParsingOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFileInProject(System.String, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] CheckFileInProjectAllowingStaleCachedResults(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyFileChanged(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyProjectCleaned(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]]] GetProjectOptionsFromScript(System.String, FSharp.Compiler.Text.ISourceText, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.String[]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Int64], Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2097,16 +2090,6 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearCache(System.Collections.G FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateAll() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateConfiguration(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Boolean Equals(System.Object) -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Int32 GetHashCode() -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]] GetSource -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]] get_GetSource() -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String FileName -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String ToString() -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String Version -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String get_FileName() -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String get_Version() -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]]) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsBindingALambdaAtPosition(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPosContainedInApplication(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPositionContainedInACurriedParameter(FSharp.Compiler.Text.Position) @@ -2204,47 +2187,6 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: System.String[] SourceFiles FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: System.String[] get_OtherOptions() FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: System.String[] get_SourceFiles() FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], System.String[], System.String[], FSharp.Compiler.CodeAnalysis.FSharpReferencedProject[], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean Equals(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean Equals(System.Object) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean Equals(System.Object, System.Collections.IEqualityComparer) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean IsIncompleteTypeCheckEnvironment -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean UseScriptResolutionRules -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_IsIncompleteTypeCheckEnvironment() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_UseScriptResolutionRules() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions ToOptions() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot OnlyWith(Microsoft.FSharp.Collections.FSharpSet`1[System.Int32]) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(Int32) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(System.String) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot WithoutFileVersions -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot get_WithoutFileVersions() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode(System.Collections.IEqualityComparer) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 IndexOf(System.String) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot] SourceFiles -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot] get_SourceFiles() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot] ReferencedProjects -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot] get_ReferencedProjects() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] OtherOptions -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] SourceFileNames -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_OtherOptions() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_SourceFileNames() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] OriginalLoadReferences -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] get_OriginalLoadReferences() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot]]]) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, System.String, Int32, FSharp.Compiler.Text.ISourceText) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] UnresolvedReferences -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] get_UnresolvedReferences() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] Stamp -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] get_Stamp() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] ProjectId -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_ProjectId() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.DateTime LoadTime -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.DateTime get_LoadTime() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String ProjectFileName -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String ToString() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String get_ProjectFileName() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject+FSharpReference: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions get_options() FSharp.Compiler.CodeAnalysis.FSharpReferencedProject+FSharpReference: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions options FSharp.Compiler.CodeAnalysis.FSharpReferencedProject+FSharpReference: System.String get_projectOutputFile() @@ -2282,12 +2224,6 @@ FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: Int32 get_Tag() FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: System.String OutputFile FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: System.String get_OutputFile() -FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: Boolean Equals(System.Object) -FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot CreateFSharp(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot) -FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: Int32 GetHashCode() -FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: System.String OutputFile -FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: System.String ToString() -FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: System.String get_OutputFile() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromAttribute FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromComputationExpression FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromDefinition @@ -10260,6 +10196,7 @@ FSharp.Compiler.Text.ISourceText: Char get_Item(Int32) FSharp.Compiler.Text.ISourceText: Int32 GetLineCount() FSharp.Compiler.Text.ISourceText: Int32 Length FSharp.Compiler.Text.ISourceText: Int32 get_Length() +FSharp.Compiler.Text.ISourceText: System.Collections.Immutable.ImmutableArray`1[System.Byte] GetChecksum() FSharp.Compiler.Text.ISourceText: System.String GetLineString(Int32) FSharp.Compiler.Text.ISourceText: System.String GetSubTextFromRange(FSharp.Compiler.Text.Range) FSharp.Compiler.Text.ISourceText: System.String GetSubTextString(Int32, Int32) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 3b7547863bd..5db446150fd 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2048,8 +2048,6 @@ FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: FSharp.Compiler.Symbols. FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] DependencyFiles FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] get_DependencyFiles() -FSharp.Compiler.CodeAnalysis.FSharpChecker: Boolean UsesTransparentCompiler -FSharp.Compiler.CodeAnalysis.FSharpChecker: Boolean get_UsesTransparentCompiler() FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.DocumentSource], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Instance FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker get_Instance() @@ -2061,19 +2059,14 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualCheckFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualParseFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer] CheckFileInProject(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] GetBackgroundParseResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpParsingOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFileInProject(System.String, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] CheckFileInProjectAllowingStaleCachedResults(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyFileChanged(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyProjectCleaned(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]]] GetProjectOptionsFromScript(System.String, FSharp.Compiler.Text.ISourceText, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.String[]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Int64], Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2097,16 +2090,6 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearCache(System.Collections.G FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateAll() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateConfiguration(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Boolean Equals(System.Object) -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Int32 GetHashCode() -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]] GetSource -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]] get_GetSource() -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String FileName -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String ToString() -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String Version -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String get_FileName() -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: System.String get_Version() -FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceText]]) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsBindingALambdaAtPosition(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPosContainedInApplication(FSharp.Compiler.Text.Position) FSharp.Compiler.CodeAnalysis.FSharpParseFileResults: Boolean IsPositionContainedInACurriedParameter(FSharp.Compiler.Text.Position) @@ -2204,47 +2187,6 @@ FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: System.String[] SourceFiles FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: System.String[] get_OtherOptions() FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: System.String[] get_SourceFiles() FSharp.Compiler.CodeAnalysis.FSharpProjectOptions: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], System.String[], System.String[], FSharp.Compiler.CodeAnalysis.FSharpReferencedProject[], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean Equals(FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean Equals(System.Object) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean Equals(System.Object, System.Collections.IEqualityComparer) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean IsIncompleteTypeCheckEnvironment -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean UseScriptResolutionRules -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_IsIncompleteTypeCheckEnvironment() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Boolean get_UseScriptResolutionRules() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions ToOptions() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot OnlyWith(Microsoft.FSharp.Collections.FSharpSet`1[System.Int32]) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(Int32) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot UpTo(System.String) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot WithoutFileVersions -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot get_WithoutFileVersions() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 GetHashCode(System.Collections.IEqualityComparer) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Int32 IndexOf(System.String) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot] SourceFiles -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot] get_SourceFiles() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot] ReferencedProjects -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot] get_ReferencedProjects() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] OtherOptions -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] SourceFileNames -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_OtherOptions() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_SourceFileNames() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] OriginalLoadReferences -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] get_OriginalLoadReferences() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot]]]) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, System.String, Int32, FSharp.Compiler.Text.ISourceText) -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] UnresolvedReferences -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] get_UnresolvedReferences() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] Stamp -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] get_Stamp() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] ProjectId -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_ProjectId() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.DateTime LoadTime -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.DateTime get_LoadTime() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String ProjectFileName -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String ToString() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: System.String get_ProjectFileName() -FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) FSharp.Compiler.CodeAnalysis.FSharpReferencedProject+FSharpReference: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions get_options() FSharp.Compiler.CodeAnalysis.FSharpReferencedProject+FSharpReference: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions options FSharp.Compiler.CodeAnalysis.FSharpReferencedProject+FSharpReference: System.String get_projectOutputFile() @@ -2282,12 +2224,6 @@ FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: Int32 get_Tag() FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: System.String OutputFile FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpReferencedProject: System.String get_OutputFile() -FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: Boolean Equals(System.Object) -FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot CreateFSharp(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectSnapshot) -FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: Int32 GetHashCode() -FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: System.String OutputFile -FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: System.String ToString() -FSharp.Compiler.CodeAnalysis.FSharpReferencedProjectSnapshot: System.String get_OutputFile() FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromAttribute FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromComputationExpression FSharp.Compiler.CodeAnalysis.FSharpSymbolUse: Boolean IsFromDefinition @@ -10260,6 +10196,7 @@ FSharp.Compiler.Text.ISourceText: Char get_Item(Int32) FSharp.Compiler.Text.ISourceText: Int32 GetLineCount() FSharp.Compiler.Text.ISourceText: Int32 Length FSharp.Compiler.Text.ISourceText: Int32 get_Length() +FSharp.Compiler.Text.ISourceText: System.Collections.Immutable.ImmutableArray`1[System.Byte] GetChecksum() FSharp.Compiler.Text.ISourceText: System.String GetLineString(Int32) FSharp.Compiler.Text.ISourceText: System.String GetSubTextFromRange(FSharp.Compiler.Text.Range) FSharp.Compiler.Text.ISourceText: System.String GetSubTextString(Int32, Int32) diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index c5b75165b6e..ef39e71988a 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -13,7 +13,7 @@ /// To model changes to "private" code in a file we change the body of a second function which /// no one calls. /// -module FSharp.Test.ProjectGeneration +module internal FSharp.Test.ProjectGeneration open System open System.Collections.Concurrent From f70d88bc40d6c236726ef22150658f0847d4aca1 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 16 Nov 2023 13:50:57 +0100 Subject: [PATCH 139/222] fix tests --- .../CompilerService/AsyncMemoize.fs | 2 +- .../FSharp.Test.Utilities.fsproj | 12 ++++++++++++ tests/FSharp.Test.Utilities/ProjectGeneration.fs | 8 ++++---- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index c93572da68d..758e31b3147 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -367,7 +367,7 @@ let ``Cancel running jobs with the same key`` cancelDuplicate expectFinished = cache.Get(key2, work()) |> Async.Start - do! Task.Delay 250 + do! Task.Delay 500 Assert.Equal((2, expectFinished), (started, finished)) } \ No newline at end of file diff --git a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj index 03a4d487091..63ed7a00258 100644 --- a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj +++ b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj @@ -69,6 +69,18 @@ + + + + + + + + + + + + diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index ef39e71988a..a8d4ce8f821 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -13,7 +13,7 @@ /// To model changes to "private" code in a file we change the body of a second function which /// no one calls. /// -module internal FSharp.Test.ProjectGeneration +module FSharp.Test.ProjectGeneration open System open System.Collections.Concurrent @@ -543,7 +543,7 @@ module ProjectOperations = |> renderSourceFile project |> SourceText.ofString - let getFileSnapshot (project: SyntheticProject) _options (path: string) = + let internal getFileSnapshot (project: SyntheticProject) _options (path: string) = async { let project, filePath = if path.EndsWith(".fsi") then @@ -716,7 +716,7 @@ module ProjectOperations = module Helpers = - let getSymbolUse fileName (source: string) (symbolName: string) snapshot (checker: FSharpChecker) = + let internal getSymbolUse fileName (source: string) (symbolName: string) snapshot (checker: FSharpChecker) = async { let lines = source.Split '\n' |> Seq.skip 1 // module definition let lineNumber, fullLine, colAtEndOfNames = @@ -741,7 +741,7 @@ module Helpers = failwith $"No symbol found in {fileName} at {lineNumber}:{colAtEndOfNames}\nFile contents:\n\n{source}\n") } - let singleFileChecker source = + let internal singleFileChecker source = let fileName = "test.fs" From 15049b0cd4cb3f08458670ce7d7bb2eeccf66395 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 16 Nov 2023 14:30:42 +0100 Subject: [PATCH 140/222] cache update --- src/Compiler/Facilities/AsyncMemoize.fs | 27 ++++++++++++++++--- src/Compiler/Service/FSharpCheckerResults.fsi | 2 +- src/Compiler/Service/TransparentCompiler.fs | 8 +++--- src/Compiler/Service/service.fs | 2 +- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index cdf46414d33..c1e614f4235 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -478,13 +478,15 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let stats = [| - if errors > 0 then $"| errors: {errors} " else "" + if errors + failed > 0 then + " [!] " + if errors > 0 then $"| ERRORS: {errors} " else "" + if failed > 0 then $"| FAILED: {failed} " else "" $"| hits: {hits}{hitRatio} " if started > 0 then $"| started: {started} " else "" if completed > 0 then $"| completed: {completed} " else "" if canceled > 0 then $"| canceled: {canceled} " else "" if restarted > 0 then $"| restarted: {restarted} " else "" - if failed > 0 then $"| failed: {failed} " else "" if evicted > 0 then $"| evicted: {evicted} " else "" if collected > 0 then $"| collected: {collected} " else "" if strengthened > 0 then @@ -494,4 +496,23 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T |] |> String.concat "" - $"{name}{locked}{running} {cache.DebuggerDisplay} {stats}{avgDuration}" + $"{locked}{running} {cache.DebuggerDisplay} {stats}{avgDuration}" + +[] +type internal AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> + ( + ?keepStrongly, + ?keepWeakly, + ?name: string, + ?cancelDuplicateRunningJobs: bool + ) = + + do ignore (keepStrongly, keepWeakly, name, cancelDuplicateRunningJobs) + + let mutable requests = 0 + + member _.Get(_key: ICacheKey<_, _>, computation) = + Interlocked.Increment &requests |> ignore + computation + + member _.DebuggerDisplay = $"(disabled) requests: {requests}" diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 7fb99c29b8d..96b7431a1b9 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -195,7 +195,7 @@ and internal FSharpProjectSnapshotDebugVersion = IsIncompleteTypeCheckEnvironment: bool UseScriptResolutionRules: bool } -and internal FSharpProjectSnapshotVersion = string +and internal FSharpProjectSnapshotVersion = string and [] internal FSharpReferencedProjectSnapshot = internal diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index d00a06d8909..9fd343084db 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -261,8 +261,6 @@ module private TypeCheckingGraphProcessing = type internal CompilerCaches(sizeFactor: int) = - // member val Source = AsyncMemoize(keepStrongly = 1000, keepWeakly = 2000, name = "Source") - let sf = sizeFactor member _.SizeFactor = sf @@ -271,7 +269,7 @@ type internal CompilerCaches(sizeFactor: int) = member val ParseAndCheckFileInProject = AsyncMemoize(sf, 2 * sf, name = "ParseAndCheckFileInProject") - member val ParseAndCheckAllFilesInProject = AsyncMemoize(sf, 2 * sf, name = "ParseAndCheckFullProject") + member val ParseAndCheckAllFilesInProject = AsyncMemoizeDisabled(sf, 2 * sf, name = "ParseAndCheckFullProject") member val ParseAndCheckProject = AsyncMemoize(sf, 2 * sf, name = "ParseAndCheckProject") @@ -281,13 +279,13 @@ type internal CompilerCaches(sizeFactor: int) = member val BootstrapInfo = AsyncMemoize(sf, 2 * sf, name = "BootstrapInfo") - member val TcLastFile = AsyncMemoize(sf, 2 * sf, name = "TcLastFile") + member val TcLastFile = AsyncMemoizeDisabled(sf, 2 * sf, name = "TcLastFile") member val TcIntermediate = AsyncMemoize(20 * sf, 20 * sf, name = "TcIntermediate") member val DependencyGraph = AsyncMemoize(sf, 2 * sf, name = "DependencyGraph") - member val ProjectExtras = AsyncMemoize(sf, 2 * sf, name = "ProjectExtras") + member val ProjectExtras = AsyncMemoizeDisabled(sf, 2 * sf, name = "ProjectExtras") member val AssemblyData = AsyncMemoize(sf, 2 * sf, name = "AssemblyData") diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index d2d54748dd3..2821a5bfe85 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -172,7 +172,7 @@ type FSharpChecker ) :> IBackgroundCompiler - static let globalInstance = lazy FSharpChecker.Create(useTransparentCompiler=false) + static let globalInstance = lazy FSharpChecker.Create() // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.braceMatchCache. Most recently used cache for brace matching. Accessed on the // background UI thread, not on the compiler thread. From cf0c1ac4f0996a130dda07d08eb9348a55227a0b Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 16 Nov 2023 16:50:40 +0100 Subject: [PATCH 141/222] more tests, include implFiles in project check result --- src/Compiler/Service/TransparentCompiler.fs | 10 +-- tests/service/ExprTests.fs | 70 +++++++++++++-------- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 9fd343084db..d6bfc9fc098 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1463,7 +1463,7 @@ type internal TransparentCompiler let results = results |> Seq.sortBy fst |> Seq.map snd |> Seq.toList // Finish the checking - let (_tcEnvAtEndOfLastFile, topAttrs, _mimpls, _), tcState = + let (_tcEnvAtEndOfLastFile, topAttrs, checkedImplFiles, _), tcState = CheckMultipleInputsFinish(results, finalInfo.tcState) let tcState, _, ccuContents = CheckClosedInputSetFinish([], tcState) @@ -1539,7 +1539,7 @@ type internal TransparentCompiler errorRecoveryNoRange exn ProjectAssemblyDataResult.Unavailable true - return finalInfo, ilAssemRef, assemblyDataResult + return finalInfo, ilAssemRef, assemblyDataResult, checkedImplFiles } ) @@ -1571,7 +1571,7 @@ type internal TransparentCompiler let! snapshotWithSources = LoadSources bootstrapInfo projectSnapshot - let! _, _, assemblyDataResult = ComputeProjectExtras bootstrapInfo snapshotWithSources + let! _, _, assemblyDataResult, _ = ComputeProjectExtras bootstrapInfo snapshotWithSources Trace.TraceInformation($"Using in-memory project reference: {name}") return assemblyDataResult @@ -1590,7 +1590,7 @@ type internal TransparentCompiler let! snapshotWithSources = LoadSources bootstrapInfo projectSnapshot - let! tcInfo, ilAssemRef, assemblyDataResult = ComputeProjectExtras bootstrapInfo snapshotWithSources + let! tcInfo, ilAssemRef, assemblyDataResult, checkedImplFiles = ComputeProjectExtras bootstrapInfo snapshotWithSources let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions let fileName = DummyFileNameForRangesWithoutASpecificLocation @@ -1635,7 +1635,7 @@ type internal TransparentCompiler getAssemblyData, ilAssemRef, tcEnvAtEnd.AccessRights, - None, + Some checkedImplFiles, Array.ofList tcDependencyFiles, projectSnapshot.ToOptions()) diff --git a/tests/service/ExprTests.fs b/tests/service/ExprTests.fs index 8c8128eca18..71e6d709df5 100644 --- a/tests/service/ExprTests.fs +++ b/tests/service/ExprTests.fs @@ -364,8 +364,8 @@ let createOptionsAux fileSources extraArgs = Utils.createTempDir() for fileSource: string, fileName in List.zip fileSources fileNames do FileSystem.OpenFileForWriteShim(fileName).Write(fileSource) - let args = [| yield! extraArgs; yield! mkProjectCommandLineArgs (dllName, fileNames) |] - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let args = [| yield! extraArgs; yield! mkProjectCommandLineArgs (dllName, []) |] + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames |> List.toArray } Utils.cleanupTempFiles (fileNames @ [dllName; projFileName]), options @@ -732,11 +732,13 @@ let ignoreTestIfStackOverflowExpected () = #endif /// This test is run in unison with its optimized counterpart below +[] +[] [] -let ``Test Unoptimized Declarations Project1`` () = +let ``Test Unoptimized Declarations Project1`` useTransparentCompiler = let cleanup, options = Project1.createOptionsWithArgs [ "--langversion:preview" ] use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -867,11 +869,13 @@ let ``Test Unoptimized Declarations Project1`` () = () +[] +[] [] -let ``Test Optimized Declarations Project1`` () = +let ``Test Optimized Declarations Project1`` useTransparentCompiler = let cleanup, options = Project1.createOptionsWithArgs [ "--langversion:preview" ] use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -1009,7 +1013,7 @@ let testOperators dnName fsName excludedTests expectedUnoptimized expectedOptimi let filePath = Utils.getTempFilePathChangeExt tempFileName ".fs" let dllPath =Utils.getTempFilePathChangeExt tempFileName ".dll" let projFilePath = Utils.getTempFilePathChangeExt tempFileName ".fsproj" - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=true) begin use _cleanup = Utils.cleanupTempFiles [filePath; dllPath; projFilePath] @@ -1019,9 +1023,9 @@ let testOperators dnName fsName excludedTests expectedUnoptimized expectedOptimi let fileSource = excludedTests |> List.fold replace source FileSystem.OpenFileForWriteShim(filePath).Write(fileSource) - let args = mkProjectCommandLineArgsSilent (dllPath, [filePath]) + let args = mkProjectCommandLineArgsSilent (dllPath, []) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFilePath, args) + let options = { checker.GetProjectOptionsFromCommandLineArgs (projFilePath, args) with SourceFiles = [|filePath|] } let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate let referencedAssemblies = wholeProjectResults.ProjectContext.GetReferencedAssemblies() @@ -3198,12 +3202,14 @@ let BigSequenceExpression(outFileOpt,docFileOpt,baseAddressOpt) = let createOptions() = createOptionsAux [fileSource1] [] +[] +[] [] -let ``Test expressions of declarations stress big expressions`` () = +let ``Test expressions of declarations stress big expressions`` useTransparentCompiler = ignoreTestIfStackOverflowExpected () let cleanup, options = ProjectStressBigExpressions.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate wholeProjectResults.Diagnostics.Length |> shouldEqual 0 @@ -3215,12 +3221,14 @@ let ``Test expressions of declarations stress big expressions`` () = printDeclarations None (List.ofSeq file1.Declarations) |> Seq.toList |> ignore +[] +[] [] -let ``Test expressions of optimized declarations stress big expressions`` () = +let ``Test expressions of optimized declarations stress big expressions`` useTransparentCompiler = ignoreTestIfStackOverflowExpected () let cleanup, options = ProjectStressBigExpressions.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate wholeProjectResults.Diagnostics.Length |> shouldEqual 0 @@ -3276,11 +3284,13 @@ let f8() = callXY (D()) (C()) let createOptions() = createOptionsAux [fileSource1] ["--langversion:7.0"] +[] +[] [] -let ``Test ProjectForWitnesses1`` () = +let ``Test ProjectForWitnesses1`` useTransparentCompiler = let cleanup, options = ProjectForWitnesses1.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3320,11 +3330,13 @@ let ``Test ProjectForWitnesses1`` () = |> shouldPairwiseEqual expected +[] +[] [] -let ``Test ProjectForWitnesses1 GetWitnessPassingInfo`` () = +let ``Test ProjectForWitnesses1 GetWitnessPassingInfo`` useTransparentCompiler = let cleanup, options = ProjectForWitnesses1.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3400,11 +3412,13 @@ type MyNumberWrapper = let createOptions() = createOptionsAux [fileSource1] ["--langversion:7.0"] +[] +[] [] -let ``Test ProjectForWitnesses2`` () = +let ``Test ProjectForWitnesses2`` useTransparentCompiler = let cleanup, options = ProjectForWitnesses2.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3455,11 +3469,13 @@ let s2 = sign p1 let createOptions() = createOptionsAux [fileSource1] ["--langversion:7.0"] +[] +[] [] -let ``Test ProjectForWitnesses3`` () = +let ``Test ProjectForWitnesses3`` useTransparentCompiler = let cleanup, options = createOptionsAux [ ProjectForWitnesses3.fileSource1 ] ["--langversion:7.0"] use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3486,11 +3502,13 @@ let ``Test ProjectForWitnesses3`` () = actual |> shouldPairwiseEqual expected +[] +[] [] -let ``Test ProjectForWitnesses3 GetWitnessPassingInfo`` () = +let ``Test ProjectForWitnesses3 GetWitnessPassingInfo`` useTransparentCompiler = let cleanup, options = ProjectForWitnesses3.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3549,11 +3567,13 @@ let isNullQuoted (ts : 't[]) = let createOptions() = createOptionsAux [fileSource1] ["--langversion:7.0"] +[] +[] [] -let ``Test ProjectForWitnesses4 GetWitnessPassingInfo`` () = +let ``Test ProjectForWitnesses4 GetWitnessPassingInfo`` useTransparentCompiler = let cleanup, options = ProjectForWitnesses4.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do From ff870329224c5a629b90da0d05579e538b1f8b45 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 16 Nov 2023 16:58:04 +0100 Subject: [PATCH 142/222] fix --- src/Compiler/FSharp.Compiler.Service.fsproj | 2 +- tests/service/Common.fs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index dfafd545ded..0211c4f7206 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -160,7 +160,7 @@ - + diff --git a/tests/service/Common.fs b/tests/service/Common.fs index c89da4d9078..e7e5a9084c3 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -158,7 +158,7 @@ let parseAndCheckScriptWithOptions (file:string, input, opts) = let fname = Path.Combine(path, Path.GetFileName(file)) let dllName = Path.ChangeExtension(fname, ".dll") let projName = Path.ChangeExtension(fname, ".fsproj") - let args = mkProjectCommandLineArgsForScript (dllName, [file]) + let args = mkProjectCommandLineArgsForScript (dllName, []) printfn "file = %A, args = %A" file args checker.GetProjectOptionsFromCommandLineArgs (projName, args) From 9a5a71409e4b782a43f556ffce16c29c13be2072 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 16 Nov 2023 17:21:36 +0100 Subject: [PATCH 143/222] fix more tests --- tests/service/ProjectAnalysisTests.fs | 113 +++++++++++++------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/tests/service/ProjectAnalysisTests.fs b/tests/service/ProjectAnalysisTests.fs index 7cb3bc42f9f..ac88e06a0c7 100644 --- a/tests/service/ProjectAnalysisTests.fs +++ b/tests/service/ProjectAnalysisTests.fs @@ -90,9 +90,10 @@ let mmmm2 : M.CAbbrev = new M.CAbbrev() // note, these don't count as uses of C FileSystem.OpenFileForWriteShim(fileName2).Write(fileSource2Text) let fileNames = [fileName1; fileName2] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames |> List.toArray } - let parsingOptions, _ = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) + let parsingOptions', _ = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) + let parsingOptions = { parsingOptions' with SourceFiles = fileNames |> List.toArray } let cleanFileName a = if a = fileName1 then "file1" else if a = fileName2 then "file2" else "??" [] @@ -676,7 +677,7 @@ let _ = GenericFunction(3, 4) FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -918,7 +919,7 @@ let getM (foo: IFoo) = foo.InterfaceMethod("d") FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -1287,7 +1288,7 @@ let inline twice(x : ^U, y : ^U) = x + y FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -1461,7 +1462,7 @@ let parseNumeric str = let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -1673,7 +1674,7 @@ let f () = let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -1729,7 +1730,7 @@ let x2 = C.M(arg1 = 3, arg2 = 4, ?arg3 = Some 5) let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -1790,7 +1791,7 @@ let x = let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -1870,7 +1871,7 @@ let inline check< ^T when ^T : (static member IsInfinity : ^T -> bool)> (num: ^T let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -1949,7 +1950,7 @@ C.M("http://goo", query = 1) let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -2029,7 +2030,7 @@ let fff (x:System.Collections.Generic.Dictionary.Enumerator) = () let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -2098,7 +2099,7 @@ let x2 = query { for i in 0 .. 100 do let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -2165,7 +2166,7 @@ let x3 = new System.DateTime() let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -2324,7 +2325,7 @@ let x2 = S(3) let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -2391,7 +2392,7 @@ let f x = let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -2478,7 +2479,7 @@ and G = Case1 | Case2 of int let cleanFileName a = if a = fileName1 then "file1" elif a = sigFileName1 then "sig1" else "??" let fileNames = [|sigFileName1; fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -2744,7 +2745,7 @@ let f3 (x: System.Exception) = x.HelpLink <- "" // check use of .NET setter prop let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -2831,7 +2832,7 @@ let _ = list<_>.Empty let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -2887,7 +2888,7 @@ let s = System.DayOfWeek.Monday let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -2962,7 +2963,7 @@ type A<'T>() = let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -3023,7 +3024,7 @@ let _ = { new IMyInterface with let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -3097,7 +3098,7 @@ let f5 (x: int[,,]) = () // test a multi-dimensional array let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -3244,7 +3245,7 @@ module Setter = let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -3415,7 +3416,7 @@ TypeWithProperties.StaticAutoPropGetSet <- 3 let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -3685,7 +3686,7 @@ let _ = XmlProvider<"13">.GetSample() let fileNames = [|fileName1|] let args = - [| yield! mkProjectCommandLineArgs (dllName, fileNames) + [| yield! mkProjectCommandLineArgs (dllName, []) yield @"-r:" + Path.Combine(__SOURCE_DIRECTORY__, Path.Combine("data", "FSharp.Data.dll")) yield @"-r:" + sysLib "System.Xml.Linq" |] let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -3824,7 +3825,7 @@ type Class() = let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -3914,7 +3915,7 @@ type CFooImpl() = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -3979,7 +3980,7 @@ type Use() = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } #if !NO_TYPEPROVIDERS [] @@ -4057,7 +4058,7 @@ let f (x: INotifyPropertyChanged) = failwith "" FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -4116,7 +4117,7 @@ type T() = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let ``Test project30 whole project errors`` () = @@ -4176,7 +4177,7 @@ let g = Console.ReadKey() FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let ``Test project31 whole project errors`` () = @@ -4318,7 +4319,7 @@ val func : int -> int let cleanFileName a = if a = fileName1 then "file1" elif a = sigFileName1 then "sig1" else "??" let fileNames = [|sigFileName1; fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -4386,7 +4387,7 @@ type System.Int32 with let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -4427,7 +4428,7 @@ module internal Project34 = let fileNames = [|sourceFileName|] let args = [| - yield! mkProjectCommandLineArgs (dllName, fileNames) + yield! mkProjectCommandLineArgs (dllName, []) // We use .NET-built version of System.Data.dll since the tests depend on implementation details // i.e. the private type System.Data.Listeners may not be available on Mono. yield @"-r:" + Path.Combine(__SOURCE_DIRECTORY__, Path.Combine("data", "System.Data.dll")) @@ -4503,7 +4504,7 @@ type Test = let cleanFileName a = if a = fileName1 then "file1" else "??" let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -4641,15 +4642,15 @@ let callToOverload = B(5).Overload(4) FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let cleanFileName a = if a = fileName1 then "file1" else "??" - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let fileNames = [|fileName1|] + let args = mkProjectCommandLineArgs (dllName, []) [] [] [] let ``Test project36 FSharpMemberOrFunctionOrValue.IsBaseValue`` useTransparentCompiler = let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) with SourceFiles = Project36.fileNames } let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(options) |> Async.RunImmediate @@ -4666,7 +4667,7 @@ let ``Test project36 FSharpMemberOrFunctionOrValue.IsBaseValue`` useTransparentC [] let ``Test project36 FSharpMemberOrFunctionOrValue.IsConstructorThisValue & IsMemberThisValue`` useTransparentCompiler = let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) with SourceFiles = Project36.fileNames } let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(options) |> Async.RunImmediate let declarations = let checkedFile = wholeProjectResults.AssemblyContents.ImplementationFiles[0] @@ -4705,7 +4706,7 @@ let ``Test project36 FSharpMemberOrFunctionOrValue.IsConstructorThisValue & IsMe [] let ``Test project36 FSharpMemberOrFunctionOrValue.LiteralValue`` useTransparentCompiler = let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) - let options = keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) + let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) with SourceFiles = Project36.fileNames } let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(options) |> Async.RunImmediate let project36Module = wholeProjectResults.AssemblySignature.Entities[0] let lit = project36Module.MembersFunctionsAndValues[0] @@ -4767,7 +4768,7 @@ do () """ FileSystem.OpenFileForWriteShim(fileName2).Write(fileSource2) let fileNames = [|fileName1; fileName2|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -4909,7 +4910,7 @@ type A<'XX, 'YY>() = """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -4995,7 +4996,7 @@ let uses () = """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let cleanFileName a = if a = fileName1 then "file1" else "??" @@ -5070,7 +5071,7 @@ let g (x: C) = x.IsItAnA,x.IsItAnAMethod() FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let cleanFileName a = if a = fileName1 then "file1" else "??" @@ -5141,7 +5142,7 @@ module M """ FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let cleanFileName a = if a = fileName1 then "file1" else "??" @@ -5233,7 +5234,7 @@ let test2() = test() """ FileSystem.OpenFileForWriteShim(fileName2).Write(fileSource2) let fileNames = [|fileName1;fileName2|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -5267,10 +5268,10 @@ module internal ProjectBig = let fileSources2 = [ for i,f in fileSources -> SourceText.ofString f ] let fileNames = [ for _,f in fileNamesI -> f ] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) - let parsingOptions, _ = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) - + let parsingOptions', _ = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) + let parsingOptions = { parsingOptions' with SourceFiles = fileNames |> List.toArray } [] @@ -5315,7 +5316,7 @@ let foo (a: Foo): bool = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -5359,7 +5360,7 @@ let x = (1 = 3.0) let fileSource1 = SourceText.ofString fileSource1Text FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) let fileNames = [| fileName1 |] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } [] @@ -5425,7 +5426,7 @@ type A(i:int) = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -5518,7 +5519,7 @@ type UseTheThings(i:int) = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -5593,7 +5594,7 @@ type UseTheThings(i:int) = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } @@ -5676,7 +5677,7 @@ module M2 = FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1Text) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, []) let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } From d2119f8dc7ca66563c6b4882ec50d27106c2f845 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 16 Nov 2023 19:31:18 +0100 Subject: [PATCH 144/222] implement backgroundcompiler events to enable more tests --- src/Compiler/Service/TransparentCompiler.fs | 28 ++++++++++++------- tests/FSharp.Test.Utilities/CompilerAssert.fs | 3 +- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index d6bfc9fc098..110f0117f75 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -329,6 +329,12 @@ type internal TransparentCompiler // if the cached incremental builder can be used for the project. let dependencyProviderForScripts = new DependencyProvider() + // Legacy events, they're used in tests... eventually they should go away + let beforeFileChecked = Event() + let fileParsed = Event() + let fileChecked = Event() + let projectChecked = Event() + // use this to process not-yet-implemented tasks let backgroundCompiler = BackgroundCompiler( @@ -934,6 +940,8 @@ type internal TransparentCompiler let input = ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) + fileParsed.Trigger(fileName, Unchecked.defaultof<_>) + return input, diagnosticsLogger.GetDiagnostics(), sourceText, tcConfig } ) @@ -1091,6 +1099,8 @@ type internal TransparentCompiler "version", "-" // key.GetVersion() |] + beforeFileChecked.Trigger(fileName, Unchecked.defaultof<_>) + let tcConfig = bootstrapInfo.TcConfig let tcGlobals = bootstrapInfo.TcGlobals let tcImports = bootstrapInfo.TcImports @@ -1143,6 +1153,8 @@ type internal TransparentCompiler let newErrors = Array.append parseErrors (capturingDiagnosticsLogger.Diagnostics |> List.toArray) + fileChecked.Trigger(fileName, Unchecked.defaultof<_>) + return { finisher = finisher @@ -1785,9 +1797,6 @@ type internal TransparentCompiler interface IBackgroundCompiler with - member this.BeforeBackgroundFileCheck: IEvent = - backgroundCompiler.BeforeBackgroundFileCheck - member this.CheckFileInProject ( parseResults: FSharpParseFileResults, @@ -1843,11 +1852,13 @@ type internal TransparentCompiler member _.DownsizeCaches() : unit = backgroundCompiler.DownsizeCaches() - member _.FileChecked: IEvent = - backgroundCompiler.FileChecked + member _.BeforeBackgroundFileCheck = beforeFileChecked.Publish + + member _.FileParsed = fileParsed.Publish - member _.FileParsed: IEvent = - backgroundCompiler.FileParsed + member _.FileChecked = fileChecked.Publish + + member _.ProjectChecked = projectChecked.Publish member this.FindReferencesInFile ( @@ -2043,9 +2054,6 @@ type internal TransparentCompiler ) : Async = backgroundCompiler.ParseFile(fileName, sourceText, options, cache, flatErrors, userOpName) - member this.ProjectChecked: IEvent = - backgroundCompiler.ProjectChecked - member this.TryGetRecentCheckResultsForFile ( fileName: string, diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index 9088e156780..ad998861fdf 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -258,7 +258,8 @@ and Compilation = module rec CompilerAssertHelpers = - let checker = FSharpChecker.Create(suggestNamesForErrors=true) + // TODO: check both TransparentCompiler and IncrementalBuilder, somehow + let checker = FSharpChecker.Create(suggestNamesForErrors=true, useTransparentCompiler=true) // Unlike C# whose entrypoint is always string[] F# can make an entrypoint with 0 args, or with an array of string[] let mkDefaultArgs (entryPoint:MethodInfo) : obj[] = [| From 0387fca10edf63107417ddbf3d21b2d2900c7c85 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 20 Nov 2023 12:02:20 +0100 Subject: [PATCH 145/222] reduce api surface --- src/Compiler/Service/BackgroundCompiler.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 452dacd7ec6..22516c1433e 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -168,17 +168,17 @@ type internal IBackgroundCompiler = abstract member ProjectChecked: IEvent -type ParseCacheLockToken() = +type internal ParseCacheLockToken() = interface LockToken -type ScriptClosureCacheToken() = +type internal ScriptClosureCacheToken() = interface LockToken type CheckFileCacheKey = FileName * SourceTextHash * FSharpProjectOptions type CheckFileCacheValue = FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash * DateTime [] -module EnvMisc = +module internal EnvMisc = let braceMatchCacheSize = GetEnvInteger "FCS_BraceMatchCacheSize" 5 let parseFileCacheSize = GetEnvInteger "FCS_ParseFileCacheSize" 2 let checkFileInProjectCacheSize = GetEnvInteger "FCS_CheckFileInProjectCacheSize" 10 From 32221041e817bb2e4cf818cdb887642d15f882b5 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 20 Nov 2023 15:33:31 +0100 Subject: [PATCH 146/222] Fixed snapshot version --- src/Compiler/Service/FSharpCheckerResults.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 242659e1ff5..11ace800551 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -310,6 +310,7 @@ type FSharpProjectSnapshot = member this.GetMd5Version() = Md5Hasher.empty |> Md5Hasher.addString this.ProjectFileName + |> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.FileName)) |> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) |> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) |> Md5Hasher.addStrings this.OtherOptions From b874823c6697738a5ca5a71a856245e7304dc736 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 20 Nov 2023 17:39:49 +0100 Subject: [PATCH 147/222] cache fix --- src/Compiler/Utilities/LruCache.fs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Utilities/LruCache.fs b/src/Compiler/Utilities/LruCache.fs index 1dfa3e65780..48308afc767 100644 --- a/src/Compiler/Utilities/LruCache.fs +++ b/src/Compiler/Utilities/LruCache.fs @@ -137,6 +137,8 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers // weaken all other versions (unless they're required to be kept) let versionsToWeaken = versionDict.Keys |> Seq.filter ((<>) version) |> Seq.toList + let mutable anythingWeakened = false + for otherVersion in versionsToWeaken do let node = versionDict[otherVersion] @@ -146,9 +148,12 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers node.Value <- key, otherVersion, label, Weak(WeakReference<_> value) weakList.AddFirst node event CacheEvent.Weakened (label, key, otherVersion) - cutWeakListIfTooLong () + anythingWeakened <- true | _ -> () + if anythingWeakened then + cutWeakListIfTooLong () + | false, _ -> let node = pushValueToTop key version label value dictionary[key] <- Dictionary() From 020c2f141519ebd6df9e0a775e739dd50daf620d Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 22 Nov 2023 14:42:11 +0100 Subject: [PATCH 148/222] this is actually faster, oops --- src/Compiler/Service/FSharpCheckerResults.fs | 35 ++++++++++--------- src/Compiler/Service/FSharpCheckerResults.fsi | 4 +-- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 11ace800551..a27285702d7 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -307,19 +307,20 @@ type FSharpProjectSnapshot = // TODO: DateTime.Now - member this.GetMd5Version() = + member this.GetMd5Version() = + ignore this Md5Hasher.empty - |> Md5Hasher.addString this.ProjectFileName - |> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.FileName)) - |> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) - |> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) - |> Md5Hasher.addStrings this.OtherOptions - |> Md5Hasher.addVersions ( - this.ReferencedProjects - |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key) - ) - |> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment - |> Md5Hasher.addBool this.UseScriptResolutionRules + //|> Md5Hasher.addString this.ProjectFileName + //|> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.FileName)) + //|> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) + //|> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) + //|> Md5Hasher.addStrings this.OtherOptions + //|> Md5Hasher.addVersions ( + // this.ReferencedProjects + // |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key) + //) + //|> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment + //|> Md5Hasher.addBool this.UseScriptResolutionRules member this.GetDebugVersion() : FSharpProjectSnapshotDebugVersion = { @@ -335,13 +336,13 @@ type FSharpProjectSnapshot = UseScriptResolutionRules = this.UseScriptResolutionRules } - interface ICacheKey with + interface ICacheKey with member this.GetLabel() = this.ToString() member this.GetKey() = this.ProjectFileName, this.OutputFileName |> Option.defaultValue "" - member this.GetVersion() = this.GetMd5Version() + member this.GetVersion() = this.GetDebugVersion() and FSharpProjectSnapshotWithSources = { @@ -424,7 +425,7 @@ and FSharpProjectSnapshotWithSources = member this.GetLabel() = this.ProjectSnapshot.Key.ToString() member this.GetKey() = this.ProjectSnapshot.Key.GetKey() - member this.GetVersion() = this.GetMd5Version() + member this.GetVersion() = this.GetDebugVersion() and FSharpProjectSnapshotWithSourcesDebugVersion = { @@ -432,7 +433,7 @@ and FSharpProjectSnapshotWithSourcesDebugVersion = SourceVersions: string list } -and FSharpProjectSnapshotWithSourcesVersion = string +and FSharpProjectSnapshotWithSourcesVersion = FSharpProjectSnapshotWithSourcesDebugVersion and FSharpProjectSnapshotDebugVersion = { @@ -445,7 +446,7 @@ and FSharpProjectSnapshotDebugVersion = UseScriptResolutionRules: bool } -and FSharpProjectSnapshotVersion = string +and FSharpProjectSnapshotVersion = FSharpProjectSnapshotDebugVersion and [] public FSharpReferencedProjectSnapshot = internal diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 96b7431a1b9..35cfaec1fbd 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -184,7 +184,7 @@ and internal FSharpProjectSnapshotWithSourcesDebugVersion = { ProjectSnapshotVersion: FSharpProjectSnapshotDebugVersion SourceVersions: string list } -and internal FSharpProjectSnapshotWithSourcesVersion = string +and internal FSharpProjectSnapshotWithSourcesVersion = FSharpProjectSnapshotWithSourcesDebugVersion and internal FSharpProjectSnapshotDebugVersion = { ProjectFileName: string @@ -195,7 +195,7 @@ and internal FSharpProjectSnapshotDebugVersion = IsIncompleteTypeCheckEnvironment: bool UseScriptResolutionRules: bool } -and internal FSharpProjectSnapshotVersion = string +and internal FSharpProjectSnapshotVersion = FSharpProjectSnapshotDebugVersion and [] internal FSharpReferencedProjectSnapshot = internal From 9587e061ab7589e24c423cdce671faf49168d72f Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 22 Nov 2023 15:13:59 +0100 Subject: [PATCH 149/222] f --- src/Compiler/Facilities/DiagnosticsLogger.fs | 2 +- src/Compiler/Service/FSharpCheckerResults.fs | 24 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fs b/src/Compiler/Facilities/DiagnosticsLogger.fs index 23cc703954b..589943a7bfc 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fs +++ b/src/Compiler/Facilities/DiagnosticsLogger.fs @@ -374,7 +374,7 @@ type CapturingDiagnosticsLogger(nm, ?eagerFormat) = let errors = diagnostics.ToArray() errors |> Array.iter diagnosticsLogger.DiagnosticSink -/// Type holds thread-static globals for use by the compile. +/// Type holds thread-static globals for use by the compiler. type internal DiagnosticsThreadStatics = [] static val mutable private buildPhase: BuildPhase diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index a27285702d7..2daa9898c0d 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -307,20 +307,20 @@ type FSharpProjectSnapshot = // TODO: DateTime.Now - member this.GetMd5Version() = + member this.GetMd5Version() = ignore this Md5Hasher.empty - //|> Md5Hasher.addString this.ProjectFileName - //|> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.FileName)) - //|> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) - //|> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) - //|> Md5Hasher.addStrings this.OtherOptions - //|> Md5Hasher.addVersions ( - // this.ReferencedProjects - // |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key) - //) - //|> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment - //|> Md5Hasher.addBool this.UseScriptResolutionRules + //|> Md5Hasher.addString this.ProjectFileName + //|> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.FileName)) + //|> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) + //|> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) + //|> Md5Hasher.addStrings this.OtherOptions + //|> Md5Hasher.addVersions ( + // this.ReferencedProjects + // |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key) + //) + //|> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment + //|> Md5Hasher.addBool this.UseScriptResolutionRules member this.GetDebugVersion() : FSharpProjectSnapshotDebugVersion = { From cdc6c0aeb1d8720cc6ce6983bda3c4ccd4e3f182 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 22 Nov 2023 20:03:09 +0100 Subject: [PATCH 150/222] back to node --- src/Compiler/FSharp.Compiler.Service.fsproj | 2 +- src/Compiler/Facilities/AsyncMemoize.fs | 59 ++------ src/Compiler/Facilities/prim-lexing.fs | 1 + src/Compiler/Service/BackgroundCompiler.fs | 13 +- src/Compiler/Service/TransparentCompiler.fs | 98 ++++++------ src/Compiler/Service/service.fs | 4 +- src/Compiler/Utilities/Activity.fsi | 4 +- .../CompilerService/AsyncMemoize.fs | 142 +++++++++++++----- 8 files changed, 181 insertions(+), 142 deletions(-) diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 0211c4f7206..902e0aa38ac 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -147,7 +147,6 @@ - @@ -160,6 +159,7 @@ + diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index c1e614f4235..92d46e729ad 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -8,6 +8,7 @@ open System.Threading open System.Threading.Tasks open FSharp.Compiler.Diagnostics +open FSharp.Compiler.BuildGraph [] module internal Utils = @@ -35,12 +36,12 @@ type internal MemoizeReply<'TValue> = | Existing of Task<'TValue> type internal MemoizeRequest<'TValue> = - | GetOrCompute of Async<'TValue> * CancellationToken + | GetOrCompute of NodeCode<'TValue> * CancellationToken | Sync [] type internal Job<'TValue> = - | Running of TaskCompletionSource<'TValue> * CancellationTokenSource * Async<'TValue> * DateTime + | Running of TaskCompletionSource<'TValue> * CancellationTokenSource * NodeCode<'TValue> * DateTime | Completed of 'TValue member this.DebuggerDisplay = @@ -77,42 +78,7 @@ type private KeyData<'TKey, 'TVersion> = Version: 'TVersion } -/// Tools for hashing things with MD5 into a string that can be used as a cache key. -module internal Md5Hasher = - let private md5 = - new ThreadLocal<_>(fun () -> System.Security.Cryptography.MD5.Create()) - - let private computeHash (bytes: byte array) = md5.Value.ComputeHash(bytes) - - let hashString (s: string) = - System.Text.Encoding.UTF8.GetBytes(s) |> computeHash - - let empty = String.Empty - - let addBytes (bytes: byte array) (s: string) = - let sbytes = s |> hashString - - Array.append sbytes bytes - |> computeHash - |> System.BitConverter.ToString - |> (fun x -> x.Replace("-", "")) - - let addString (s: string) (s2: string) = - s |> System.Text.Encoding.UTF8.GetBytes |> addBytes <| s2 - - let addSeq<'item> (items: 'item seq) (addItem: 'item -> string -> string) (s: string) = - items |> Seq.fold (fun s a -> addItem a s) s - - let addStrings strings = addSeq strings addString - - let addVersions<'a, 'b when 'a :> ICacheKey<'b, string>> (versions: 'a seq) (s: string) = - versions |> Seq.map (fun x -> x.GetVersion()) |> addStrings <| s - - let addBool (b: bool) (s: string) = - b |> BitConverter.GetBytes |> addBytes <| s - - let addDateTime (dt: System.DateTime) (s: string) = dt.Ticks.ToString() |> addString <| s type internal AsyncLock() = @@ -315,7 +281,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T log (Started, key) Interlocked.Increment &restarted |> ignore System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}" - let! result = Async.StartAsTask(computation, cancellationToken = cts.Token) + let! result = NodeCode.StartAsTask_ForTesting(computation, ct = cts.Token) post (key, (JobCompleted result)) with | :? OperationCanceledException -> @@ -408,26 +374,26 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T Version = key.GetVersion() } - async { - let! ct = Async.CancellationToken + node { + let! ct = NodeCode.CancellationToken - match! processRequest post (key, GetOrCompute(computation, ct)) |> Async.AwaitTask with + match! processRequest post (key, GetOrCompute(computation, ct)) |> NodeCode.AwaitTask with | New internalCt -> let linkedCtSource = CancellationTokenSource.CreateLinkedTokenSource(ct, internalCt) try return! - Async.StartAsTask( - async { + NodeCode.StartAsTask_ForTesting( + node { log (Started, key) let! result = computation post (key, (JobCompleted result)) return result }, - cancellationToken = linkedCtSource.Token + ct = linkedCtSource.Token ) - |> Async.AwaitTask + |> NodeCode.AwaitTask with | :? TaskCanceledException | :? OperationCanceledException as ex -> return raise ex @@ -435,7 +401,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T post (key, (JobFailed ex)) return raise ex - | Existing job -> return! job |> Async.AwaitTask + | Existing job -> return! job |> NodeCode.AwaitTask + } member _.Clear() = cache.Clear() diff --git a/src/Compiler/Facilities/prim-lexing.fs b/src/Compiler/Facilities/prim-lexing.fs index ea962721bf2..851c4de5683 100644 --- a/src/Compiler/Facilities/prim-lexing.fs +++ b/src/Compiler/Facilities/prim-lexing.fs @@ -9,6 +9,7 @@ open System.IO open System.Collections.Immutable open Internal.Utilities.Collections +open Internal.Utilities.Hashing type ISourceText = diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 22516c1433e..de7199af5d7 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -139,7 +139,7 @@ type internal IBackgroundCompiler = abstract member ParseAndCheckFileInProject: fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> - Async + NodeCode /// Parse and typecheck the whole project. abstract member ParseAndCheckProject: options: FSharpProjectOptions * userOpName: string -> NodeCode @@ -151,7 +151,7 @@ type internal IBackgroundCompiler = Async abstract member ParseFile: - fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> NodeCode + fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> Async /// Try to get recent approximate type check results for a file. abstract member TryGetRecentCheckResultsForFile: @@ -1622,17 +1622,16 @@ type internal BackgroundCompiler fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string - ) : Async = - async { + ) : NodeCode = + node { let fileSnapshot = projectSnapshot.SourceFiles |> Seq.find (fun f -> f.FileName = fileName) - let! sourceText = fileSnapshot.GetSource() |> Async.AwaitTask + let! sourceText = fileSnapshot.GetSource() |> NodeCode.AwaitTask let options = projectSnapshot.ToOptions() return! self.ParseAndCheckFileInProject(fileName, 0, sourceText, options, userOpName) - |> Async.AwaitNodeCode } member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = @@ -1649,7 +1648,7 @@ type internal BackgroundCompiler cache: bool, flatErrors: bool, userOpName: string - ) : Async = + ) = self.ParseFile(fileName, sourceText, options, cache, flatErrors, userOpName) member _.ParseFile(_fileName: string, _projectSnapshot: FSharpProjectSnapshot, _userOpName: string) = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 110f0117f75..e2108c48764 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -375,13 +375,11 @@ type internal TransparentCompiler caches.FrameworkImports.Get( key, - async { + node { use _ = Activity.start "ComputeFrameworkImports" [] let tcConfigP = TcConfigProvider.Constant tcConfig - return! - TcImports.BuildFrameworkTcImports(tcConfigP, frameworkDLLs, nonFrameworkResolutions) - |> Async.AwaitNodeCode + return! TcImports.BuildFrameworkTcImports(tcConfigP, frameworkDLLs, nonFrameworkResolutions) } ) @@ -401,14 +399,14 @@ type internal TransparentCompiler importsInvalidatedByTypeProvider: Event ) = - async { + node { let diagnosticsLogger = CompilationDiagnosticLogger("CombineImportedAssembliesTask", tcConfig.diagnosticsOptions) use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Parameter) let! tcImports = - async { + node { try let! tcImports = TcImports.BuildNonFrameworkTcImports( @@ -418,7 +416,6 @@ type internal TransparentCompiler unresolvedReferences, dependencyProvider ) - |> Async.AwaitNodeCode #if !NO_TYPEPROVIDERS // TODO: review and handle the event tcImports.GetCcusExcludingBase() @@ -618,7 +615,7 @@ type internal TransparentCompiler caches.BootstrapInfoStatic.Get( projectSnapshot.WithoutFileVersions.Key, - async { + node { use _ = Activity.start "ComputeBootstrapInfoStatic" @@ -753,7 +750,7 @@ type internal TransparentCompiler ) let computeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = - async { + node { let! assemblyName, outFile, sourceFiles, @@ -818,7 +815,7 @@ type internal TransparentCompiler caches.BootstrapInfo.Get( projectSnapshot.Key, - async { + node { use _ = Activity.start "ComputeBootstrapInfo" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] @@ -827,7 +824,7 @@ type internal TransparentCompiler use _ = new CompilationGlobalsScope(delayedLogger, BuildPhase.Parameter) let! bootstrapInfoOpt = - async { + node { try return! computeBootstrapInfoInner projectSnapshot with exn -> @@ -860,8 +857,8 @@ type internal TransparentCompiler // TODO: Not sure if we should cache this. For VS probably not. Maybe it can be configurable by FCS user. let LoadSource (file: FSharpFile) = - async { - let! source = file.Source.GetSource() |> Async.AwaitTask + node { + let! source = file.Source.GetSource() |> NodeCode.AwaitTask return { @@ -876,7 +873,7 @@ type internal TransparentCompiler } let LoadSources (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = - async { + node { let sourceFileMap = bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName, f) |> Map @@ -887,7 +884,7 @@ type internal TransparentCompiler |> sourceFileMap.TryFind |> Option.defaultWith (fun _ -> failwith $"File {f.FileName} not found in {projectSnapshot.Key.GetLabel()}") |> LoadSource) - |> Async.Parallel + |> NodeCode.Parallel return { @@ -914,7 +911,7 @@ type internal TransparentCompiler caches.ParseFile.Get( key, - async { + node { use _ = Activity.start "ComputeParseFile" @@ -960,7 +957,7 @@ type internal TransparentCompiler |> Graph.make let computeDependencyGraph (tcConfig: TcConfig) parsedInputs (processGraph: Graph -> Graph) = - async { + node { let sourceFiles: FileInProject array = parsedInputs |> Seq.toArray @@ -1073,7 +1070,7 @@ type internal TransparentCompiler let ComputeTcIntermediate (projectSnapshot: FSharpProjectSnapshotWithSources) (dependencyGraph: Graph) - (node: NodeToTypeCheck) + (nodeToCheck: NodeToTypeCheck) (parsedInput: ParsedInput, parseErrors) bootstrapInfo (prevTcInfo: TcInfo) @@ -1086,7 +1083,8 @@ type internal TransparentCompiler caches.TcIntermediate.Get( key, - async { + node { + let input = parsedInput let fileName = input.FileName @@ -1129,14 +1127,14 @@ type internal TransparentCompiler let input, moduleNamesDict = DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input - let! ct = Async.CancellationToken + let! ct = NodeCode.CancellationToken try - do! maxParallelismSemaphore.WaitAsync(ct) |> Async.AwaitTask + do! maxParallelismSemaphore.WaitAsync(ct) |> NodeCode.AwaitTask let! finisher = CheckOneInputWithCallback - node + nodeToCheck ((fun () -> hadParseErrors || diagnosticsLogger.ErrorCount > 0), tcConfig, tcImports, @@ -1147,6 +1145,7 @@ type internal TransparentCompiler input, true) |> Cancellable.toAsync + |> NodeCode.AwaitAsync //fileChecked.Trigger fileName @@ -1177,6 +1176,7 @@ type internal TransparentCompiler (fileNode: NodeToTypeCheck) tcInfo = + // TODO: should this be node? async { match fileNode with @@ -1185,6 +1185,7 @@ type internal TransparentCompiler let! tcIntermediate = ComputeTcIntermediate projectSnapshot dependencyFiles fileNode (input, parseErrors) bootstrapInfo tcInfo + |> Async.AwaitNodeCode let (Finisher (node = node; finisher = finisher)) = tcIntermediate.finisher @@ -1287,7 +1288,7 @@ type internal TransparentCompiler caches.TcLastFile.Get( projectSnapshot.FileKey fileName, - async { + node { let file = projectSnapshot.SourceFiles |> List.last use _ = @@ -1296,7 +1297,7 @@ type internal TransparentCompiler let! parsedInputs = projectSnapshot.SourceFiles |> Seq.map (ComputeParseFile projectSnapshot.ProjectSnapshot) - |> Async.Parallel + |> NodeCode.Parallel let! graph, dependencyFiles = ComputeDependencyGraphForFile bootstrapInfo.TcConfig projectSnapshot (parsedInputs |> Array.map p14) @@ -1307,6 +1308,7 @@ type internal TransparentCompiler graph (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles false) bootstrapInfo.InitialTcInfo + |> NodeCode.AwaitAsync let lastResult = results |> List.head |> snd @@ -1315,7 +1317,7 @@ type internal TransparentCompiler ) let getParseResult (projectSnapshot: FSharpProjectSnapshot) creationDiags file = - async { + node { let! parseTree, parseDiagnostics, sourceText, tcConfig = ComputeParseFile projectSnapshot file let parseDiagnostics = @@ -1349,7 +1351,7 @@ type internal TransparentCompiler let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = caches.ParseAndCheckFileInProject.Get( projectSnapshot.FileKey fileName, - async { + node { use _ = Activity.start "ComputeParseAndCheckFileInProject" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] @@ -1437,7 +1439,7 @@ type internal TransparentCompiler let ComputeParseAndCheckAllFilesInProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = caches.ParseAndCheckAllFilesInProject.Get( projectSnapshot.Key, - async { + node { use _ = Activity.start "ComputeParseAndCheckAllFilesInProject" @@ -1448,7 +1450,7 @@ type internal TransparentCompiler let! parsedInputs = projectSnapshot.SourceFiles |> Seq.map (ComputeParseFile projectSnapshot.ProjectSnapshot) - |> Async.Parallel + |> NodeCode.Parallel let! graph, dependencyFiles = ComputeDependencyGraphForProject bootstrapInfo.TcConfig projectSnapshot (parsedInputs |> Array.map p14) @@ -1458,13 +1460,14 @@ type internal TransparentCompiler graph (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles true) bootstrapInfo.InitialTcInfo + |> NodeCode.AwaitAsync } ) let ComputeProjectExtras (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = caches.ProjectExtras.Get( projectSnapshot.WithoutImplFilesThatHaveSignatures.Key, - async { + node { let! results, finalInfo = ComputeParseAndCheckAllFilesInProject bootstrapInfo projectSnapshot @@ -1558,7 +1561,7 @@ type internal TransparentCompiler let ComputeAssemblyData (projectSnapshot: FSharpProjectSnapshot) fileName = caches.AssemblyData.Get( projectSnapshot.WithoutImplFilesThatHaveSignatures.Key, - async { + node { let availableOnDiskModifiedTime = if FileSystem.FileExistsShim fileName then Some <| FileSystem.GetLastWriteTimeShim fileName @@ -1593,7 +1596,7 @@ type internal TransparentCompiler let ComputeParseAndCheckProject (projectSnapshot: FSharpProjectSnapshot) = caches.ParseAndCheckProject.Get( projectSnapshot.Key, - async { + node { match! ComputeBootstrapInfo projectSnapshot with | None, creationDiags -> @@ -1665,7 +1668,7 @@ type internal TransparentCompiler ) let tryGetSink (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = - async { + node { match! ComputeBootstrapInfo projectSnapshot with | None, _ -> return None | Some bootstrapInfo, _creationDiags -> @@ -1680,7 +1683,7 @@ type internal TransparentCompiler let ComputeSemanticClassification (fileName: string, projectSnapshot: FSharpProjectSnapshot) = caches.SemanticClassification.Get( projectSnapshot.FileKey fileName, - async { + node { use _ = Activity.start "ComputeSemanticClassification" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] @@ -1710,7 +1713,7 @@ type internal TransparentCompiler let ComputeItemKeyStore (fileName: string, projectSnapshot: FSharpProjectSnapshot) = caches.ItemKeyStore.Get( projectSnapshot.FileKey fileName, - async { + node { use _ = Activity.start "ComputeItemKeyStore" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] @@ -1745,7 +1748,7 @@ type internal TransparentCompiler ) member _.ParseFile(fileName, projectSnapshot: FSharpProjectSnapshot, _userOpName) = - async { + node { //use _ = // Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] @@ -1780,14 +1783,14 @@ type internal TransparentCompiler member _.FindReferencesInFile(fileName: string, projectSnapshot: FSharpProjectSnapshot, symbol: FSharpSymbol, userOpName: string) = ignore userOpName - async { + node { match! ComputeItemKeyStore(fileName, projectSnapshot) with | None -> return Seq.empty | Some itemKeyStore -> return itemKeyStore.FindAll symbol.Item } member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, fileName, _userOpName) = - ComputeAssemblyData projectSnapshot fileName |> NodeCode.AwaitAsync + ComputeAssemblyData projectSnapshot fileName member _.Caches = caches @@ -1815,7 +1818,6 @@ type internal TransparentCompiler let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) - |> NodeCode.AwaitAsync return result } @@ -1838,7 +1840,6 @@ type internal TransparentCompiler let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) - |> NodeCode.AwaitAsync return Some result } @@ -1874,14 +1875,12 @@ type internal TransparentCompiler return! this.FindReferencesInFile(fileName, snapshot, symbol, userOpName) - |> NodeCode.AwaitAsync } member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = node { return! this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) - |> NodeCode.AwaitAsync } member _.FrameworkImportsCache: FrameworkImportsCache = @@ -1912,7 +1911,6 @@ type internal TransparentCompiler match! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) - |> NodeCode.AwaitAsync with | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return parseResult, checkResult | parseResult, FSharpCheckFileAnswer.Aborted -> return parseResult, FSharpCheckFileResults.MakeEmpty(fileName, [||], true) @@ -1926,7 +1924,7 @@ type internal TransparentCompiler ) : NodeCode = node { let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! this.ParseFile(fileName, snapshot, userOpName) |> NodeCode.AwaitAsync + return! this.ParseFile(fileName, snapshot, userOpName) } member this.GetCachedCheckFileResult @@ -1945,7 +1943,6 @@ type internal TransparentCompiler match! this.ParseAndCheckFileInProject(fileName, snapshot, "GetCachedCheckFileResult") - |> NodeCode.AwaitAsync with | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return Some(parseResult, checkResult) | _, FSharpCheckFileAnswer.Aborted -> return None @@ -1982,7 +1979,7 @@ type internal TransparentCompiler member this.GetSemanticClassificationForFile(fileName: string, snapshot: FSharpProjectSnapshot, userOpName: string) = node { ignore userOpName - return! ComputeSemanticClassification(fileName, snapshot) |> NodeCode.AwaitAsync + return! ComputeSemanticClassification(fileName, snapshot) } member this.GetSemanticClassificationForFile @@ -1994,7 +1991,7 @@ type internal TransparentCompiler node { ignore userOpName let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! ComputeSemanticClassification(fileName, snapshot) |> NodeCode.AwaitAsync + return! ComputeSemanticClassification(fileName, snapshot) } member this.InvalidateConfiguration(options: FSharpProjectOptions, userOpName: string) : unit = @@ -2021,7 +2018,6 @@ type internal TransparentCompiler return! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) - |> NodeCode.AwaitAsync } member this.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = @@ -2031,17 +2027,17 @@ type internal TransparentCompiler node { ignore userOpName let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! ComputeParseAndCheckProject snapshot |> NodeCode.AwaitAsync + return! ComputeParseAndCheckProject snapshot } member this.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = node { ignore userOpName - return! ComputeParseAndCheckProject projectSnapshot |> NodeCode.AwaitAsync + return! ComputeParseAndCheckProject projectSnapshot } member this.ParseFile(fileName, projectSnapshot, userOpName) = - node { return! this.ParseFile(fileName, projectSnapshot, userOpName) |> NodeCode.AwaitAsync } + this.ParseFile(fileName, projectSnapshot, userOpName) |> Async.AwaitNodeCode member this.ParseFile ( @@ -2051,7 +2047,7 @@ type internal TransparentCompiler cache: bool, flatErrors: bool, userOpName: string - ) : Async = + ) = backgroundCompiler.ParseFile(fileName, sourceText, options, cache, flatErrors, userOpName) member this.TryGetRecentCheckResultsForFile diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 2821a5bfe85..5f6b593989b 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -321,7 +321,6 @@ type FSharpChecker let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.ParseFile(fileName, projectSnapshot, userOpName) - |> Async.AwaitNodeCode member ic.ParseFileInProject(fileName, source: string, options, ?cache: bool, ?userOpName: string) = let parsingOptions, _ = ic.GetParsingOptionsFromProjectOptions(options) @@ -451,6 +450,7 @@ type FSharpChecker member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.ParseAndCheckFileInProject(fileName, projectSnapshot, userOpName) + |> Async.AwaitNodeCode member _.ParseAndCheckProject(options: FSharpProjectOptions, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" @@ -496,7 +496,7 @@ type FSharpChecker let userOpName = defaultArg userOpName "Unknown" node { - let! parseResults = backgroundCompiler.ParseFile(fileName, projectSnapshot, userOpName) + let! parseResults = backgroundCompiler.ParseFile(fileName, projectSnapshot, userOpName) |> NodeCode.AwaitAsync if parseResults.ParseTree.Identifiers |> Set.contains symbol.DisplayNameCore diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index afce0f3b554..5e7519c34fb 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -1,4 +1,6 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + + +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. namespace FSharp.Compiler.Diagnostics open System diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 758e31b3147..d5822e7d408 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -7,17 +7,19 @@ open Internal.Utilities.Collections open System.Threading.Tasks open System.Diagnostics open System.Collections.Concurrent - +open FSharp.Compiler.DiagnosticsLogger +open FSharp.Compiler.Diagnostics +open FSharp.Compiler.BuildGraph [] let ``Stack trace`` () = let memoize = AsyncMemoize() - let computation key = async { + let computation key = node { // do! Async.Sleep 1 |> NodeCode.AwaitAsync - let! result = memoize.Get'(key * 2, async { + let! result = memoize.Get'(key * 2, node { //do! Async.Sleep 1 |> NodeCode.AwaitAsync return key * 5 }) @@ -27,7 +29,7 @@ let ``Stack trace`` () = //let _r2 = computation 10 - let result = memoize.Get'(1, computation 1) |> Async.RunSynchronously + let result = memoize.Get'(1, computation 1) |> NodeCode.RunImmediateWithoutCancellation Assert.Equal(10, result) @@ -35,8 +37,8 @@ let ``Stack trace`` () = [] let ``Basics``() = - let computation key = async { - do! Async.Sleep 1 + let computation key = node { + do! Async.Sleep 1 |> NodeCode.AwaitAsync return key * 2 } @@ -54,8 +56,8 @@ let ``Basics``() = memoize.Get'(3, computation 3) memoize.Get'(2, computation 2) } - |> Async.Parallel - |> Async.RunSynchronously + |> NodeCode.Parallel + |> NodeCode.RunImmediateWithoutCancellation let expected = [| 10; 10; 4; 10; 6; 4|] @@ -72,9 +74,9 @@ let ``We can cancel a job`` () = let jobStarted = new ManualResetEvent(false) - let computation key = async { + let computation key = node { jobStarted.Set() |> ignore - do! Async.Sleep 1000 + do! Async.Sleep 1000 |> NodeCode.AwaitAsync failwith "Should be canceled before it gets here" return key * 2 } @@ -89,9 +91,9 @@ let ``We can cancel a job`` () = let key = 1 - let _task1 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts1.Token) - let _task2 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts2.Token) - let _task3 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts3.Token) + let _task1 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts1.Token) + let _task2 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts2.Token) + let _task3 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts3.Token) jobStarted.WaitOne() |> ignore @@ -118,11 +120,11 @@ let ``Job is restarted if first requestor cancels`` () = task { let jobStarted = new ManualResetEvent(false) - let computation key = async { + let computation key = node { jobStarted.Set() |> ignore for _ in 1 .. 5 do - do! Async.Sleep 100 + do! Async.Sleep 100 |> NodeCode.AwaitAsync return key * 2 } @@ -137,10 +139,9 @@ let ``Job is restarted if first requestor cancels`` () = let key = 1 - let _task1 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts1.Token) - let _task2 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts2.Token) - let _task3 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts3.Token) - + let _task1 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts1.Token) + let _task2 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts2.Token) + let _task3 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts3.Token) jobStarted.WaitOne() |> ignore @@ -165,11 +166,11 @@ let ``Job is restarted if first requestor cancels but keeps running if second re task { let jobStarted = new ManualResetEvent(false) - let computation key = async { + let computation key = node { jobStarted.Set() |> ignore for _ in 1 .. 5 do - do! Async.Sleep 100 + do! Async.Sleep 100 |> NodeCode.AwaitAsync return key * 2 } @@ -184,12 +185,12 @@ let ``Job is restarted if first requestor cancels but keeps running if second re let key = 1 - let _task1 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts1.Token) + let _task1 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts1.Token) jobStarted.WaitOne() |> ignore jobStarted.Reset() |> ignore - let _task2 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts2.Token) - let _task3 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts3.Token) + let _task2 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts2.Token) + let _task3 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts3.Token) cts1.Cancel() @@ -240,21 +241,21 @@ let ``Stress test`` () = while (int s.ElapsedMilliseconds) < durationMs do number <- number + 1 % 12345 return [result] - } + } |> NodeCode.AwaitAsync let rec sleepyComputation durationMs result = - async { + node { if rng.NextDouble() < (exceptionProbability / (float durationMs / float stepMs)) then raise (ExpectedException()) if durationMs > 0 then - do! Async.Sleep (min stepMs durationMs) + do! Async.Sleep (min stepMs durationMs) |> NodeCode.AwaitAsync return! sleepyComputation (durationMs - stepMs) result else return [result] } let rec mixedComputation durationMs result = - async { + node { if durationMs > 0 then if rng.NextDouble() < 0.5 then let! _ = intenseComputation (min stepMs durationMs) () @@ -296,7 +297,7 @@ let ``Stress test`` () = let result = key * 2 let job = cache.Get'(key, computation durationMs result) let cts = new CancellationTokenSource() - let runningJob = Async.StartAsTask(job, cancellationToken = cts.Token) + let runningJob = NodeCode.StartAsTask_ForTesting(job, ct = cts.Token) cts.CancelAfter timeoutMs Interlocked.Increment &started |> ignore try @@ -342,10 +343,10 @@ let ``Cancel running jobs with the same key`` cancelDuplicate expectFinished = let mutable started = 0 let mutable finished = 0 - let work () = async { + let work () = node { Interlocked.Increment &started |> ignore for _ in 1..10 do - do! Async.Sleep 10 + do! Async.Sleep 10 |> NodeCode.AwaitAsync Interlocked.Increment &finished |> ignore } @@ -355,7 +356,7 @@ let ``Cancel running jobs with the same key`` cancelDuplicate expectFinished = member _.GetVersion() = 1 member _.GetLabel() = "key1" } - cache.Get(key1, work()) |> Async.Start + cache.Get(key1, work()) |> Async.AwaitNodeCode |> Async.Start do! Task.Delay 50 @@ -365,9 +366,82 @@ let ``Cancel running jobs with the same key`` cancelDuplicate expectFinished = member _.GetVersion() = key1.GetVersion() + 1 member _.GetLabel() = "key2" } - cache.Get(key2, work()) |> Async.Start + cache.Get(key2, work()) |> Async.AwaitNodeCode |> Async.Start do! Task.Delay 500 Assert.Equal((2, expectFinished), (started, finished)) - } \ No newline at end of file + } + + +type DummyException(msg) = + inherit Exception(msg) + +[] +let ``Preserve thread static diagnostics`` () = + + let seed = System.Random().Next() + + let rng = System.Random seed + + let job1Cache = AsyncMemoize() + let job2Cache = AsyncMemoize() + + let job1 (input: string) = node { + let! _ = Async.Sleep 10 |> NodeCode.AwaitAsync + let ex = DummyException("job1 error") + DiagnosticsThreadStatics.DiagnosticsLogger.ErrorR(ex) + return Ok input + } + + let job2 (input: int) = node { + + DiagnosticsThreadStatics.DiagnosticsLogger.Warning(DummyException("job2 error 1")) + + let! _ = Async.Sleep 10 |> NodeCode.AwaitAsync + + let key = { new ICacheKey<_, _> with + member _.GetKey() = "job1" + member _.GetVersion() = input + member _.GetLabel() = "job1" } + + let! result = job1Cache.Get(key, job1 "${input}" ) + + DiagnosticsThreadStatics.DiagnosticsLogger.Warning(DummyException("job2 error 2")) + + return input, result + + } + + let tasks = seq { + for i in 1 .. 100 do + + task { + let diagnosticsLogger = + CompilationDiagnosticLogger($"Testing task {i}", FSharpDiagnosticOptions.Default) + + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Optimize) + + DiagnosticsThreadStatics.DiagnosticsLogger.Warning(DummyException("task error")) + + + let key = { new ICacheKey<_, _> with + member _.GetKey() = "job2" + member _.GetVersion() = rng.Next(1, 10) + member _.GetLabel() = "job2" } + + let! result = job2Cache.Get(key, job2 (i % 10)) |> Async.AwaitNodeCode + + let diagnostics = diagnosticsLogger.GetDiagnostics() + + //Assert.Equal(3, diagnostics.Length) + + return result, diagnostics + } + } + + let results = (Task.WhenAll tasks).Result + + let diagnosticCounts = results |> Seq.map snd |> Seq.map Array.length |> Seq.groupBy id |> Seq.map (fun (k, v) -> k, v |> Seq.length) |> Seq.sortBy fst |> Seq.toList + + Assert.Equal<(int * int) list>([], diagnosticCounts) From 16427ac0e73c5957c24545ece100e86f1eba0d21 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 23 Nov 2023 13:03:38 +0100 Subject: [PATCH 151/222] fix --- src/Compiler/FSharp.Compiler.Service.fsproj | 1 + src/Compiler/Facilities/Hashing.fs | 42 ++++++++++++++++++++ src/Compiler/Service/FSharpCheckerResults.fs | 1 + src/Compiler/Service/TransparentCompiler.fs | 1 + 4 files changed, 45 insertions(+) create mode 100644 src/Compiler/Facilities/Hashing.fs diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 902e0aa38ac..d56595f4503 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -147,6 +147,7 @@ + diff --git a/src/Compiler/Facilities/Hashing.fs b/src/Compiler/Facilities/Hashing.fs new file mode 100644 index 00000000000..5e49bb1fae4 --- /dev/null +++ b/src/Compiler/Facilities/Hashing.fs @@ -0,0 +1,42 @@ +namespace Internal.Utilities.Hashing + +open System +open System.Threading + +/// Tools for hashing things with MD5 into a string that can be used as a cache key. +module internal Md5Hasher = + + let private md5 = + new ThreadLocal<_>(fun () -> System.Security.Cryptography.MD5.Create()) + + let private computeHash (bytes: byte array) = md5.Value.ComputeHash(bytes) + + let hashString (s: string) = + System.Text.Encoding.UTF8.GetBytes(s) |> computeHash + + let empty = String.Empty + + let addBytes (bytes: byte array) (s: string) = + let sbytes = s |> hashString + + Array.append sbytes bytes + |> computeHash + |> System.BitConverter.ToString + |> (fun x -> x.Replace("-", "")) + + let addString (s: string) (s2: string) = + s |> System.Text.Encoding.UTF8.GetBytes |> addBytes <| s2 + + let addSeq<'item> (items: 'item seq) (addItem: 'item -> string -> string) (s: string) = + items |> Seq.fold (fun s a -> addItem a s) s + + let addStrings strings = addSeq strings addString + + // If we use this make it an extension method? + //let addVersions<'a, 'b when 'a :> ICacheKey<'b, string>> (versions: 'a seq) (s: string) = + // versions |> Seq.map (fun x -> x.GetVersion()) |> addStrings <| s + + let addBool (b: bool) (s: string) = + b |> BitConverter.GetBytes |> addBytes <| s + + let addDateTime (dt: System.DateTime) (s: string) = dt.Ticks.ToString() |> addString <| s \ No newline at end of file diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 2daa9898c0d..4188b9a5cfe 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -56,6 +56,7 @@ open Internal.Utilities.Collections open FSharp.Compiler.AbstractIL.ILBinaryReader open System.Threading.Tasks open System.Runtime.CompilerServices +open Internal.Utilities.Hashing type FSharpUnresolvedReferencesSet = FSharpUnresolvedReferencesSet of UnresolvedAssemblyReference list diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index e2108c48764..5efdfec92c7 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -42,6 +42,7 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.CreateILModule open FSharp.Compiler.TypedTreeOps open System.Threading +open Internal.Utilities.Hashing type internal FSharpFile = { From 8622d85c657887a140add9894bccc9cd3511c792 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 23 Nov 2023 14:14:58 +0100 Subject: [PATCH 152/222] F --- src/Compiler/Driver/GraphChecking/Graph.fs | 2 +- .../Driver/GraphChecking/GraphProcessing.fs | 4 +- src/Compiler/Driver/ParseAndCheckInputs.fs | 16 ++++++-- src/Compiler/Facilities/AsyncMemoize.fs | 40 +++++++------------ src/Compiler/Facilities/TaskAgent.fs | 7 +--- src/Compiler/Service/BackgroundCompiler.fs | 24 +++++------ src/Compiler/Service/FSharpCheckerResults.fs | 30 +++++++++----- src/Compiler/Service/FSharpCheckerResults.fsi | 14 ++++++- src/Compiler/Service/IncrementalBuild.fsi | 1 + src/Compiler/Service/TransparentCompiler.fs | 25 ++++++------ src/Compiler/Service/service.fsi | 6 ++- src/Compiler/Utilities/LruCache.fs | 11 ++--- 12 files changed, 99 insertions(+), 81 deletions(-) diff --git a/src/Compiler/Driver/GraphChecking/Graph.fs b/src/Compiler/Driver/GraphChecking/Graph.fs index 865be5cb086..bd0a3ee824a 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fs +++ b/src/Compiler/Driver/GraphChecking/Graph.fs @@ -56,7 +56,7 @@ module internal Graph = let relevant n = n = node || allDeps |> Array.contains n graph - |> Seq.choose (fun (KeyValue (src, deps)) -> + |> Seq.choose (fun (KeyValue(src, deps)) -> if relevant src then Some(src, deps |> Array.filter relevant) else diff --git a/src/Compiler/Driver/GraphChecking/GraphProcessing.fs b/src/Compiler/Driver/GraphChecking/GraphProcessing.fs index 886c4a4b954..afe491b4b74 100644 --- a/src/Compiler/Driver/GraphChecking/GraphProcessing.fs +++ b/src/Compiler/Driver/GraphChecking/GraphProcessing.fs @@ -156,7 +156,7 @@ let processGraph<'Item, 'Result when 'Item: equality and 'Item: comparison> // If we stopped early due to an exception, reraise it. match getExn () with | None -> () - | Some (item, ex) -> raise (GraphProcessingException($"Encountered exception when processing item '{item}'", ex)) + | Some(item, ex) -> raise (GraphProcessingException($"Encountered exception when processing item '{item}'", ex)) // All calculations succeeded - extract the results and sort in input order. nodes.Values @@ -251,7 +251,7 @@ let processGraphAsync<'Item, 'Result when 'Item: equality and 'Item: comparison> let! res = processNode node |> Async.Catch match res with - | Choice1Of2 () -> () + | Choice1Of2() -> () | Choice2Of2 ex -> raiseExn (node.Info.Item, ex) }, cts.Token diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index e9f172bdcec..5a23c95ca7b 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1484,7 +1484,8 @@ let CheckOneInputWithCallback tcSink, tcState: TcState, input: ParsedInput, - _skipImplIfSigExists: bool): (unit -> bool) * TcConfig * TcImports * TcGlobals * LongIdent option * TcResultsSink * TcState * ParsedInput * bool) + _skipImplIfSigExists: bool): + (unit -> bool) * TcConfig * TcImports * TcGlobals * LongIdent option * TcResultsSink * TcState * ParsedInput * bool) : Cancellable> = cancellable { try @@ -1763,7 +1764,7 @@ let TransformDependencyGraph (graph: Graph, filePairs: FilePairMap) = // Transform the graph to include ArtificialImplFile nodes when necessary. graph - |> Seq.collect (fun (KeyValue (fileIdx, deps)) -> + |> Seq.collect (fun (KeyValue(fileIdx, deps)) -> if filePairs.IsSignature fileIdx then // Add an additional ArtificialImplFile node for the signature file. [| @@ -1778,7 +1779,16 @@ let TransformDependencyGraph (graph: Graph, filePairs: FilePairMap) = /// Constructs a file dependency graph and type-checks the files in parallel where possible. let CheckMultipleInputsUsingGraphMode - ((ctok, checkForErrors, tcConfig: TcConfig, tcImports: TcImports, tcGlobals, prefixPathOpt, tcState, eagerFormat, inputs): 'a * (unit -> bool) * TcConfig * TcImports * TcGlobals * LongIdent option * TcState * (PhasedDiagnostic -> PhasedDiagnostic) * ParsedInput list) + ((ctok, checkForErrors, tcConfig: TcConfig, tcImports: TcImports, tcGlobals, prefixPathOpt, tcState, eagerFormat, inputs): + 'a * + (unit -> bool) * + TcConfig * + TcImports * + TcGlobals * + LongIdent option * + TcState * + (PhasedDiagnostic -> PhasedDiagnostic) * + ParsedInput list) : FinalFileResult list * TcState = use cts = new CancellationTokenSource() diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index c1e614f4235..c8cf7f7de64 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -45,7 +45,7 @@ type internal Job<'TValue> = member this.DebuggerDisplay = match this with - | Running (_, cts, _, ts) -> + | Running(_, cts, _, ts) -> let cancellation = if cts.IsCancellationRequested then " ! Cancellation Requested" @@ -135,12 +135,7 @@ type internal AsyncLock() = [] type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> - ( - ?keepStrongly, - ?keepWeakly, - ?name: string, - ?cancelDuplicateRunningJobs: bool - ) = + (?keepStrongly, ?keepWeakly, ?name: string, ?cancelDuplicateRunningJobs: bool) = let name = defaultArg name "N/A" let cancelDuplicateRunningJobs = defaultArg cancelDuplicateRunningJobs false @@ -230,10 +225,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T return match msg, cached with | Sync, _ -> New Unchecked.defaultof<_> - | GetOrCompute _, Some (Completed result) -> + | GetOrCompute _, Some(Completed result) -> Interlocked.Increment &hits |> ignore Existing(Task.FromResult result) - | GetOrCompute (_, ct), Some (Running (tcs, _, _, _)) -> + | GetOrCompute(_, ct), Some(Running(tcs, _, _, _)) -> Interlocked.Increment &hits |> ignore incrRequestCount key @@ -244,7 +239,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T Existing tcs.Task - | GetOrCompute (computation, ct), None -> + | GetOrCompute(computation, ct), None -> Interlocked.Increment &started |> ignore incrRequestCount key @@ -259,7 +254,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T otherVersions |> Seq.choose (function - | v, Running (_tcs, cts, _, _) -> Some(v, cts) + | v, Running(_tcs, cts, _, _) -> Some(v, cts) | _ -> None) |> Seq.iter (fun (_v, cts) -> use _ = Activity.start $"{name}: Duplicate running job" [| "key", key.Label |] @@ -289,7 +284,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T match action, cached with - | OriginatorCanceled, Some (Running (tcs, cts, computation, _)) -> + | OriginatorCanceled, Some(Running(tcs, cts, computation, _)) -> decrRequestCount key @@ -326,7 +321,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cts.Token) |> ignore - | CancelRequest, Some (Running (tcs, cts, _c, _)) -> + | CancelRequest, Some(Running(tcs, cts, _c, _)) -> decrRequestCount key @@ -344,11 +339,11 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T // Probably in some cases cancellation can be fired off even after we just unregistered it | CancelRequest, None - | CancelRequest, Some (Completed _) + | CancelRequest, Some(Completed _) | OriginatorCanceled, None - | OriginatorCanceled, Some (Completed _) -> () + | OriginatorCanceled, Some(Completed _) -> () - | JobFailed ex, Some (Running (tcs, _cts, _c, _ts)) -> + | JobFailed ex, Some(Running(tcs, _cts, _c, _ts)) -> cancelRegistration key cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore @@ -357,7 +352,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T failures.Add(key.Label, ex) tcs.TrySetException ex |> ignore - | JobCompleted result, Some (Running (tcs, _cts, _c, started)) -> + | JobCompleted result, Some(Running(tcs, _cts, _c, started)) -> cancelRegistration key cache.Set(key.Key, key.Version, key.Label, (Completed result)) requestCounts.Remove key |> ignore @@ -379,9 +374,9 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | JobCompleted _, None -> internalError key.Label "Invalid state: Running job missing in cache (completed)" - | JobFailed ex, Some (Completed _job) -> internalError key.Label $"Invalid state: Failed Completed job \n%A{ex}" + | JobFailed ex, Some(Completed _job) -> internalError key.Label $"Invalid state: Failed Completed job \n%A{ex}" - | JobCompleted _result, Some (Completed _job) -> internalError key.Label "Invalid state: Double-Completed job" + | JobCompleted _result, Some(Completed _job) -> internalError key.Label "Invalid state: Double-Completed job" }) } @@ -500,12 +495,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T [] type internal AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> - ( - ?keepStrongly, - ?keepWeakly, - ?name: string, - ?cancelDuplicateRunningJobs: bool - ) = + (?keepStrongly, ?keepWeakly, ?name: string, ?cancelDuplicateRunningJobs: bool) = do ignore (keepStrongly, keepWeakly, name, cancelDuplicateRunningJobs) diff --git a/src/Compiler/Facilities/TaskAgent.fs b/src/Compiler/Facilities/TaskAgent.fs index 5fdafa22127..0f978343af8 100644 --- a/src/Compiler/Facilities/TaskAgent.fs +++ b/src/Compiler/Facilities/TaskAgent.fs @@ -45,10 +45,7 @@ type TaskInbox<'Msg, 'MsgNoReply, 'Reply>() = [] type TaskAgent<'Msg, 'MsgNoReply, 'Reply> - ( - processMessage: ('MsgNoReply -> unit) -> 'Msg -> 'Reply, - processMessageNoReply: ('MsgNoReply -> unit) -> 'MsgNoReply -> unit - ) = + (processMessage: ('MsgNoReply -> unit) -> 'Msg -> 'Reply, processMessageNoReply: ('MsgNoReply -> unit) -> 'MsgNoReply -> unit) = let inbox = new TaskInbox<'Msg, 'MsgNoReply, 'Reply>() let exceptionEvent = new Event<_>() @@ -59,7 +56,7 @@ type TaskAgent<'Msg, 'MsgNoReply, 'Reply> backgroundTask { while running do match! inbox.Receive() with - | ExpectsReply (msg, replySource) -> + | ExpectsReply(msg, replySource) -> try let reply = processMessage inbox.Post msg replySource.SetResult reply diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 22516c1433e..0bd026be4c8 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -277,7 +277,7 @@ type internal BackgroundCompiler for r in options.ReferencedProjects do match r with - | FSharpReferencedProject.FSharpReference (nm, opts) -> + | FSharpReferencedProject.FSharpReference(nm, opts) -> // Don't use cross-project references for FSharp.Core, since various bits of code // require a concrete FSharp.Core to exist on-disk. The only solutions that have // these cross-project references to FSharp.Core are VisualFSharp.sln and FSharp.sln. The ramification @@ -303,7 +303,7 @@ type internal BackgroundCompiler member x.FileName = nm } - | FSharpReferencedProject.PEReference (getStamp, delayedReader) -> + | FSharpReferencedProject.PEReference(getStamp, delayedReader) -> { new IProjectReference with member x.EvaluateRawContents() = node { @@ -324,7 +324,7 @@ type internal BackgroundCompiler member x.FileName = delayedReader.OutputFile } - | FSharpReferencedProject.ILModuleReference (nm, getStamp, getReader) -> + | FSharpReferencedProject.ILModuleReference(nm, getStamp, getReader) -> { new IProjectReference with member x.EvaluateRawContents() = node { @@ -673,7 +673,7 @@ type internal BackgroundCompiler | parseResults, checkResults, _, priorTimeStamp when (match builder.GetCheckResultsBeforeFileInProjectEvenIfStale fileName with | None -> false - | Some (tcPrior) -> + | Some(tcPrior) -> tcPrior.ProjectTimeStamp = priorTimeStamp && builder.AreCheckResultsBeforeFileInProjectReady(fileName)) -> @@ -745,7 +745,7 @@ type internal BackgroundCompiler node { match! bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) with - | Some (_, results) -> return FSharpCheckFileAnswer.Succeeded results + | Some(_, results) -> return FSharpCheckFileAnswer.Succeeded results | _ -> let lazyCheckFile = getCheckFileNode (parseResults, sourceText, fileName, options, fileVersion, builder, tcPrior, tcInfo, creationDiags) @@ -781,15 +781,15 @@ type internal BackgroundCompiler match builderOpt with | Some builder -> match! bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) with - | Some (_, checkResults) -> return Some(builder, creationDiags, Some(FSharpCheckFileAnswer.Succeeded checkResults)) + | Some(_, checkResults) -> return Some(builder, creationDiags, Some(FSharpCheckFileAnswer.Succeeded checkResults)) | _ -> return Some(builder, creationDiags, None) | _ -> return None // the builder wasn't ready } match cachedResults with | None -> return None - | Some (_, _, Some x) -> return Some x - | Some (builder, creationDiags, None) -> + | Some(_, _, Some x) -> return Some x + | Some(builder, creationDiags, None) -> Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CheckFileInProjectAllowingStaleCachedResults.CacheMiss", fileName) match builder.GetCheckResultsBeforeFileInProjectEvenIfStale fileName with @@ -844,7 +844,7 @@ type internal BackgroundCompiler let! cachedResults = bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) match cachedResults with - | Some (_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults + | Some(_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults | _ -> let! tcPrior = builder.GetCheckResultsBeforeFileInProject fileName let! tcInfo = tcPrior.GetOrComputeTcInfo() @@ -894,7 +894,7 @@ type internal BackgroundCompiler let! cachedResults = bc.GetCachedCheckFileResult(builder, fileName, sourceText, options) match cachedResults with - | Some (parseResults, checkResults) -> return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) + | Some(parseResults, checkResults) -> return (parseResults, FSharpCheckFileAnswer.Succeeded checkResults) | _ -> let! tcPrior = builder.GetCheckResultsBeforeFileInProject fileName let! tcInfo = tcPrior.GetOrComputeTcInfo() @@ -1150,7 +1150,7 @@ type internal BackgroundCompiler match resOpt with | Some res -> match res.TryPeekValue() with - | ValueSome (a, b, c, _) -> Some(a, b, c) + | ValueSome(a, b, c, _) -> Some(a, b, c) | ValueNone -> None | None -> None | None -> None @@ -1252,7 +1252,7 @@ type internal BackgroundCompiler match tryGetBuilderNode options with | Some lazyWork -> match lazyWork.TryPeekValue() with - | ValueSome (Some builder, _) -> Some(builder.GetLogicalTimeStampForProject(cache)) + | ValueSome(Some builder, _) -> Some(builder.GetLogicalTimeStampForProject(cache)) | _ -> None | _ -> None diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index c77ffd46029..e803d6d395a 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -172,7 +172,7 @@ type FSharpProjectSnapshot = static member UseSameProject(options1, options2) = match options1.ProjectId, options2.ProjectId with - | Some (projectId1), Some (projectId2) when + | Some(projectId1), Some(projectId2) when not (String.IsNullOrWhiteSpace(projectId1)) && not (String.IsNullOrWhiteSpace(projectId2)) -> @@ -195,7 +195,7 @@ type FSharpProjectSnapshot = && (options1.ReferencedProjects, options2.ReferencedProjects) ||> List.forall2 (fun r1 r2 -> match r1, r2 with - | FSharpReferencedProjectSnapshot.FSharpReference (n1, a), FSharpReferencedProjectSnapshot.FSharpReference (n2, b) -> + | FSharpReferencedProjectSnapshot.FSharpReference(n1, a), FSharpReferencedProjectSnapshot.FSharpReference(n2, b) -> n1 = n2 && FSharpProjectSnapshot.AreSameForChecking(a, b)) && options1.LoadTime = options2.LoadTime @@ -330,7 +330,7 @@ type FSharpProjectSnapshot = OtherOptions = this.OtherOptions ReferencedProjects = [ - for FSharpReference (_, p) in this.ReferencedProjects -> p.WithoutImplFilesThatHaveSignatures.GetDebugVersion() + for FSharpReference(_, p) in this.ReferencedProjects -> p.WithoutImplFilesThatHaveSignatures.GetDebugVersion() ] IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment UseScriptResolutionRules = this.UseScriptResolutionRules @@ -463,7 +463,7 @@ and [] public FSharpReferencedProjectSnapshot = /// member this.OutputFile = match this with - | FSharpReference (projectOutputFile, _) -> projectOutputFile + | FSharpReference(projectOutputFile, _) -> projectOutputFile /// /// Creates a reference for an F# project. The physical data for it is stored/cached inside of the compiler service. @@ -477,7 +477,7 @@ and [] public FSharpReferencedProjectSnapshot = match o with | :? FSharpReferencedProjectSnapshot as o -> match this, o with - | FSharpReference (projectOutputFile1, options1), FSharpReference (projectOutputFile2, options2) -> + | FSharpReference(projectOutputFile1, options1), FSharpReference(projectOutputFile2, options2) -> projectOutputFile1 = projectOutputFile2 && options1 = options2 | _ -> false @@ -486,7 +486,7 @@ and [] public FSharpReferencedProjectSnapshot = member this.Key = match this with - | FSharpReference (_, snapshot) -> snapshot.Key + | FSharpReference(_, snapshot) -> snapshot.Key [] type FSharpReferencedProject = @@ -572,7 +572,7 @@ type FSharpProjectSnapshot with ReferencedProjects = this.ReferencedProjects |> Seq.map (function - | FSharpReference (name, opts) -> FSharpReferencedProject.FSharpReference(name, opts.ToOptions())) + | FSharpReference(name, opts) -> FSharpReferencedProject.FSharpReference(name, opts.ToOptions())) |> Seq.toArray IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment UseScriptResolutionRules = this.UseScriptResolutionRules @@ -595,7 +595,7 @@ type FSharpProjectSnapshot with let! referencedProjects = options.ReferencedProjects |> Seq.choose (function - | FSharpReferencedProject.FSharpReference (outputName, options) -> + | FSharpReferencedProject.FSharpReference(outputName, options) -> Some( async { let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, snapshotAccumulator) @@ -3859,7 +3859,19 @@ type FSharpCheckProjectResults tcConfigOption: TcConfig option, keepAssemblyContents: bool, diagnostics: FSharpDiagnostic[], - details: (TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * Choice> * TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * AccessorDomain * CheckedImplFile list option * string[] * FSharpProjectOptions) option + details: + (TcGlobals * + TcImports * + CcuThunk * + ModuleOrNamespaceType * + Choice> * + TopAttribs option * + (unit -> IRawFSharpAssemblyData option) * + ILAssemblyRef * + AccessorDomain * + CheckedImplFile list option * + string[] * + FSharpProjectOptions) option ) = let getDetails () = diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 35cfaec1fbd..d3d06969156 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -733,7 +733,19 @@ type public FSharpCheckProjectResults = tcConfigOption: TcConfig option * keepAssemblyContents: bool * diagnostics: FSharpDiagnostic[] * - details: (TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * Choice> * TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * AccessorDomain * CheckedImplFile list option * string[] * FSharpProjectOptions) option -> + details: + (TcGlobals * + TcImports * + CcuThunk * + ModuleOrNamespaceType * + Choice> * + TopAttribs option * + (unit -> IRawFSharpAssemblyData option) * + ILAssemblyRef * + AccessorDomain * + CheckedImplFile list option * + string[] * + FSharpProjectOptions) option -> FSharpCheckProjectResults module internal ParseAndCheckFile = diff --git a/src/Compiler/Service/IncrementalBuild.fsi b/src/Compiler/Service/IncrementalBuild.fsi index e678aa52ee3..0dedfb02948 100644 --- a/src/Compiler/Service/IncrementalBuild.fsi +++ b/src/Compiler/Service/IncrementalBuild.fsi @@ -154,6 +154,7 @@ type internal RawFSharpAssemblyDataBackedByLanguageService = assemblyName: string * ilAssemRef: FSharp.Compiler.AbstractIL.IL.ILAssemblyRef -> RawFSharpAssemblyDataBackedByLanguageService + interface IRawFSharpAssemblyData /// Manages an incremental build graph for the build of an F# project diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 110f0117f75..fff41051310 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -223,12 +223,12 @@ module private TypeCheckingGraphProcessing = async { let workWrapper - (getProcessedNode: NodeToTypeCheck - -> ProcessedNode>) + (getProcessedNode: + NodeToTypeCheck -> ProcessedNode>) (node: NodeInfo) : Async> = async { - let folder (state: TcInfo) (Finisher (finisher = finisher)) : TcInfo = finisher state |> snd + let folder (state: TcInfo) (Finisher(finisher = finisher)) : TcInfo = finisher state |> snd let deps = node.Deps |> Array.except [| node.Item |] |> Array.map getProcessedNode let transitiveDeps = @@ -245,14 +245,14 @@ module private TypeCheckingGraphProcessing = let! results = processGraphAsync graph workWrapper - let finalFileResults, state: (int * PartialResult) list * TcInfo = + let finalFileResults, state = (([], emptyState), results |> Array.choose (fun (item, res) -> match item with | NodeToTypeCheck.ArtificialImplFile _ -> None | NodeToTypeCheck.PhysicalFile file -> Some(file, res))) - ||> Array.fold (fun (fileResults, state) (item, (_, Finisher (finisher = finisher))) -> + ||> Array.fold (fun (fileResults, state) (item, (_, Finisher(finisher = finisher))) -> let fileResult, state = finisher state (item, fileResult) :: fileResults, state) @@ -499,7 +499,7 @@ type internal TransparentCompiler for r in project.ReferencedProjects do match r with - | FSharpReferencedProjectSnapshot.FSharpReference (nm, projectSnapshot) -> + | FSharpReferencedProjectSnapshot.FSharpReference(nm, projectSnapshot) -> // Don't use cross-project references for FSharp.Core, since various bits of code // require a concrete FSharp.Core to exist on-disk. The only solutions that have // these cross-project references to FSharp.Core are VisualFSharp.sln and FSharp.sln. The ramification @@ -543,7 +543,7 @@ type internal TransparentCompiler let getSwitchValue (switchString: string) = match commandLineArgs |> List.tryFindIndex (fun s -> s.StartsWithOrdinal switchString) with - | Some idx -> Some(commandLineArgs[ idx ].Substring(switchString.Length)) + | Some idx -> Some(commandLineArgs[idx].Substring(switchString.Length)) | _ -> None let sdkDirOverride = @@ -644,7 +644,7 @@ type internal TransparentCompiler loadClosure.References |> List.tryFind (fun (resolved, _) -> resolved = reference.Text) with - | Some (resolved, closureReferences) -> + | Some(resolved, closureReferences) -> for closureReference in closureReferences do yield AssemblyReference(closureReference.originalReference.Range, resolved, None) | None -> yield reference @@ -701,7 +701,7 @@ type internal TransparentCompiler let basicDependencies = [ - for UnresolvedAssemblyReference (referenceText, _) in unresolvedReferences do + for UnresolvedAssemblyReference(referenceText, _) in unresolvedReferences do // Exclude things that are definitely not a file name if not (FileSystem.IsInvalidPathShim referenceText) then let file = @@ -1186,7 +1186,7 @@ type internal TransparentCompiler let! tcIntermediate = ComputeTcIntermediate projectSnapshot dependencyFiles fileNode (input, parseErrors) bootstrapInfo tcInfo - let (Finisher (node = node; finisher = finisher)) = tcIntermediate.finisher + let (Finisher(node = node; finisher = finisher)) = tcIntermediate.finisher return Finisher( @@ -1526,11 +1526,10 @@ type internal TransparentCompiler // We return 'None' for the assembly portion of the cross-assembly reference let hasTypeProviderAssemblyAttrib = topAttrs.assemblyAttrs - |> List.exists (fun (Attrib (tcref, _, _, _, _, _, _)) -> + |> List.exists (fun (Attrib(tcref, _, _, _, _, _, _)) -> let nm = tcref.CompiledRepresentationForNamedType.BasicQualifiedName - nm = typeof - .FullName) + nm = typeof.FullName) if tcState.CreatesGeneratedProvidedTypes || hasTypeProviderAssemblyAttrib then ProjectAssemblyDataResult.Unavailable true diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index 6d72ca72dd5..db6d5736077 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -58,8 +58,10 @@ type public FSharpChecker = ?enablePartialTypeChecking: bool * ?parallelReferenceResolution: bool * ?captureIdentifiersWhenParsing: bool * - [] ?documentSource: DocumentSource * - [] ?useSyntaxTreeCache: bool * + [] ?documentSource: + DocumentSource * + [] ?useSyntaxTreeCache: + bool * [] ?useTransparentCompiler: bool -> FSharpChecker diff --git a/src/Compiler/Utilities/LruCache.fs b/src/Compiler/Utilities/LruCache.fs index 48308afc767..b0308b951b8 100644 --- a/src/Compiler/Utilities/LruCache.fs +++ b/src/Compiler/Utilities/LruCache.fs @@ -22,12 +22,7 @@ type internal ValueLink<'T when 'T: not struct> = [] type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality and 'TValue: not struct> - ( - keepStrongly, - ?keepWeakly, - ?requiredToKeep, - ?event - ) = + (keepStrongly, ?keepWeakly, ?requiredToKeep, ?event) = let keepWeakly = defaultArg keepWeakly 100 let requiredToKeep = defaultArg requiredToKeep (fun _ -> false) @@ -50,7 +45,7 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers match w.TryGetTarget() with | false, _ -> weakList.Remove node - dictionary[ key ].Remove version |> ignore + dictionary[key].Remove version |> ignore if dictionary[key].Count = 0 then dictionary.Remove key |> ignore @@ -71,7 +66,7 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers let previous = node.Previous let key, version, label, _ = node.Value weakList.Remove node - dictionary[ key ].Remove version |> ignore + dictionary[key].Remove version |> ignore if dictionary[key].Count = 0 then dictionary.Remove key |> ignore From d99b13de021f2fa56b6a0d3ac36d0669698c866b Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 23 Nov 2023 14:19:29 +0100 Subject: [PATCH 153/222] BackgorundCompiler sync --- src/Compiler/Service/BackgroundCompiler.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 0bd026be4c8..804198a84ff 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -916,8 +916,8 @@ type internal BackgroundCompiler parsingOptions, userOpName, suggestNamesForErrors, - captureIdentifiersWhenParsing, builder.TcConfig.flatErrors, + captureIdentifiersWhenParsing, ct ) From 069ab144d4a2d414d0bf5555594ea4c246ef3d2d Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 23 Nov 2023 14:35:21 +0100 Subject: [PATCH 154/222] fix --- .../FSharp.Editor/LanguageService/WorkspaceExtensions.fs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index da49418276c..6c810a16698 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -10,6 +10,7 @@ open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols +open FSharp.Compiler.BuildGraph open CancellableTasks @@ -132,11 +133,12 @@ module private CheckerExtensions = snapshotCache.Get( key, - async { - let! ct = Async.CancellationToken - return! getProjectSnapshot None document.Project ct |> Async.AwaitTask + node { + let! ct = NodeCode.CancellationToken + return! getProjectSnapshot None document.Project ct |> NodeCode.AwaitTask } ) + |> Async.AwaitNodeCode type FSharpChecker with From ad0af09a773ed367a67adb3dccbc4d19011026f5 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 23 Nov 2023 15:12:01 +0100 Subject: [PATCH 155/222] BDN update --- eng/Versions.props | 2 +- .../CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index c96ae7ba999..a5c8cbe59e2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -181,7 +181,7 @@ 1.0.0 1.1.33 - 0.13.2 + 0.13.10 2.16.5 4.3.0.0 1.0.31 diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs index aeeb39b752d..959acc28ae8 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs @@ -234,7 +234,7 @@ type TestProjectType = [] [] [] -[] +[] type TransparentCompilerBenchmark() = let size = 30 @@ -338,7 +338,7 @@ type TransparentCompilerBenchmark() = [] [] [] -[] +[] type TransparentCompilerGiraffeBenchmark() = let mutable benchmark : ProjectWorkflowBuilder = Unchecked.defaultof<_> From da8c1f6a2611aab26fd05c2137903af83c5f9442 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 23 Nov 2023 15:14:28 +0100 Subject: [PATCH 156/222] BDN update --- eng/Versions.props | 2 +- .../CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index c96ae7ba999..a5c8cbe59e2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -181,7 +181,7 @@ 1.0.0 1.1.33 - 0.13.2 + 0.13.10 2.16.5 4.3.0.0 1.0.31 diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs index aeeb39b752d..959acc28ae8 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/BackgroundCompilerBenchmarks.fs @@ -234,7 +234,7 @@ type TestProjectType = [] [] [] -[] +[] type TransparentCompilerBenchmark() = let size = 30 @@ -338,7 +338,7 @@ type TransparentCompilerBenchmark() = [] [] [] -[] +[] type TransparentCompilerGiraffeBenchmark() = let mutable benchmark : ProjectWorkflowBuilder = Unchecked.defaultof<_> From 0a70004ff85e18bc07838066e55246323bde8a32 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 23 Nov 2023 19:30:22 +0100 Subject: [PATCH 157/222] Replay diagnostics for cached computations --- src/Compiler/Facilities/AsyncMemoize.fs | 152 ++++++++++++------ src/Compiler/Facilities/Hashing.fs | 2 +- src/Compiler/Service/BackgroundCompiler.fs | 3 +- src/Compiler/Service/TransparentCompiler.fs | 27 +--- src/Compiler/Service/service.fs | 5 +- src/Compiler/Utilities/Activity.fsi | 4 +- .../CompilerService/AsyncMemoize.fs | 86 +++++++++- 7 files changed, 200 insertions(+), 79 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 17dbfd01e09..87127c29781 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -7,8 +7,9 @@ open System.IO open System.Threading open System.Threading.Tasks -open FSharp.Compiler.Diagnostics open FSharp.Compiler.BuildGraph +open FSharp.Compiler.Diagnostics +open FSharp.Compiler.DiagnosticsLogger [] module internal Utils = @@ -25,11 +26,13 @@ module internal Utils = $"{dir}{Path.GetFileName path}" + let replayDiagnostics (logger: DiagnosticsLogger) = Seq.iter ((<|) logger.DiagnosticSink) + type internal StateUpdate<'TValue> = | CancelRequest | OriginatorCanceled - | JobCompleted of 'TValue - | JobFailed of exn + | JobCompleted of 'TValue * (PhasedDiagnostic * FSharpDiagnosticSeverity) list + | JobFailed of exn * (PhasedDiagnostic * FSharpDiagnosticSeverity) list type internal MemoizeReply<'TValue> = | New of CancellationToken @@ -41,12 +44,12 @@ type internal MemoizeRequest<'TValue> = [] type internal Job<'TValue> = - | Running of TaskCompletionSource<'TValue> * CancellationTokenSource * NodeCode<'TValue> * DateTime - | Completed of 'TValue + | Running of TaskCompletionSource<'TValue> * CancellationTokenSource * NodeCode<'TValue> * DateTime * ResizeArray + | Completed of 'TValue * (PhasedDiagnostic * FSharpDiagnosticSeverity) list member this.DebuggerDisplay = match this with - | Running(_, cts, _, ts) -> + | Running(_, cts, _, ts, _) -> let cancellation = if cts.IsCancellationRequested then " ! Cancellation Requested" @@ -54,7 +57,7 @@ type internal Job<'TValue> = "" $"Running since {ts.ToShortTimeString()}{cancellation}" - | Completed value -> $"Completed {value}" + | Completed(value, diags) -> $"Completed {value}" + (if diags.Length > 0 then $" ({diags.Length})" else "") type internal JobEvent = | Started @@ -78,8 +81,6 @@ type private KeyData<'TKey, 'TVersion> = Version: 'TVersion } - - type internal AsyncLock() = let semaphore = new SemaphoreSlim(1, 1) @@ -99,6 +100,22 @@ type internal AsyncLock() = interface IDisposable with member _.Dispose() = semaphore.Dispose() +type internal CachingDiagnosticsLogger(originalLogger: DiagnosticsLogger option) = + inherit DiagnosticsLogger($"CachingDiagnosticsLogger") + + let capturedDiagnostics = ResizeArray() + + override _.ErrorCount = + originalLogger + |> Option.map (fun x -> x.ErrorCount) + |> Option.defaultValue capturedDiagnostics.Count + + override _.DiagnosticSink(diagnostic: PhasedDiagnostic, severity: FSharpDiagnosticSeverity) = + originalLogger |> Option.iter (fun x -> x.DiagnosticSink(diagnostic, severity)) + capturedDiagnostics.Add(diagnostic, severity) + + member _.CapturedDiagnostics = capturedDiagnostics |> Seq.toList + [] type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> (?keepStrongly, ?keepWeakly, ?name: string, ?cancelDuplicateRunningJobs: bool) = @@ -181,7 +198,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let lock = new AsyncLock() - let processRequest post (key: KeyData<_, _>, msg) = + let processRequest post (key: KeyData<_, _>, msg) diagnosticLogger = lock.Do(fun () -> task { @@ -191,10 +208,11 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T return match msg, cached with | Sync, _ -> New Unchecked.defaultof<_> - | GetOrCompute _, Some(Completed result) -> + | GetOrCompute _, Some(Completed(result, diags)) -> Interlocked.Increment &hits |> ignore + diags |> replayDiagnostics diagnosticLogger Existing(Task.FromResult result) - | GetOrCompute(_, ct), Some(Running(tcs, _, _, _)) -> + | GetOrCompute(_, ct), Some(Running(tcs, _, _, _, loggers)) -> Interlocked.Increment &hits |> ignore incrRequestCount key @@ -203,6 +221,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T post (key, CancelRequest)) |> saveRegistration key + loggers.Add diagnosticLogger + Existing tcs.Task | GetOrCompute(computation, ct), None -> @@ -216,11 +236,16 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let cts = new CancellationTokenSource() - cache.Set(key.Key, key.Version, key.Label, (Running(TaskCompletionSource(), cts, computation, DateTime.Now))) + cache.Set( + key.Key, + key.Version, + key.Label, + (Running(TaskCompletionSource(), cts, computation, DateTime.Now, ResizeArray())) + ) otherVersions |> Seq.choose (function - | v, Running(_tcs, cts, _, _) -> Some(v, cts) + | v, Running(_tcs, cts, _, _, _) -> Some(v, cts) | _ -> None) |> Seq.iter (fun (_v, cts) -> use _ = Activity.start $"{name}: Duplicate running job" [| "key", key.Label |] @@ -250,7 +275,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T match action, cached with - | OriginatorCanceled, Some(Running(tcs, cts, computation, _)) -> + | OriginatorCanceled, Some(Running(tcs, cts, computation, _, _)) -> decrRequestCount key @@ -264,30 +289,39 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T Interlocked.Increment &canceled |> ignore use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] () - //System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" else // We need to restart the computation Task.Run(fun () -> - task { - do! Task.Delay 0 - - try - log (Started, key) - Interlocked.Increment &restarted |> ignore - System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}" - let! result = NodeCode.StartAsTask_ForTesting(computation, ct = cts.Token) - post (key, (JobCompleted result)) - with - | :? OperationCanceledException -> - post (key, CancelRequest) - () - | ex -> post (key, (JobFailed ex)) - }, + Async.StartAsTask( + async { + + let cachingLogger = new CachingDiagnosticsLogger(None) + + try + log (Started, key) + Interlocked.Increment &restarted |> ignore + System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}" + let currentLogger = DiagnosticsThreadStatics.DiagnosticsLogger + DiagnosticsThreadStatics.DiagnosticsLogger <- cachingLogger + + try + let! result = computation |> Async.AwaitNodeCode + post (key, (JobCompleted(result, cachingLogger.CapturedDiagnostics))) + return () + finally + DiagnosticsThreadStatics.DiagnosticsLogger <- currentLogger + with + | :? OperationCanceledException -> + post (key, CancelRequest) + () + | ex -> post (key, (JobFailed(ex, cachingLogger.CapturedDiagnostics))) + } + ), cts.Token) |> ignore - | CancelRequest, Some(Running(tcs, cts, _c, _)) -> + | CancelRequest, Some(Running(tcs, cts, _c, _, _)) -> decrRequestCount key @@ -301,7 +335,6 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T Interlocked.Increment &canceled |> ignore use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] () - //System.Diagnostics.Trace.TraceInformation $"{name} Canceled {key.Label}" // Probably in some cases cancellation can be fired off even after we just unregistered it | CancelRequest, None @@ -309,18 +342,22 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | OriginatorCanceled, None | OriginatorCanceled, Some(Completed _) -> () - | JobFailed ex, Some(Running(tcs, _cts, _c, _ts)) -> + | JobFailed(ex, diags), Some(Running(tcs, _cts, _c, _ts, loggers)) -> cancelRegistration key cache.Remove(key.Key, key.Version) requestCounts.Remove key |> ignore log (Failed, key) Interlocked.Increment &failed |> ignore failures.Add(key.Label, ex) + + for logger in loggers do + diags |> replayDiagnostics logger + tcs.TrySetException ex |> ignore - | JobCompleted result, Some(Running(tcs, _cts, _c, started)) -> + | JobCompleted(result, diags), Some(Running(tcs, _cts, _c, started, loggers)) -> cancelRegistration key - cache.Set(key.Key, key.Version, key.Label, (Completed result)) + cache.Set(key.Key, key.Version, key.Label, (Completed(result, diags))) requestCounts.Remove key |> ignore log (Finished, key) Interlocked.Increment &completed |> ignore @@ -332,6 +369,9 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T else avgDurationMs + (duration - avgDurationMs) / float completed + for logger in loggers do + diags |> replayDiagnostics logger + if tcs.TrySetResult result = false then internalError key.Label "Invalid state: Completed job already completed" @@ -340,9 +380,11 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | JobCompleted _, None -> internalError key.Label "Invalid state: Running job missing in cache (completed)" - | JobFailed ex, Some(Completed _job) -> internalError key.Label $"Invalid state: Failed Completed job \n%A{ex}" + | JobFailed(ex, _diags), Some(Completed(_job, _diags2)) -> + internalError key.Label $"Invalid state: Failed Completed job \n%A{ex}" - | JobCompleted _result, Some(Completed _job) -> internalError key.Label "Invalid state: Double-Completed job" + | JobCompleted(_result, _diags), Some(Completed(_job, _diags2)) -> + internalError key.Label "Invalid state: Double-Completed job" }) } @@ -372,28 +414,40 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T node { let! ct = NodeCode.CancellationToken - match! processRequest post (key, GetOrCompute(computation, ct)) |> NodeCode.AwaitTask with + let callerDiagnosticLogger = DiagnosticsThreadStatics.DiagnosticsLogger + + match! + processRequest post (key, GetOrCompute(computation, ct)) callerDiagnosticLogger + |> NodeCode.AwaitTask + with | New internalCt -> let linkedCtSource = CancellationTokenSource.CreateLinkedTokenSource(ct, internalCt) + let cachingLogger = new CachingDiagnosticsLogger(Some callerDiagnosticLogger) try return! - NodeCode.StartAsTask_ForTesting( - node { + Async.StartAsTask( + async { log (Started, key) - let! result = computation - post (key, (JobCompleted result)) - return result + let currentLogger = DiagnosticsThreadStatics.DiagnosticsLogger + DiagnosticsThreadStatics.DiagnosticsLogger <- cachingLogger + + try + let! result = computation |> Async.AwaitNodeCode + post (key, (JobCompleted(result, cachingLogger.CapturedDiagnostics))) + return result + finally + DiagnosticsThreadStatics.DiagnosticsLogger <- currentLogger }, - ct = linkedCtSource.Token + cancellationToken = linkedCtSource.Token ) |> NodeCode.AwaitTask with | :? TaskCanceledException | :? OperationCanceledException as ex -> return raise ex | ex -> - post (key, (JobFailed ex)) + post (key, (JobFailed(ex, cachingLogger.CapturedDiagnostics))) return raise ex | Existing job -> return! job |> NodeCode.AwaitTask @@ -427,7 +481,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let running = valueStats.TryFind "Running" - |> Option.map (sprintf " Running: %d") + |> Option.map (sprintf " Running: %d ") |> Option.defaultValue "" let avgDuration = avgDurationMs |> sprintf "| Avg: %.0f ms" @@ -441,7 +495,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let stats = [| if errors + failed > 0 then - " [!] " + " /_!_\ " if errors > 0 then $"| ERRORS: {errors} " else "" if failed > 0 then $"| FAILED: {failed} " else "" $"| hits: {hits}{hitRatio} " @@ -458,7 +512,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T |] |> String.concat "" - $"{locked}{running} {cache.DebuggerDisplay} {stats}{avgDuration}" + $"{locked}{running}{cache.DebuggerDisplay} {stats}{avgDuration}" [] type internal AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> diff --git a/src/Compiler/Facilities/Hashing.fs b/src/Compiler/Facilities/Hashing.fs index 5e49bb1fae4..26704259e41 100644 --- a/src/Compiler/Facilities/Hashing.fs +++ b/src/Compiler/Facilities/Hashing.fs @@ -39,4 +39,4 @@ module internal Md5Hasher = let addBool (b: bool) (s: string) = b |> BitConverter.GetBytes |> addBytes <| s - let addDateTime (dt: System.DateTime) (s: string) = dt.Ticks.ToString() |> addString <| s \ No newline at end of file + let addDateTime (dt: System.DateTime) (s: string) = dt.Ticks.ToString() |> addString <| s diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 5dbac7191b7..6a78a13c09f 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -1630,8 +1630,7 @@ type internal BackgroundCompiler let! sourceText = fileSnapshot.GetSource() |> NodeCode.AwaitTask let options = projectSnapshot.ToOptions() - return! - self.ParseAndCheckFileInProject(fileName, 0, sourceText, options, userOpName) + return! self.ParseAndCheckFileInProject(fileName, 0, sourceText, options, userOpName) } member _.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 3f3d1b4ffbf..a548cddf3b8 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1085,7 +1085,7 @@ type internal TransparentCompiler caches.TcIntermediate.Get( key, node { - + let input = parsedInput let fileName = input.FileName @@ -1816,8 +1816,7 @@ type internal TransparentCompiler ignore parseResults - let! _, result = - this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) return result } @@ -1838,8 +1837,7 @@ type internal TransparentCompiler ignore parseResults - let! _, result = - this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) return Some result } @@ -1873,15 +1871,11 @@ type internal TransparentCompiler ignore canInvalidateProject let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! - this.FindReferencesInFile(fileName, snapshot, symbol, userOpName) + return! this.FindReferencesInFile(fileName, snapshot, symbol, userOpName) } member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = - node { - return! - this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) - } + node { return! this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) } member _.FrameworkImportsCache: FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache @@ -1909,9 +1903,7 @@ type internal TransparentCompiler node { let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - match! - this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) - with + match! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) with | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return parseResult, checkResult | parseResult, FSharpCheckFileAnswer.Aborted -> return parseResult, FSharpCheckFileResults.MakeEmpty(fileName, [||], true) } @@ -1941,9 +1933,7 @@ type internal TransparentCompiler FSharpProjectSnapshot.FromOptions(options, fileName, 1, sourceText) |> NodeCode.AwaitAsync - match! - this.ParseAndCheckFileInProject(fileName, snapshot, "GetCachedCheckFileResult") - with + match! this.ParseAndCheckFileInProject(fileName, snapshot, "GetCachedCheckFileResult") with | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return Some(parseResult, checkResult) | _, FSharpCheckFileAnswer.Aborted -> return None } @@ -2016,8 +2006,7 @@ type internal TransparentCompiler FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync - return! - this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + return! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) } member this.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index b20bc4e4186..463c5fc70c3 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -449,6 +449,7 @@ type FSharpChecker member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.ParseAndCheckFileInProject(fileName, projectSnapshot, userOpName) |> Async.AwaitNodeCode @@ -496,7 +497,9 @@ type FSharpChecker let userOpName = defaultArg userOpName "Unknown" node { - let! parseResults = backgroundCompiler.ParseFile(fileName, projectSnapshot, userOpName) |> NodeCode.AwaitAsync + let! parseResults = + backgroundCompiler.ParseFile(fileName, projectSnapshot, userOpName) + |> NodeCode.AwaitAsync if parseResults.ParseTree.Identifiers |> Set.contains symbol.DisplayNameCore diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index 5e7519c34fb..afce0f3b554 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -1,6 +1,4 @@ - - -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. namespace FSharp.Compiler.Diagnostics open System diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index d5822e7d408..aeb9846a23d 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -388,7 +388,7 @@ let ``Preserve thread static diagnostics`` () = let job2Cache = AsyncMemoize() let job1 (input: string) = node { - let! _ = Async.Sleep 10 |> NodeCode.AwaitAsync + let! _ = Async.Sleep (rng.Next(1, 30)) |> NodeCode.AwaitAsync let ex = DummyException("job1 error") DiagnosticsThreadStatics.DiagnosticsLogger.ErrorR(ex) return Ok input @@ -398,7 +398,7 @@ let ``Preserve thread static diagnostics`` () = DiagnosticsThreadStatics.DiagnosticsLogger.Warning(DummyException("job2 error 1")) - let! _ = Async.Sleep 10 |> NodeCode.AwaitAsync + let! _ = Async.Sleep (rng.Next(1, 30)) |> NodeCode.AwaitAsync let key = { new ICacheKey<_, _> with member _.GetKey() = "job1" @@ -442,6 +442,84 @@ let ``Preserve thread static diagnostics`` () = let results = (Task.WhenAll tasks).Result - let diagnosticCounts = results |> Seq.map snd |> Seq.map Array.length |> Seq.groupBy id |> Seq.map (fun (k, v) -> k, v |> Seq.length) |> Seq.sortBy fst |> Seq.toList + let _diagnosticCounts = results |> Seq.map snd |> Seq.map Array.length |> Seq.groupBy id |> Seq.map (fun (k, v) -> k, v |> Seq.length) |> Seq.sortBy fst |> Seq.toList - Assert.Equal<(int * int) list>([], diagnosticCounts) + //Assert.Equal<(int * int) list>([4, 100], diagnosticCounts) + + let diagnosticMessages = results |> Seq.map snd |> Seq.map (Array.map (fun (d, _) -> d.Exception.Message) >> Array.toList) |> Set + + Assert.Equal>(Set [["task error"; "job2 error 1"; "job1 error"; "job2 error 2"; ]], diagnosticMessages) + + +[] +let ``Preserve thread static diagnostics already completed job`` () = + + let cache = AsyncMemoize() + + let key = { new ICacheKey<_, _> with + member _.GetKey() = "job1" + member _.GetVersion() = 1 + member _.GetLabel() = "job1" } + + let job (input: string) = node { + let ex = DummyException($"job {input} error") + DiagnosticsThreadStatics.DiagnosticsLogger.ErrorR(ex) + return Ok input + } + + async { + + let diagnosticsLogger = CompilationDiagnosticLogger($"Testing", FSharpDiagnosticOptions.Default) + + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Optimize) + + let! _ = cache.Get(key, job "1" ) |> Async.AwaitNodeCode + let! _ = cache.Get(key, job "2" ) |> Async.AwaitNodeCode + + let diagnosticMessages = diagnosticsLogger.GetDiagnostics() |> Array.map (fun (d, _) -> d.Exception.Message) |> Array.toList + + Assert.Equal>(["job 1 error"; "job 1 error"], diagnosticMessages) + + } + |> Async.StartAsTask + + +[] +let ``We get diagnostics from the job that failed`` () = + + let cache = AsyncMemoize() + + let key = { new ICacheKey<_, _> with + member _.GetKey() = "job1" + member _.GetVersion() = 1 + member _.GetLabel() = "job1" } + + let job (input: int) = node { + let ex = DummyException($"job {input} error") + do! Async.Sleep 100 |> NodeCode.AwaitAsync + DiagnosticsThreadStatics.DiagnosticsLogger.Error(ex) + return 5 + } + + let result = + [1; 2] + |> Seq.map (fun i -> + async { + let diagnosticsLogger = CompilationDiagnosticLogger($"Testing", FSharpDiagnosticOptions.Default) + + use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Optimize) + try + let! _ = cache.Get(key, job i ) |> Async.AwaitNodeCode + () + with _ -> + () + let diagnosticMessages = diagnosticsLogger.GetDiagnostics() |> Array.map (fun (d, _) -> d.Exception.Message) |> Array.toList + + //Assert.Equal>(["job 1 error"], diagnosticMessages) + return diagnosticMessages + }) + |> Async.Parallel + |> Async.StartAsTask + |> (fun t -> t.Result) + + Assert.Equal>([["job 1 error"]; ["job 1 error"]], result) \ No newline at end of file From 6577566dfefe071e3625615a8f630882e47a3007 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 23 Nov 2023 19:31:50 +0100 Subject: [PATCH 158/222] F --- .../FSharp.Editor/LanguageService/WorkspaceExtensions.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 6c810a16698..c2a4c6ea40e 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -52,11 +52,11 @@ module private CheckerExtensions = match! projectOptionsManager.TryGetOptionsByProject(this, ct) with | ValueNone -> return raise (OperationCanceledException("FSharp project options not found.")) - | ValueSome (parsingOptions, projectOptions) -> + | ValueSome(parsingOptions, projectOptions) -> let result = (service.Checker, projectOptionsManager, parsingOptions, projectOptions) - return ProjectCache.Projects.GetValue(this, ConditionalWeakTable<_, _>.CreateValueCallback (fun _ -> result)) + return ProjectCache.Projects.GetValue(this, ConditionalWeakTable<_, _>.CreateValueCallback(fun _ -> result)) } let getProjectSnapshot (snapshotAccumulatorOpt) (project: Project) = @@ -174,7 +174,7 @@ module private CheckerExtensions = return match checkFileAnswer with | FSharpCheckFileAnswer.Aborted -> None - | FSharpCheckFileAnswer.Succeeded (checkFileResults) -> Some(parseResults, checkFileResults) + | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> Some(parseResults, checkFileResults) } /// Parse and check the source text from the Roslyn document with possible stale results. From 7c52422be657d36a28c517b2169d4b2fb8bf6ac8 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 24 Nov 2023 00:16:10 +0100 Subject: [PATCH 159/222] Fixed fsproj --- src/Compiler/FSharp.Compiler.Service.fsproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index d56595f4503..15de92be6ac 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -147,7 +147,7 @@ - + From ecf201d0c9e430584a03a60e8b6b2995ed1e171f Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 24 Nov 2023 15:56:17 +0100 Subject: [PATCH 160/222] test fixes --- src/Compiler/Facilities/AsyncMemoize.fs | 2 ++ src/Compiler/Service/TransparentCompiler.fs | 16 +++++++++++++++- .../ConstraintSolver/ObjInference.fs | 3 ++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 87127c29781..83e86c02b15 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -299,6 +299,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let cachingLogger = new CachingDiagnosticsLogger(None) try + // TODO: Should unify starting and restarting log (Started, key) Interlocked.Increment &restarted |> ignore System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}" @@ -429,6 +430,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T return! Async.StartAsTask( async { + // TODO: Should unify starting and restarting log (Started, key) let currentLogger = DiagnosticsThreadStatics.DiagnosticsLogger DiagnosticsThreadStatics.DiagnosticsLogger <- cachingLogger diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index a548cddf3b8..4d0a4f67562 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1392,6 +1392,10 @@ type internal TransparentCompiler let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions + // TODO: Apparently creating diagnostics can produce further diagnostics. So let's capture those too. Hopefully there is a more elegant solution... + let extraLogger = CapturingDiagnosticsLogger("DiagnosticsWhileCreatingDiagnostics") + use _ = new CompilationGlobalsScope(extraLogger, BuildPhase.TypeCheck) + let tcDiagnostics = DiagnosticHelpers.CreateDiagnostics( diagnosticsOptions, @@ -1403,7 +1407,17 @@ type internal TransparentCompiler None // TODO: Add SymbolEnv ) - let tcDiagnostics = [| yield! creationDiags; yield! tcDiagnostics |] + let extraDiagnostics = DiagnosticHelpers.CreateDiagnostics( + diagnosticsOptions, + false, + fileName, + extraLogger.Diagnostics, + suggestNamesForErrors, + bootstrapInfo.TcConfig.flatErrors, + None // TODO: Add SymbolEnv + ) + + let tcDiagnostics = [| yield! creationDiags; yield! extraDiagnostics; yield! tcDiagnostics; |] let loadClosure = None // TODO: script support diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index 32950d8ba4c..64a433e30d4 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -116,7 +116,8 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere |> withLangVersion80 |> typecheck |> shouldFail - |> withSingleDiagnostic (Information 3559, Line line1, Col (col1 + 3), Line line2, Col (col2 + 3), message) + // Informational become warning if explicitly on and not explicitly off + |> withSingleDiagnostic (Warning 3559, Line line1, Col (col1 + 3), Line line2, Col (col2 + 3), message) [] [] From 0b207e69ac5a049b183afb72346f743ae8152944 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 24 Nov 2023 15:57:59 +0100 Subject: [PATCH 161/222] F --- src/Compiler/Service/TransparentCompiler.fs | 22 +++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 4d0a4f67562..913f18e2a31 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1407,17 +1407,19 @@ type internal TransparentCompiler None // TODO: Add SymbolEnv ) - let extraDiagnostics = DiagnosticHelpers.CreateDiagnostics( - diagnosticsOptions, - false, - fileName, - extraLogger.Diagnostics, - suggestNamesForErrors, - bootstrapInfo.TcConfig.flatErrors, - None // TODO: Add SymbolEnv - ) + let extraDiagnostics = + DiagnosticHelpers.CreateDiagnostics( + diagnosticsOptions, + false, + fileName, + extraLogger.Diagnostics, + suggestNamesForErrors, + bootstrapInfo.TcConfig.flatErrors, + None // TODO: Add SymbolEnv + ) - let tcDiagnostics = [| yield! creationDiags; yield! extraDiagnostics; yield! tcDiagnostics; |] + let tcDiagnostics = + [| yield! creationDiags; yield! extraDiagnostics; yield! tcDiagnostics |] let loadClosure = None // TODO: script support From 0e31a5cb90585374ef8213ee795e8a91bba10a6f Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 24 Nov 2023 16:47:56 +0100 Subject: [PATCH 162/222] test fixes --- .../CompilerService/AsyncMemoize.fs | 9 +++++++-- .../ConstraintSolver/ObjInference.fs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index aeb9846a23d..b266512b756 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -230,7 +230,7 @@ let ``Stress test`` () = let keyCount = rng.Next(5, 200) let keys = [| 1 .. keyCount |] - let testTimeoutMs = threads * iterations * maxDuration / 2 + let testTimeoutMs = threads * iterations * maxDuration let intenseComputation durationMs result = async { @@ -522,4 +522,9 @@ let ``We get diagnostics from the job that failed`` () = |> Async.StartAsTask |> (fun t -> t.Result) - Assert.Equal>([["job 1 error"]; ["job 1 error"]], result) \ No newline at end of file + Assert.Equal>([["job 1 error"]; ["job 1 error"]], result) + + +[] +let ``What if requestor cancels and their diagnosticsLogger gets disposed?``() = + failwith "TODO" \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index 64a433e30d4..2e7abce968a 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -40,7 +40,7 @@ let x = deserialize "" |> f""", 3, 9, 3, 28 |> withLangVersion80 |> typecheck |> shouldFail - |> withSingleDiagnostic (Information 3559, Line line1, Col col1, Line line2, Col col2, message) + |> withSingleDiagnostic (Warning 3559, Line line1, Col col1, Line line2, Col col2, message) let quotableNoWarningCases = [ From fe1d32e9bd93bbc4ed7d475955d298e1b1437341 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 27 Nov 2023 12:08:10 +0100 Subject: [PATCH 163/222] serialize snapshot to json --- src/Compiler/Service/FSharpCheckerResults.fs | 30 --------- src/Compiler/Service/FSharpCheckerResults.fsi | 6 -- .../CompilerService/AsyncMemoize.fs | 1 - .../FSharp.Test.Utilities.fsproj | 1 + tests/FSharp.Test.Utilities/Utilities.fs | 11 ++++ .../FSharp.Compiler.Benchmarks.fsproj | 1 + .../LanguageService/WorkspaceExtensions.fs | 66 ++++++++++++++++++- 7 files changed, 78 insertions(+), 38 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index bff5f7e4bc8..099836815af 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -171,36 +171,6 @@ type FSharpProjectSnapshot = Stamp: int64 option } - static member UseSameProject(options1, options2) = - match options1.ProjectId, options2.ProjectId with - | Some(projectId1), Some(projectId2) when - not (String.IsNullOrWhiteSpace(projectId1)) - && not (String.IsNullOrWhiteSpace(projectId2)) - -> - projectId1 = projectId2 - | Some _, Some _ - | None, None -> options1.ProjectFileName = options2.ProjectFileName - | _ -> false - - static member AreSameForChecking(options1, options2) = - match options1.Stamp, options2.Stamp with - | Some x, Some y -> (x = y) - | _ -> - FSharpProjectSnapshot.UseSameProject(options1, options2) - && options1.SourceFiles = options2.SourceFiles - && options1.OtherOptions = options2.OtherOptions - && options1.ReferencesOnDisk = options2.ReferencesOnDisk - && options1.UnresolvedReferences = options2.UnresolvedReferences - && options1.OriginalLoadReferences = options2.OriginalLoadReferences - && options1.ReferencedProjects.Length = options2.ReferencedProjects.Length - && (options1.ReferencedProjects, options2.ReferencedProjects) - ||> List.forall2 (fun r1 r2 -> - match r1, r2 with - | FSharpReferencedProjectSnapshot.FSharpReference(n1, a), FSharpReferencedProjectSnapshot.FSharpReference(n2, b) -> - n1 = n2 && FSharpProjectSnapshot.AreSameForChecking(a, b)) - - && options1.LoadTime = options2.LoadTime - member po.ProjectDirectory = Path.GetDirectoryName(po.ProjectFileName) member this.OutputFileName = diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index d3d06969156..fd90c72bfa9 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -115,12 +115,6 @@ type internal FSharpProjectSnapshot = Stamp: int64 option } - /// Whether the two parse options refer to the same project. - static member internal UseSameProject: options1: FSharpProjectSnapshot * options2: FSharpProjectSnapshot -> bool - - /// Compare two options sets with respect to the parts of the options that are important to building. - static member internal AreSameForChecking: options1: FSharpProjectSnapshot * options2: FSharpProjectSnapshot -> bool - /// Compute the project directory. member internal ProjectDirectory: string diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index b266512b756..c1571387fe8 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -515,7 +515,6 @@ let ``We get diagnostics from the job that failed`` () = () let diagnosticMessages = diagnosticsLogger.GetDiagnostics() |> Array.map (fun (d, _) -> d.Exception.Message) |> Array.toList - //Assert.Equal>(["job 1 error"], diagnosticMessages) return diagnosticMessages }) |> Async.Parallel diff --git a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj index d1d1eecece4..5cfcba98ca7 100644 --- a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj +++ b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj @@ -100,6 +100,7 @@ + diff --git a/tests/FSharp.Test.Utilities/Utilities.fs b/tests/FSharp.Test.Utilities/Utilities.fs index afd0a37cad5..16938d363ea 100644 --- a/tests/FSharp.Test.Utilities/Utilities.fs +++ b/tests/FSharp.Test.Utilities/Utilities.fs @@ -15,6 +15,10 @@ open Microsoft.CodeAnalysis.CSharp open TestFramework open NUnit.Framework open System.Collections.Generic +open FSharp.Compiler.CodeAnalysis +open Newtonsoft.Json +open Newtonsoft.Json.Linq + type TheoryForNETCOREAPPAttribute() = inherit Xunit.TheoryAttribute() @@ -373,3 +377,10 @@ let main argv = 0""" | TargetFramework.NetStandard20 -> netStandard20Files.Value |> Seq.toArray | TargetFramework.NetCoreApp31 -> [||] //ToDo --- Perhaps NetCoreApp31Files | TargetFramework.Current -> currentReferences + + +module internal FSharpProjectSnapshotSerialization = + + let serializeSnapshotToJson (snapshot: FSharpProjectSnapshot) = + + JsonConvert.SerializeObject(snapshot, Formatting.Indented, new JsonSerializerSettings(ReferenceLoopHandling = ReferenceLoopHandling.Ignore)) \ No newline at end of file diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj index a8c8ff31bc6..ad9ddf8461c 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj @@ -21,6 +21,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index c2a4c6ea40e..20aee8e2bfe 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -15,6 +15,9 @@ open FSharp.Compiler.BuildGraph open CancellableTasks open Internal.Utilities.Collections +open Newtonsoft.Json +open Newtonsoft.Json.Linq +open System.Text.Json.Nodes [] module internal ProjectCache = @@ -31,6 +34,63 @@ type Solution with member internal this.GetFSharpWorkspaceService() = this.Workspace.Services.GetRequiredService() + +module internal FSharpProjectSnapshotSerialization = + + let serializeFileSnapshot (snapshot: FSharpFileSnapshot) = + let output = JObject() + output.Add("FileName", snapshot.FileName) + output.Add("Version", snapshot.Version) + output + + let serializeReferenceOnDisk (reference: ReferenceOnDisk) = + let output = JObject() + output.Add("Path", reference.Path) + output.Add("LastModified", reference.LastModified) + output + + let rec serializeReferencedProject (reference: FSharpReferencedProjectSnapshot) = + let output = JObject() + match reference with + | FSharpReference (projectOutputFile, snapshot) -> + output.Add("projectOutputFile", projectOutputFile) + output.Add("snapshot", serializeSnapshot snapshot) + output + + and serializeSnapshot (snapshot: FSharpProjectSnapshot) = + + let output = JObject() + + output.Add("ProjectFileName", snapshot.ProjectFileName) + output.Add("ProjectId", (snapshot.ProjectId |> Option.defaultValue null |> JToken.FromObject)) + output.Add("SourceFiles", snapshot.SourceFiles |> Seq.map serializeFileSnapshot |> JArray ) + output.Add("ReferencesOnDisk", snapshot.ReferencesOnDisk |> Seq.map serializeReferenceOnDisk |> JArray ) + output.Add("OtherOptions", JArray(snapshot.OtherOptions)) + output.Add("ReferencedProjects", snapshot.ReferencedProjects |> Seq.map serializeReferencedProject |> JArray ) + output.Add("IsIncompleteTypeCheckEnvironment", snapshot.IsIncompleteTypeCheckEnvironment) + output.Add("UseScriptResolutionRules", snapshot.UseScriptResolutionRules) + output.Add("LoadTime", snapshot.LoadTime) + // output.Add("UnresolvedReferences", snapshot.UnresolvedReferences) + output.Add("OriginalLoadReferences", + snapshot.OriginalLoadReferences + |> Seq.map (fun (r:Text.range, a, b) -> + JArray(r.FileName, r.Start, r.End, a, b)) |> JArray) + + output.Add("Stamp", (snapshot.Stamp |> (Option.defaultValue 0) |> JToken.FromObject )) + + output + + let dumpToJson (snapshot) = + + let jObject = serializeSnapshot snapshot + + let json = jObject.ToString(Formatting.Indented) + + json + + +open FSharpProjectSnapshotSerialization + [] module private CheckerExtensions = @@ -119,7 +179,11 @@ module private CheckerExtensions = } } - return! FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) + + let _json = dumpToJson snapshot + + return snapshot } let getProjectSnapshotForDocument (document: Document, options: FSharpProjectOptions) = From 9b01f9c8c8930e959abe3224e8a1770a29da9b40 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 27 Nov 2023 15:59:45 +0100 Subject: [PATCH 164/222] Fix DiagnosticExtendedData --- src/Compiler/Service/TransparentCompiler.fs | 17 ++++++----------- .../ProjectSnapshotBenchmarks.fs | 13 +++++++++++++ 2 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ProjectSnapshotBenchmarks.fs diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 913f18e2a31..247db5bc3a6 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1382,29 +1382,24 @@ type internal TransparentCompiler let tcDependencyFiles = [] // TODO add as a set to TcIntermediate - let tcDiagnostics = - seq { - yield! tcInfo.TcDiagnostics - - //for x in tcIntermediate.tcDiagnosticsRev do - // yield! x - } - let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions // TODO: Apparently creating diagnostics can produce further diagnostics. So let's capture those too. Hopefully there is a more elegant solution... + // Probably diagnostics need to be evaluated during typecheck anyway for proper formatting, which might take care of this too. let extraLogger = CapturingDiagnosticsLogger("DiagnosticsWhileCreatingDiagnostics") use _ = new CompilationGlobalsScope(extraLogger, BuildPhase.TypeCheck) + let symbolEnv = SymbolEnv(bootstrapInfo.TcGlobals, tcState.Ccu, Some tcState.CcuSig, bootstrapInfo.TcImports) + let tcDiagnostics = DiagnosticHelpers.CreateDiagnostics( diagnosticsOptions, false, fileName, - tcDiagnostics, + tcInfo.TcDiagnostics, suggestNamesForErrors, bootstrapInfo.TcConfig.flatErrors, - None // TODO: Add SymbolEnv + Some symbolEnv ) let extraDiagnostics = @@ -1415,7 +1410,7 @@ type internal TransparentCompiler extraLogger.Diagnostics, suggestNamesForErrors, bootstrapInfo.TcConfig.flatErrors, - None // TODO: Add SymbolEnv + Some symbolEnv ) let tcDiagnostics = diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ProjectSnapshotBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ProjectSnapshotBenchmarks.fs new file mode 100644 index 00000000000..df9f936d60c --- /dev/null +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ProjectSnapshotBenchmarks.fs @@ -0,0 +1,13 @@ +module FSharp.Benchmarks.ProjectSnapshotBenchmarks + +open System.IO +open BenchmarkDotNet.Attributes +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.Text +open FSharp.Compiler.Diagnostics +open FSharp.Test.ProjectGeneration +open BenchmarkDotNet.Engines + + +[] +let FSharpCategory = "fsharp" From 9952028432b3a3939faae70a868fa97837f744c5 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 30 Nov 2023 17:53:37 +0100 Subject: [PATCH 165/222] Move Project Snapshot to class --- src/Compiler/FSharp.Compiler.Service.fsproj | 1 + src/Compiler/Facilities/Hashing.fs | 40 +- src/Compiler/Service/BackgroundCompiler.fs | 4 +- src/Compiler/Service/FSharpCheckerResults.fs | 455 ------------ src/Compiler/Service/FSharpCheckerResults.fsi | 191 ----- src/Compiler/Service/FSharpProjectSnapshot.fs | 657 ++++++++++++++++++ src/Compiler/Service/TransparentCompiler.fs | 38 +- .../FSharpChecker/ProjectSnapshot.fs | 101 +-- .../FSharpChecker/TransparentCompiler.fs | 6 +- tests/FSharp.Test.Utilities/CompilerAssert.fs | 1 + .../ProjectGeneration.fs | 1 + .../LanguageService/WorkspaceExtensions.fs | 1 + 12 files changed, 777 insertions(+), 719 deletions(-) create mode 100644 src/Compiler/Service/FSharpProjectSnapshot.fs diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 15de92be6ac..641db223232 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -499,6 +499,7 @@ + diff --git a/src/Compiler/Facilities/Hashing.fs b/src/Compiler/Facilities/Hashing.fs index 26704259e41..8f67b8e89fd 100644 --- a/src/Compiler/Facilities/Hashing.fs +++ b/src/Compiler/Facilities/Hashing.fs @@ -4,7 +4,7 @@ open System open System.Threading /// Tools for hashing things with MD5 into a string that can be used as a cache key. -module internal Md5Hasher = +module internal Md5StringHasher = let private md5 = new ThreadLocal<_>(fun () -> System.Security.Cryptography.MD5.Create()) @@ -40,3 +40,41 @@ module internal Md5Hasher = b |> BitConverter.GetBytes |> addBytes <| s let addDateTime (dt: System.DateTime) (s: string) = dt.Ticks.ToString() |> addString <| s + +module internal Md5Hasher = + + let private md5 = + new ThreadLocal<_>(fun () -> System.Security.Cryptography.MD5.Create()) + + let private computeHash (bytes: byte array) = md5.Value.ComputeHash(bytes) + + let empty = Array.empty + + let hashString (s: string)= + s |> System.Text.Encoding.UTF8.GetBytes |> computeHash + + let addBytes (bytes: byte array) (s: byte array) = + + Array.append s bytes + |> computeHash + + let addString (s: string) (s2: byte array) = + s |> System.Text.Encoding.UTF8.GetBytes |> addBytes <| s2 + + let addSeq<'item> (items: 'item seq) (addItem: 'item -> byte array -> byte array) (s: byte array) = + items |> Seq.fold (fun s a -> addItem a s) s + + let addStrings strings = addSeq strings addString + let addBytes' bytes = addSeq bytes addBytes + + // If we use this make it an extension method? + //let addVersions<'a, 'b when 'a :> ICacheKey<'b, string>> (versions: 'a seq) (s: string) = + // versions |> Seq.map (fun x -> x.GetVersion()) |> addStrings <| s + + let addBool (b: bool) (s: byte array) = + b |> BitConverter.GetBytes |> addBytes <| s + + let addDateTime (dt: System.DateTime) (s: byte array) = dt.Ticks |> BitConverter.GetBytes |> addBytes <| s + + let toString (bytes: byte array) = + bytes |> System.BitConverter.ToString \ No newline at end of file diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 20c59c60dcb..98b03f393db 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -44,6 +44,8 @@ type FilePath = string type ProjectPath = string type FileVersion = int +type FSharpProjectSnapshot = FSharp.Compiler.CodeAnalysis.ProjectSnapshot.FSharpProjectSnapshot + type internal IBackgroundCompiler = /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. @@ -1524,7 +1526,7 @@ type internal BackgroundCompiler member _.GetAssemblyData ( - projectSnapshot: FSharpProjectSnapshot, + projectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot.FSharpProjectSnapshot, _fileName: string, userOpName: string ) : NodeCode = diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 099836815af..849d4b71fa0 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -114,351 +114,6 @@ type DelayedILModuleReader = } | _ -> cancellable.Return(Some this.result) -[] -type FSharpFileSnapshot = - { - FileName: string - Version: string - GetSource: unit -> Task - } - - member this.IsSignatureFile = this.FileName.ToLower().EndsWith(".fsi") - - override this.Equals(o) = - match o with - | :? FSharpFileSnapshot as o -> o.FileName = this.FileName && o.Version = this.Version - | _ -> false - - override this.GetHashCode() = this.Key.GetHashCode() - - member this.Key = this :> ICacheKey<_, _> - - interface ICacheKey with - member this.GetLabel() = this.FileName |> shortPath - member this.GetKey() = this.FileName - member this.GetVersion() = this.Version - -type FSharpFileSnapshotWithSource = - { - FileName: string - SourceHash: string - Source: ISourceText - IsLastCompiland: bool - IsExe: bool - } - - member this.IsSignatureFile = this.FileName.ToLower().EndsWith(".fsi") - -type ReferenceOnDisk = - { Path: string; LastModified: DateTime } - -type ProjectSnapshotKey = string * string - -[] -type FSharpProjectSnapshot = - { - ProjectFileName: string - ProjectId: string option - SourceFiles: FSharpFileSnapshot list - ReferencesOnDisk: ReferenceOnDisk list - OtherOptions: string list - ReferencedProjects: FSharpReferencedProjectSnapshot list - IsIncompleteTypeCheckEnvironment: bool - UseScriptResolutionRules: bool - LoadTime: DateTime - UnresolvedReferences: FSharpUnresolvedReferencesSet option - OriginalLoadReferences: (range * string * string) list - Stamp: int64 option - } - - member po.ProjectDirectory = Path.GetDirectoryName(po.ProjectFileName) - - member this.OutputFileName = - this.OtherOptions - |> List.tryFind (fun x -> x.StartsWith("-o:")) - |> Option.map (fun x -> x.Substring(3)) - - member this.IndexOf fileName = - this.SourceFiles - |> List.tryFindIndex (fun x -> x.FileName = fileName) - |> Option.defaultWith (fun () -> failwith (sprintf "Unable to find file %s in project %s" fileName this.ProjectFileName)) - - member this.UpTo fileIndex = - { this with - SourceFiles = this.SourceFiles[..fileIndex] - } - - member this.UpTo fileName = this.UpTo(this.IndexOf fileName) - - member this.OnlyWith fileIndexes = - { this with - SourceFiles = - fileIndexes - |> Set.toList - |> List.sort - |> List.choose (fun x -> this.SourceFiles |> List.tryItem x) - } - - member this.WithoutImplFilesThatHaveSignatures = - let files = - (([], Set.empty), this.SourceFiles) - ||> Seq.fold (fun (res, sigs) file -> - if file.IsSignatureFile then - file :: res, sigs |> Set.add file.FileName - else - let sigFileName = $"{file.FileName}i" - - if sigs.Contains sigFileName then - res, sigs |> Set.remove sigFileName - else - file :: res, sigs) - |> fst - |> List.rev - - { this with SourceFiles = files } - - member this.WithoutImplFilesThatHaveSignaturesExceptLastOne = - let lastFile = this.SourceFiles |> List.last - - let snapshotWithoutImplFilesThatHaveSignatures = - this.WithoutImplFilesThatHaveSignatures - - if - lastFile.IsSignatureFile - || snapshotWithoutImplFilesThatHaveSignatures.SourceFiles |> List.last = lastFile - then - this.WithoutImplFilesThatHaveSignatures - else - { snapshotWithoutImplFilesThatHaveSignatures with - SourceFiles = snapshotWithoutImplFilesThatHaveSignatures.SourceFiles @ [ lastFile ] - } - - member this.SourceFileNames = this.SourceFiles |> List.map (fun x -> x.FileName) - - member this.CommandLineOptions = - seq { - for r in this.ReferencesOnDisk do - $"-r:{r.Path}" - - yield! this.OtherOptions - } - |> Seq.toList - - member this.WithoutFileVersions = - { this with - SourceFiles = this.SourceFiles |> List.map (fun x -> { x with Version = "" }) - } - - member this.WithoutReferences = - { this with - ReferencedProjects = [] - ReferencesOnDisk = [] - } - - member this.WithoutSourceFiles = { this with SourceFiles = [] } - - override this.ToString() = - Path.GetFileNameWithoutExtension this.ProjectFileName - |> sprintf "FSharpProjectSnapshot(%s)" - - member this.Key = this :> ICacheKey<_, _> - - member this.FileKey(fileName) = - { new ICacheKey<_, _> with - member _.GetLabel() = fileName |> shortPath - member _.GetKey() = fileName, this.Key.GetKey() - - member _.GetVersion() = - this - .UpTo(fileName) - .WithoutImplFilesThatHaveSignaturesExceptLastOne.Key.GetVersion() - } - - member this.GetLastModifiedTimeOnDisk() = - // TODO: - DateTime.Now - - member this.GetMd5Version() = - ignore this - Md5Hasher.empty - //|> Md5Hasher.addString this.ProjectFileName - //|> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.FileName)) - //|> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.Version)) - //|> Md5Hasher.addSeq this.ReferencesOnDisk (fun r -> Md5Hasher.addString r.Path >> Md5Hasher.addDateTime r.LastModified) - //|> Md5Hasher.addStrings this.OtherOptions - //|> Md5Hasher.addVersions ( - // this.ReferencedProjects - // |> Seq.map (fun (FSharpReference (_name, p)) -> p.WithoutImplFilesThatHaveSignatures.Key) - //) - //|> Md5Hasher.addBool this.IsIncompleteTypeCheckEnvironment - //|> Md5Hasher.addBool this.UseScriptResolutionRules - - member this.GetDebugVersion() : FSharpProjectSnapshotDebugVersion = - { - ProjectFileName = this.ProjectFileName - SourceFiles = [ for f in this.SourceFiles -> f.FileName, f.Version ] - ReferencesOnDisk = this.ReferencesOnDisk - OtherOptions = this.OtherOptions - ReferencedProjects = - [ - for FSharpReference(_, p) in this.ReferencedProjects -> p.WithoutImplFilesThatHaveSignatures.GetDebugVersion() - ] - IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment - UseScriptResolutionRules = this.UseScriptResolutionRules - } - - interface ICacheKey with - member this.GetLabel() = this.ToString() - - member this.GetKey() = - this.ProjectFileName, this.OutputFileName |> Option.defaultValue "" - - member this.GetVersion() = this.GetDebugVersion() - -and FSharpProjectSnapshotWithSources = - { - ProjectSnapshot: FSharpProjectSnapshot - SourceFiles: FSharpFileSnapshotWithSource list - } - - member this.IndexOf fileName = - this.SourceFiles - |> List.tryFindIndex (fun x -> x.FileName = fileName) - |> Option.defaultWith (fun () -> - failwith (sprintf "Unable to find file %s in project %s" fileName this.ProjectSnapshot.ProjectFileName)) - - member this.UpTo(fileIndex: FileIndex) = - { - ProjectSnapshot = this.ProjectSnapshot.UpTo fileIndex - SourceFiles = this.SourceFiles[..fileIndex] - } - - member this.UpTo fileName = this.UpTo(this.IndexOf fileName) - - member this.WithoutImplFilesThatHaveSignatures = - let files = - (([], Set.empty), this.SourceFiles) - ||> Seq.fold (fun (res, sigs) file -> - if file.IsSignatureFile then - file :: res, sigs |> Set.add file.FileName - else - let sigFileName = $"{file.FileName}i" - - if sigs.Contains sigFileName then - res, sigs |> Set.remove sigFileName - else - file :: res, sigs) - |> fst - |> List.rev - - { this with SourceFiles = files } - - member this.WithoutImplFilesThatHaveSignaturesExceptLastOne = - let lastFile = this.SourceFiles |> List.last - - let snapshotWithoutImplFilesThatHaveSignatures = - this.WithoutImplFilesThatHaveSignatures - - if - lastFile.IsSignatureFile - || snapshotWithoutImplFilesThatHaveSignatures.SourceFiles |> List.last = lastFile - then - this.WithoutImplFilesThatHaveSignatures - else - { snapshotWithoutImplFilesThatHaveSignatures with - SourceFiles = snapshotWithoutImplFilesThatHaveSignatures.SourceFiles @ [ lastFile ] - } - - member internal this.Key = this :> ICacheKey<_, _> - - member this.FileKey(fileName) = - { new ICacheKey<_, _> with - member _.GetLabel() = fileName |> shortPath - member _.GetKey() = fileName, this.Key.GetKey() - - member _.GetVersion() = - this - .UpTo(fileName) - .WithoutImplFilesThatHaveSignaturesExceptLastOne.Key.GetVersion() - } - - member this.GetDebugVersion() = - { - ProjectSnapshotVersion = this.ProjectSnapshot.WithoutFileVersions.GetDebugVersion() - SourceVersions = this.SourceFiles |> List.map (fun x -> x.SourceHash) - } - - member this.GetMd5Version() = - this.ProjectSnapshot.GetMd5Version() - |> Md5Hasher.addStrings (this.SourceFiles |> Seq.map (fun x -> x.SourceHash)) - - interface ICacheKey with - member this.GetLabel() = this.ProjectSnapshot.Key.ToString() - member this.GetKey() = this.ProjectSnapshot.Key.GetKey() - - member this.GetVersion() = this.GetDebugVersion() - -and FSharpProjectSnapshotWithSourcesDebugVersion = - { - ProjectSnapshotVersion: FSharpProjectSnapshotDebugVersion - SourceVersions: string list - } - -and FSharpProjectSnapshotWithSourcesVersion = FSharpProjectSnapshotWithSourcesDebugVersion - -and FSharpProjectSnapshotDebugVersion = - { - ProjectFileName: string - SourceFiles: (string * string) list - ReferencesOnDisk: ReferenceOnDisk list - OtherOptions: string list - ReferencedProjects: FSharpProjectSnapshotDebugVersion list - IsIncompleteTypeCheckEnvironment: bool - UseScriptResolutionRules: bool - } - -and FSharpProjectSnapshotVersion = FSharpProjectSnapshotDebugVersion - -and [] public FSharpReferencedProjectSnapshot = - internal - | FSharpReference of projectOutputFile: string * options: FSharpProjectSnapshot - //| PEReference of projectOutputFile: string * getStamp: (unit -> DateTime) * delayedReader: DelayedILModuleReader - //| ILModuleReference of - // projectOutputFile: string * - // getStamp: (unit -> DateTime) * - // getReader: (unit -> ILModuleReader) - - /// - /// The fully qualified path to the output of the referenced project. This should be the same value as the -r - /// reference in the project options for this referenced project. - /// - member this.OutputFile = - match this with - | FSharpReference(projectOutputFile, _) -> projectOutputFile - - /// - /// Creates a reference for an F# project. The physical data for it is stored/cached inside of the compiler service. - /// - /// The fully qualified path to the output of the referenced project. This should be the same value as the -r reference in the project options for this referenced project. - /// The Project Options for this F# project - static member CreateFSharp(projectOutputFile, options: FSharpProjectSnapshot) = - FSharpReference(projectOutputFile, options) - - override this.Equals(o) = - match o with - | :? FSharpReferencedProjectSnapshot as o -> - match this, o with - | FSharpReference(projectOutputFile1, options1), FSharpReference(projectOutputFile2, options2) -> - projectOutputFile1 = projectOutputFile2 && options1 = options2 - - | _ -> false - - override this.GetHashCode() = this.OutputFile.GetHashCode() - - member this.Key = - match this with - | FSharpReference(_, snapshot) -> snapshot.Key - [] type FSharpReferencedProject = | FSharpReference of projectOutputFile: string * options: FSharpProjectOptions @@ -532,116 +187,6 @@ and FSharpProjectOptions = override this.ToString() = "FSharpProjectOptions(" + this.ProjectFileName + ")" -type FSharpProjectSnapshot with - - member this.ToOptions() : FSharpProjectOptions = - { - ProjectFileName = this.ProjectFileName - ProjectId = this.ProjectId - SourceFiles = this.SourceFiles |> Seq.map (fun x -> x.FileName) |> Seq.toArray - OtherOptions = this.CommandLineOptions |> List.toArray - ReferencedProjects = - this.ReferencedProjects - |> Seq.map (function - | FSharpReference(name, opts) -> FSharpReferencedProject.FSharpReference(name, opts.ToOptions())) - |> Seq.toArray - IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment - UseScriptResolutionRules = this.UseScriptResolutionRules - LoadTime = this.LoadTime - UnresolvedReferences = this.UnresolvedReferences - OriginalLoadReferences = this.OriginalLoadReferences - Stamp = this.Stamp - } - - static member FromOptions(options: FSharpProjectOptions, getFileSnapshot, ?snapshotAccumulator) = - let snapshotAccumulator = defaultArg snapshotAccumulator (Dictionary()) - - async { - - // TODO: check if options is a good key here - if not (snapshotAccumulator.ContainsKey options) then - - let! sourceFiles = options.SourceFiles |> Seq.map (getFileSnapshot options) |> Async.Parallel - - let! referencedProjects = - options.ReferencedProjects - |> Seq.choose (function - | FSharpReferencedProject.FSharpReference(outputName, options) -> - Some( - async { - let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, snapshotAccumulator) - return FSharpReferencedProjectSnapshot.FSharpReference(outputName, snapshot) - } - ) - // TODO: other types - | _ -> None) - |> Async.Sequential - - let referencesOnDisk, otherOptions = - options.OtherOptions - |> Array.partition (fun x -> x.StartsWith("-r:")) - |> map1Of2 ( - Array.map (fun x -> - let path = x.Substring(3) - - { - Path = path - LastModified = FileSystem.GetLastWriteTimeShim(path) - }) - ) - - let snapshot = - { - ProjectFileName = options.ProjectFileName - ProjectId = options.ProjectId - SourceFiles = sourceFiles |> List.ofArray - ReferencesOnDisk = referencesOnDisk |> List.ofArray - OtherOptions = otherOptions |> List.ofArray - ReferencedProjects = referencedProjects |> List.ofArray - IsIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment - UseScriptResolutionRules = options.UseScriptResolutionRules - LoadTime = options.LoadTime - UnresolvedReferences = options.UnresolvedReferences - OriginalLoadReferences = options.OriginalLoadReferences - Stamp = options.Stamp - } - - snapshotAccumulator.Add(options, snapshot) - - return snapshotAccumulator[options] - } - - static member GetFileSnapshotFromDisk _ fileName = - async { - let timeStamp = FileSystem.GetLastWriteTimeShim(fileName) - let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() - - return - { - FileName = fileName - Version = timeStamp.Ticks.ToString() - GetSource = fun () -> Task.FromResult(SourceText.ofString contents) - } - } - - static member FromOptions(options: FSharpProjectOptions) = - FSharpProjectSnapshot.FromOptions(options, FSharpProjectSnapshot.GetFileSnapshotFromDisk) - - static member FromOptions(options: FSharpProjectOptions, fileName: string, fileVersion: int, sourceText: ISourceText) = - - let getFileSnapshot _ fName = - if fName = fileName then - async.Return - { - FileName = fileName - GetSource = fun () -> Task.FromResult sourceText - Version = $"{fileVersion}{sourceText.GetHashCode().ToString()}" - } - else - FSharpProjectSnapshot.GetFileSnapshotFromDisk () fName - - FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) - [] module internal FSharpCheckerResultsSettings = diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index fd90c72bfa9..081aeac9153 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -45,177 +45,6 @@ type DelayedILModuleReader = /// Unused in this API type public FSharpUnresolvedReferencesSet = internal FSharpUnresolvedReferencesSet of UnresolvedAssemblyReference list -[] -type internal FSharpFileSnapshot = - { FileName: string - Version: string - GetSource: unit -> Task } - - member internal Key: ICacheKey - - interface ICacheKey - -type internal FSharpFileSnapshotWithSource = - { FileName: string - SourceHash: string - Source: ISourceText - IsLastCompiland: bool - IsExe: bool } - -/// Referenced assembly on disk. Includes last modified time so we know we need to rebuild when it changes. -type internal ReferenceOnDisk = - { Path: string; LastModified: DateTime } - -type internal ProjectSnapshotKey = string * string - -[] -type internal FSharpProjectSnapshot = - { - // Note that this may not reduce to just the project directory, because there may be two projects in the same directory. - ProjectFileName: string - - /// This is the unique identifier for the project, it is case sensitive. If it's None, will key off of ProjectFileName in our caching. - ProjectId: string option - - /// The files in the project. - SourceFiles: FSharpFileSnapshot list - - /// Referenced assemblies on disk. - ReferencesOnDisk: ReferenceOnDisk list - - /// Additional command line argument options for the project. - OtherOptions: string list - - /// The command line arguments for the other projects referenced by this project, indexed by the - /// exact text used in the "-r:" reference in FSharpProjectOptions. - ReferencedProjects: FSharpReferencedProjectSnapshot list - - /// When true, the typechecking environment is known a priori to be incomplete, for - /// example when a .fs file is opened outside of a project. In this case, the number of error - /// messages reported is reduced. - IsIncompleteTypeCheckEnvironment: bool - - /// When true, use the reference resolution rules for scripts rather than the rules for compiler. - UseScriptResolutionRules: bool - - /// Timestamp of project/script load, used to differentiate between different instances of a project load. - /// This ensures that a complete reload of the project or script type checking - /// context occurs on project or script unload/reload. - LoadTime: DateTime - - /// Unused in this API and should be 'None' when used as user-specified input - UnresolvedReferences: FSharpUnresolvedReferencesSet option - - /// Unused in this API and should be '[]' when used as user-specified input - OriginalLoadReferences: (range * string * string) list - - /// An optional stamp to uniquely identify this set of options - /// If two sets of options both have stamps, then they are considered equal - /// if and only if the stamps are equal - Stamp: int64 option - } - - /// Compute the project directory. - member internal ProjectDirectory: string - - member OutputFileName: string option - - member SourceFileNames: string list - - member CommandLineOptions: string list - - member IndexOf: fileName: string -> FileIndex - - /// A snapshot of the same project but only up to the given file index (including). - member UpTo: fileIndex: int -> FSharpProjectSnapshot - - /// A snapshot of the same project but only up to the given file (including). - member UpTo: fileName: string -> FSharpProjectSnapshot - - /// A snapshot of the same project but only with source files specified by given indexes. - member OnlyWith: fileIndexes: Set -> FSharpProjectSnapshot - - member WithoutImplFilesThatHaveSignatures: FSharpProjectSnapshot - - member WithoutImplFilesThatHaveSignaturesExceptLastOne: FSharpProjectSnapshot - - /// A snapshot of the same project with file versions removed. - member WithoutFileVersions: FSharpProjectSnapshot - - /// A snapshot of the same project with ReferencedProjects and ReferencesOnDisk removed. - member WithoutReferences: FSharpProjectSnapshot - - member WithoutSourceFiles: FSharpProjectSnapshot - - member GetLastModifiedTimeOnDisk: unit -> DateTime - - member internal Key: ICacheKey - - member internal FileKey: fileName: string -> ICacheKey<(string * ProjectSnapshotKey), FSharpProjectSnapshotVersion> - - interface ICacheKey - -and internal FSharpProjectSnapshotWithSources = - { ProjectSnapshot: FSharpProjectSnapshot - SourceFiles: FSharpFileSnapshotWithSource list } - - /// A snapshot of the same project but only up to the given file index (including). - member UpTo: fileIndex: int -> FSharpProjectSnapshotWithSources - - /// A snapshot of the same project but only up to the given file (including). - member UpTo: fileName: string -> FSharpProjectSnapshotWithSources - - member WithoutImplFilesThatHaveSignatures: FSharpProjectSnapshotWithSources - member WithoutImplFilesThatHaveSignaturesExceptLastOne: FSharpProjectSnapshotWithSources - - member internal FileKey: - fileName: string -> ICacheKey<(string * ProjectSnapshotKey), FSharpProjectSnapshotWithSourcesVersion> - - member internal Key: ICacheKey - interface ICacheKey - -and internal FSharpProjectSnapshotWithSourcesDebugVersion = - { ProjectSnapshotVersion: FSharpProjectSnapshotDebugVersion - SourceVersions: string list } - -and internal FSharpProjectSnapshotWithSourcesVersion = FSharpProjectSnapshotWithSourcesDebugVersion - -and internal FSharpProjectSnapshotDebugVersion = - { ProjectFileName: string - SourceFiles: (string * string) list - ReferencesOnDisk: ReferenceOnDisk list - OtherOptions: string list - ReferencedProjects: FSharpProjectSnapshotDebugVersion list - IsIncompleteTypeCheckEnvironment: bool - UseScriptResolutionRules: bool } - -and internal FSharpProjectSnapshotVersion = FSharpProjectSnapshotDebugVersion - -and [] internal FSharpReferencedProjectSnapshot = - internal - | FSharpReference of projectOutputFile: string * options: FSharpProjectSnapshot - //| PEReference of projectOutputFile: string * version: string * delayedReader: DelayedILModuleReader - //| ILModuleReference of - // projectOutputFile: string * - // getStamp: (unit -> DateTime) * - // getReader: (unit -> ILModuleReader) - - /// - /// The fully qualified path to the output of the referenced project. This should be the same value as the -r - /// reference in the project options for this referenced project. - /// - member OutputFile: string - - /// - /// Creates a reference for an F# project. The physical data for it is stored/cached inside of the compiler service. - /// - /// The fully qualified path to the output of the referenced project. This should be the same value as the -r reference in the project options for this referenced project. - /// The Project Options for this F# project - static member CreateFSharp: - projectOutputFile: string * options: FSharpProjectSnapshot -> FSharpReferencedProjectSnapshot - - member internal Key: ICacheKey - /// A set of information describing a project or script build configuration. type public FSharpProjectOptions = { @@ -305,26 +134,6 @@ and [] FSharpReferencedPro /// member OutputFile: string -type FSharpProjectSnapshot with - - member ToOptions: unit -> FSharpProjectOptions - - /// Create snapshot from FSharpProjectOptions using given function to retrieve file contnets. - /// If given a snapshotAccumulator, it will be filled with any snapshots created during the operation, i.e. from this project and any referenced projects - static member FromOptions: - options: FSharpProjectOptions * - getFileSnapshot: (FSharpProjectOptions -> string -> Async) * - ?snapshotAccumulator: Dictionary -> - Async - - /// Create snapshot from FSharpProjectOptions using the filesystem to retrieve file contents. - static member FromOptions: options: FSharpProjectOptions -> Async - - /// Create snapshot from FSharpProjectOptions using the filesystem to retrieve file contents except one file that is specified by the arguments. - static member FromOptions: - options: FSharpProjectOptions * fileName: string * fileVersion: int * sourceText: ISourceText -> - Async - /// Represents the use of an F# symbol from F# source code [] type public FSharpSymbolUse = diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs new file mode 100644 index 00000000000..8cdd5664d6f --- /dev/null +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -0,0 +1,657 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +// Open up the compiler as an incremental service for parsing, +// type checking and intellisense-like environment-reporting. + +module FSharp.Compiler.CodeAnalysis.ProjectSnapshot + +open System +open System.Collections.Generic +open System.IO +open System.Reflection +open FSharp.Compiler.IO +open Internal.Utilities.Library.Extras +open FSharp.Core.Printf +open FSharp.Compiler.Text + +open Internal.Utilities.Collections +open System.Threading.Tasks +open Internal.Utilities.Hashing +open System.Collections.Immutable + + +[] +module internal Helpers = + + let isSignatureFile (fileName: string) = + // TODO: is this robust enough? + fileName[fileName.Length - 1] = 'i' + + +[] +type internal FSharpFileSnapshot = + { + FileName: string + Version: string + GetSource: unit -> Task + } + + member this.IsSignatureFile = this.FileName |> isSignatureFile + + override this.Equals(o) = + match o with + | :? FSharpFileSnapshot as o -> o.FileName = this.FileName && o.Version = this.Version + | _ -> false + + override this.GetHashCode() = this.Key.GetHashCode() + + member this.Key = this :> ICacheKey<_, _> + + interface ICacheKey with + member this.GetLabel() = this.FileName |> shortPath + member this.GetKey() = this.FileName + member this.GetVersion() = this.Version + +type FSharpFileSnapshotWithSource = + { + FileName: string + SourceHash: ImmutableArray + Source: ISourceText + IsLastCompiland: bool + IsExe: bool + } + + member this.IsSignatureFile = this.FileName |> isSignatureFile + +type ReferenceOnDisk = + { Path: string; LastModified: DateTime } + +type ProjectSnapshotKey = string * string + +[] +type internal ProjectCore( + ProjectFileName: string, + ProjectId: string option, + ReferencesOnDisk: ReferenceOnDisk list, + OtherOptions: string list, + ReferencedProjects: FSharpReferencedProjectSnapshot list, + IsIncompleteTypeCheckEnvironment: bool, + UseScriptResolutionRules: bool, + LoadTime: DateTime, + UnresolvedReferences: FSharpUnresolvedReferencesSet option, + OriginalLoadReferences: (range * string * string) list, + Stamp: int64 option) = + + let hashForParsing = lazy ( + Md5Hasher.empty + |> Md5Hasher.addString ProjectFileName + |> Md5Hasher.addStrings OtherOptions + |> Md5Hasher.addBool IsIncompleteTypeCheckEnvironment + |> Md5Hasher.addBool UseScriptResolutionRules) + + let fullHash = lazy ( + hashForParsing.Value + |> Md5Hasher.addStrings (ReferencesOnDisk |> Seq.map (fun r -> r.Path)) + |> Md5Hasher.addBytes' ( + ReferencedProjects + |> Seq.map (fun (FSharpReference (_name, p)) -> p.SignatureVersion) + )) + + let commandLineOptions = lazy ( + seq { + for r in ReferencesOnDisk do + $"-r:{r.Path}" + + yield! OtherOptions + } + |> Seq.toList) + + let outputFileName = lazy ( + OtherOptions + |> List.tryFind (fun x -> x.StartsWith("-o:")) + |> Option.map (fun x -> x.Substring(3))) + + let key = lazy (ProjectFileName, outputFileName.Value |> Option.defaultValue "") + + member val ProjectDirectory = Path.GetDirectoryName(ProjectFileName) + member _.OutputFileName = outputFileName.Value + member _.Key = key.Value + + member _.Version = fullHash.Value + member _.VersionForParsing = hashForParsing.Value + + member _.CommandLineOptions = commandLineOptions.Value + + member _.ProjectFileName = ProjectFileName + member _.ProjectId = ProjectId + member _.ReferencesOnDisk = ReferencesOnDisk + member _.OtherOptions = OtherOptions + member _.ReferencedProjects = ReferencedProjects + member _.IsIncompleteTypeCheckEnvironment = IsIncompleteTypeCheckEnvironment + member _.UseScriptResolutionRules = UseScriptResolutionRules + member _.LoadTime = LoadTime + member _.UnresolvedReferences = UnresolvedReferences + member _.OriginalLoadReferences = OriginalLoadReferences + member _.Stamp = Stamp + +and [] internal FSharpProjectSnapshot private (projectCore: ProjectCore, sourceFiles: FSharpFileSnapshot list) = + + let noFileVersionsHash = lazy ( + projectCore.Version + |> Md5Hasher.addStrings (sourceFiles |> Seq.map (fun x -> x.FileName))) + + let noFileVersionsKey = lazy ( + { new ICacheKey<_, _> with + member this.GetLabel() = this.ToString() + member this.GetKey() = projectCore.Key + member this.GetVersion() = noFileVersionsHash.Value |> Md5Hasher.toString + }) + + let fullHash = lazy ( + projectCore.Version + |> Md5Hasher.addStrings (sourceFiles |> Seq.collect (fun x -> seq { x.FileName; x.Version }))) + + let fullKey = lazy ( + { new ICacheKey<_, _> with + member this.GetLabel() = this.ToString() + member this.GetKey() = projectCore.Key + member this.GetVersion() = fullHash.Value |> Md5Hasher.toString + }) + + let addHash (file: FSharpFileSnapshot) hash = + hash + |> Md5Hasher.addString file.FileName + |> Md5Hasher.addString file.Version + + let signatureHash = lazy ( + + let mutable lastFile = "" + + ((projectCore.Version, Set.empty), sourceFiles) + ||> Seq.fold (fun (res, sigs) file -> + if file.IsSignatureFile then + lastFile <- file.FileName + res |> addHash file, sigs |> Set.add file.FileName + else + let sigFileName = $"{file.FileName}i" + + if sigs.Contains sigFileName then + res, sigs |> Set.remove sigFileName + else + lastFile <- file.FileName + res |> addHash file, sigs) + |> fst, + lastFile) + + let signatureKey = lazy ( + { new ICacheKey<_, _> with + member this.GetLabel() = this.ToString() + member this.GetKey() = projectCore.Key + member this.GetVersion() = signatureHash.Value |> fst |> Md5Hasher.toString + }) + + let lastFileHash = lazy ( + let lastFile = sourceFiles |> List.last + let sigHash, f = signatureHash.Value + (if f = lastFile.FileName then + sigHash + else + sigHash |> addHash lastFile), + lastFile) + + let lastFileKey = lazy ( + let hash, f = lastFileHash.Value + { new ICacheKey<_, _> with + member this.GetLabel() = this.ToString() + member this.GetKey() = f.FileName, projectCore.Key + member this.GetVersion() = hash |> Md5Hasher.toString + }) + + let sourceFileNames = lazy (sourceFiles |> List.map (fun x -> x.FileName)) + + static member Create( + projectFileName: string, + projectId: string option, + sourceFiles: FSharpFileSnapshot list, + referencesOnDisk: ReferenceOnDisk list, + otherOptions: string list, + referencedProjects: FSharpReferencedProjectSnapshot list, + isIncompleteTypeCheckEnvironment: bool, + useScriptResolutionRules: bool, + loadTime: DateTime, + unresolvedReferences: FSharpUnresolvedReferencesSet option, + originalLoadReferences: (range * string * string) list, + stamp: int64 option) = + + let projectCore = ProjectCore( + projectFileName, + projectId, + referencesOnDisk, + otherOptions, + referencedProjects, + isIncompleteTypeCheckEnvironment, + useScriptResolutionRules, + loadTime, + unresolvedReferences, + originalLoadReferences, + stamp) + FSharpProjectSnapshot(projectCore, sourceFiles) + + member _.ProjectFileName = projectCore.ProjectFileName + member _.ProjectId = projectCore.ProjectId + member _.ReferencesOnDisk = projectCore.ReferencesOnDisk + member _.OtherOptions = projectCore.OtherOptions + member _.ReferencedProjects = projectCore.ReferencedProjects + member _.IsIncompleteTypeCheckEnvironment = projectCore.IsIncompleteTypeCheckEnvironment + member _.UseScriptResolutionRules = projectCore.UseScriptResolutionRules + member _.LoadTime = projectCore.LoadTime + member _.UnresolvedReferences = projectCore.UnresolvedReferences + member _.OriginalLoadReferences = projectCore.OriginalLoadReferences + member _.Stamp = projectCore.Stamp + member _.CommandLineOptions = projectCore.CommandLineOptions + member _.ProjectDirectory = projectCore.ProjectDirectory + + member _.OutputFileName = projectCore.OutputFileName + + member _.ProjectCore = projectCore + + member _.SourceFiles = sourceFiles + + member _.SourceFileNames = sourceFileNames.Value + + member _.IndexOf fileName = + sourceFiles + |> List.tryFindIndex (fun x -> x.FileName = fileName) + |> Option.defaultWith (fun () -> failwith (sprintf "Unable to find file %s in project %s" fileName projectCore.ProjectFileName)) + + member private _.With(sourceFiles: FSharpFileSnapshot list) = FSharpProjectSnapshot(projectCore, sourceFiles) + + member this.UpTo fileIndex = + this.With sourceFiles[..fileIndex] + + member this.UpTo fileName = this.UpTo(this.IndexOf fileName) + + member this.OnlyWith fileIndexes = + this.With( + fileIndexes + |> Set.toList + |> List.sort + |> List.choose (fun x -> sourceFiles |> List.tryItem x)) + + member this.WithoutImplFilesThatHaveSignatures = + let files = + (([], Set.empty), sourceFiles) + ||> Seq.fold (fun (res, sigs) file -> + if file.IsSignatureFile then + file :: res, sigs |> Set.add file.FileName + else + let sigFileName = $"{file.FileName}i" + + if sigs.Contains sigFileName then + res, sigs |> Set.remove sigFileName + else + file :: res, sigs) + |> fst + |> List.rev + + this.With files + + member this.WithoutImplFilesThatHaveSignaturesExceptLastOne = + let lastFile = sourceFiles |> List.last + + let snapshotWithoutImplFilesThatHaveSignatures = + this.WithoutImplFilesThatHaveSignatures + + if + lastFile.IsSignatureFile + || snapshotWithoutImplFilesThatHaveSignatures.SourceFiles |> List.last = lastFile + then + this.WithoutImplFilesThatHaveSignatures + else + snapshotWithoutImplFilesThatHaveSignatures.With( + snapshotWithoutImplFilesThatHaveSignatures.SourceFiles @ [ lastFile ]) + + + member this.WithoutFileVersions = + this.With(sourceFiles |> List.map (fun x -> { x with Version = "" })) + + member this.WithoutSourceFiles = this.With [] + + override this.ToString() = + Path.GetFileNameWithoutExtension this.ProjectFileName + |> sprintf "FSharpProjectSnapshot(%s)" + + member this.GetLastModifiedTimeOnDisk() = + // TODO: + DateTime.Now + + member this.GetDebugVersion() : FSharpProjectSnapshotDebugVersion = + { + ProjectFileName = this.ProjectFileName + SourceFiles = [ for f in this.SourceFiles -> f.FileName, f.Version ] + ReferencesOnDisk = this.ReferencesOnDisk + OtherOptions = this.OtherOptions + ReferencedProjects = + [ + for FSharpReference(_, p) in this.ReferencedProjects -> p.WithoutImplFilesThatHaveSignatures.GetDebugVersion() + ] + IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = this.UseScriptResolutionRules + } + + member this.FullVersion = fullHash.Value + member this.SignatureVersion = signatureHash.Value |> fst + member this.LastFileVersion = lastFileHash.Value |> fst + + /// Version for parsing - doesn't include any references because they don't affect parsing (...right?) + member this.ParsingVersion = projectCore.VersionForParsing |> Md5Hasher.toString + + /// A key for this snapshot but without file versions. So it will be the same across any in-file changes. + member this.NoFileVersionsKey = noFileVersionsKey.Value + + /// A full key for this snapshot, any change will cause this to change. + member this.FullKey = fullKey.Value + + /// A key including the public surface or signature for this snapshot + member this.SignatureKey = signatureKey.Value + + /// A key including the public surface or signature for this snapshot and the last file (even if it's not a signature file) + member this.LastFileKey = lastFileKey.Value + + //TODO: cache it here? + member this.FileKey(fileName: string) = this.UpTo(fileName).LastFileKey + + +and internal FSharpProjectSnapshotWithSources( + projectSnapshot: FSharpProjectSnapshot, + sourceFiles: FSharpFileSnapshotWithSource list + ) = + + let projectCore = projectSnapshot.ProjectCore + + let hashedVersion = lazy ( + projectCore.Version + |> Md5Hasher.addStrings (sourceFiles |> Seq.map (fun x -> x.FileName)) + |> Md5Hasher.addBytes' (sourceFiles |> Seq.map (fun x -> x.SourceHash.ToBuilder().ToArray()))) + + let fullKey = lazy ( + { new ICacheKey<_, _> with + member this.GetLabel() = this.ToString() + member this.GetKey() = projectCore.Key + member this.GetVersion() = hashedVersion.Value |> Md5Hasher.toString + }) + + let addHash (file: FSharpFileSnapshotWithSource) hash = + hash + |> Md5Hasher.addString file.FileName + |> Md5Hasher.addBytes <| file.SourceHash.ToBuilder().ToArray() + + let signatureHash = lazy ( + + let mutable lastFile = "" + + ((projectCore.Version, Set.empty), sourceFiles) + ||> Seq.fold (fun (res, sigs) file -> + if file.IsSignatureFile then + lastFile <- file.FileName + res |> addHash file, sigs |> Set.add file.FileName + else + let sigFileName = $"{file.FileName}i" + + if sigs.Contains sigFileName then + res, sigs |> Set.remove sigFileName + else + lastFile <- file.FileName + res |> addHash file, sigs) + |> fst, + lastFile) + + let signatureKey = lazy ( + { new ICacheKey<_, _> with + member this.GetLabel() = this.ToString() + member this.GetKey() = projectCore.Key + member this.GetVersion() = signatureHash.Value |> fst |> Md5Hasher.toString + }) + + let lastFileHash = lazy ( + let lastFile = sourceFiles |> List.last + let sigHash, f = signatureHash.Value + (if f = lastFile.FileName then + sigHash + else + sigHash |> addHash lastFile), + lastFile) + + let lastFileKey = lazy ( + let hash, f = lastFileHash.Value + { new ICacheKey<_, _> with + member this.GetLabel() = this.ToString() + member this.GetKey() = f.FileName, projectCore.Key + member this.GetVersion() = hash |> Md5Hasher.toString + }) + + member _.ProjectSnapshot = projectSnapshot + member _.SourceFiles = sourceFiles + + member this.IndexOf fileName = + this.SourceFiles + |> List.tryFindIndex (fun x -> x.FileName = fileName) + |> Option.defaultWith (fun () -> + failwith (sprintf "Unable to find file %s in project %s" fileName this.ProjectSnapshot.ProjectFileName)) + + member private _.With(sourceFiles: FSharpFileSnapshotWithSource list) = FSharpProjectSnapshotWithSources(projectSnapshot, sourceFiles) + + member this.UpTo(fileIndex: FileIndex) = this.With sourceFiles[..fileIndex] + + member this.UpTo fileName = this.UpTo(this.IndexOf fileName) + + member this.WithoutImplFilesThatHaveSignatures = + let files = + (([], Set.empty), this.SourceFiles) + ||> Seq.fold (fun (res, sigs) file -> + if file.IsSignatureFile then + file :: res, sigs |> Set.add file.FileName + else + let sigFileName = $"{file.FileName}i" + + if sigs.Contains sigFileName then + res, sigs |> Set.remove sigFileName + else + file :: res, sigs) + |> fst + |> List.rev + + this.With files + + member this.WithoutImplFilesThatHaveSignaturesExceptLastOne = + let lastFile = this.SourceFiles |> List.last + + let snapshotWithoutImplFilesThatHaveSignatures = + this.WithoutImplFilesThatHaveSignatures + + if + lastFile.IsSignatureFile + || snapshotWithoutImplFilesThatHaveSignatures.SourceFiles |> List.last = lastFile + then + this.WithoutImplFilesThatHaveSignatures + else + this.With( snapshotWithoutImplFilesThatHaveSignatures.SourceFiles @ [ lastFile ]) + + member this.GetDebugVersion() = + { + ProjectSnapshotVersion = this.ProjectSnapshot.WithoutFileVersions.GetDebugVersion() + SourceVersions = this.SourceFiles |> List.map (fun x -> x.SourceHash.ToBuilder().ToArray() |> BitConverter.ToString) + } + + member this.FullKey = fullKey.Value + member this.SignatureKey = signatureKey.Value + member this.LastFileKey = lastFileKey.Value + member this.FileKey(fileName: string) = this.UpTo(fileName).LastFileKey + + +and FSharpProjectSnapshotWithSourcesDebugVersion = + { + ProjectSnapshotVersion: FSharpProjectSnapshotDebugVersion + SourceVersions: string list + } + +and FSharpProjectSnapshotWithSourcesVersion = FSharpProjectSnapshotWithSourcesDebugVersion + +and FSharpProjectSnapshotDebugVersion = + { + ProjectFileName: string + SourceFiles: (string * string) list + ReferencesOnDisk: ReferenceOnDisk list + OtherOptions: string list + ReferencedProjects: FSharpProjectSnapshotDebugVersion list + IsIncompleteTypeCheckEnvironment: bool + UseScriptResolutionRules: bool + } + +and FSharpProjectSnapshotVersion = string + +and [] internal FSharpReferencedProjectSnapshot = + internal + | FSharpReference of projectOutputFile: string * options: FSharpProjectSnapshot + //| PEReference of projectOutputFile: string * getStamp: (unit -> DateTime) * delayedReader: DelayedILModuleReader + //| ILModuleReference of + // projectOutputFile: string * + // getStamp: (unit -> DateTime) * + // getReader: (unit -> ILModuleReader) + + /// + /// The fully qualified path to the output of the referenced project. This should be the same value as the -r + /// reference in the project options for this referenced project. + /// + member this.OutputFile = + match this with + | FSharpReference(projectOutputFile, _) -> projectOutputFile + + /// + /// Creates a reference for an F# project. The physical data for it is stored/cached inside of the compiler service. + /// + /// The fully qualified path to the output of the referenced project. This should be the same value as the -r reference in the project options for this referenced project. + /// The Project Options for this F# project + static member CreateFSharp(projectOutputFile, options: FSharpProjectSnapshot) = + FSharpReference(projectOutputFile, options) + + override this.Equals(o) = + match o with + | :? FSharpReferencedProjectSnapshot as o -> + match this, o with + | FSharpReference(projectOutputFile1, options1), FSharpReference(projectOutputFile2, options2) -> + projectOutputFile1 = projectOutputFile2 && options1 = options2 + + | _ -> false + + override this.GetHashCode() = this.OutputFile.GetHashCode() + +type FSharpProjectSnapshot with + + member this.ToOptions() : FSharpProjectOptions = + { + ProjectFileName = this.ProjectFileName + ProjectId = this.ProjectId + SourceFiles = this.SourceFiles |> Seq.map (fun x -> x.FileName) |> Seq.toArray + OtherOptions = this.CommandLineOptions |> List.toArray + ReferencedProjects = + this.ReferencedProjects + |> Seq.map (function + | FSharpReference(name, opts) -> FSharpReferencedProject.FSharpReference(name, opts.ToOptions())) + |> Seq.toArray + IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = this.UseScriptResolutionRules + LoadTime = this.LoadTime + UnresolvedReferences = this.UnresolvedReferences + OriginalLoadReferences = this.OriginalLoadReferences + Stamp = this.Stamp + } + + static member FromOptions(options: FSharpProjectOptions, getFileSnapshot, ?snapshotAccumulator) = + let snapshotAccumulator = defaultArg snapshotAccumulator (Dictionary()) + + async { + + // TODO: check if options is a good key here + if not (snapshotAccumulator.ContainsKey options) then + + let! sourceFiles = options.SourceFiles |> Seq.map (getFileSnapshot options) |> Async.Parallel + + let! referencedProjects = + options.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProject.FSharpReference(outputName, options) -> + Some( + async { + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, snapshotAccumulator) + return FSharpReferencedProjectSnapshot.FSharpReference(outputName, snapshot) + } + ) + // TODO: other types + | _ -> None) + |> Async.Sequential + + let referencesOnDisk, otherOptions = + options.OtherOptions + |> Array.partition (fun x -> x.StartsWith("-r:")) + |> map1Of2 ( + Array.map (fun x -> + let path = x.Substring(3) + + { + Path = path + LastModified = FileSystem.GetLastWriteTimeShim(path) + }) + ) + + let snapshot = + FSharpProjectSnapshot.Create( + projectFileName = options.ProjectFileName, + projectId = options.ProjectId, + sourceFiles = (sourceFiles |> List.ofArray), + referencesOnDisk = (referencesOnDisk |> List.ofArray), + otherOptions = (otherOptions |> List.ofArray), + referencedProjects = (referencedProjects |> List.ofArray), + isIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment, + useScriptResolutionRules = options.UseScriptResolutionRules, + loadTime = options.LoadTime, + unresolvedReferences = options.UnresolvedReferences, + originalLoadReferences = options.OriginalLoadReferences, + stamp = options.Stamp + ) + + snapshotAccumulator.Add(options, snapshot) + + return snapshotAccumulator[options] + } + + static member GetFileSnapshotFromDisk _ fileName = + async { + let timeStamp = FileSystem.GetLastWriteTimeShim(fileName) + let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() + + return + { + FileName = fileName + Version = timeStamp.Ticks.ToString() + GetSource = fun () -> Task.FromResult(SourceText.ofString contents) + } + } + + static member FromOptions(options: FSharpProjectOptions) = + FSharpProjectSnapshot.FromOptions(options, FSharpProjectSnapshot.GetFileSnapshotFromDisk) + + static member FromOptions(options: FSharpProjectOptions, fileName: string, fileVersion: int, sourceText: ISourceText) = + + let getFileSnapshot _ fName = + if fName = fileName then + async.Return + { + FileName = fileName + GetSource = fun () -> Task.FromResult sourceText + Version = $"{fileVersion}{sourceText.GetHashCode().ToString()}" + } + else + FSharpProjectSnapshot.GetFileSnapshotFromDisk () fName + + FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) \ No newline at end of file diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 247db5bc3a6..9dd9252ac8e 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -44,6 +44,8 @@ open FSharp.Compiler.TypedTreeOps open System.Threading open Internal.Utilities.Hashing +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot + type internal FSharpFile = { Range: range @@ -152,7 +154,8 @@ type internal Extensions = member _.GetVersion() = Md5Hasher.empty - |> Md5Hasher.addStrings (fileSnapshots |> Seq.map (fun f -> f.SourceHash)) + |> Md5Hasher.addBytes' (fileSnapshots |> Seq.map (fun f -> f.SourceHash.ToBuilder().ToArray())) + |> Md5Hasher.toString } [] @@ -615,7 +618,7 @@ type internal TransparentCompiler let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) = caches.BootstrapInfoStatic.Get( - projectSnapshot.WithoutFileVersions.Key, + projectSnapshot.NoFileVersionsKey, node { use _ = Activity.start @@ -815,7 +818,7 @@ type internal TransparentCompiler let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) = caches.BootstrapInfo.Get( - projectSnapshot.Key, + projectSnapshot.FullKey, node { use _ = Activity.start "ComputeBootstrapInfo" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] @@ -865,9 +868,7 @@ type internal TransparentCompiler { FileName = file.Source.FileName Source = source - SourceHash = - Md5Hasher.empty - |> Md5Hasher.addBytes (source.GetChecksum().ToBuilder().ToArray()) + SourceHash = source.GetChecksum() IsLastCompiland = file.IsLastCompiland IsExe = file.IsExe } @@ -883,28 +884,23 @@ type internal TransparentCompiler |> Seq.map (fun f -> f.FileName |> sourceFileMap.TryFind - |> Option.defaultWith (fun _ -> failwith $"File {f.FileName} not found in {projectSnapshot.Key.GetLabel()}") + |> Option.defaultWith (fun _ -> failwith $"File {f.FileName} not found in {projectSnapshot}") |> LoadSource) |> NodeCode.Parallel - return - { - ProjectSnapshot = projectSnapshot - SourceFiles = sources |> Array.toList - } + return FSharpProjectSnapshotWithSources (projectSnapshot, sources |> Array.toList) + } let ComputeParseFile (projectSnapshot: FSharpProjectSnapshot) (file: FSharpFileSnapshotWithSource) = - let projectKey = projectSnapshot.WithoutSourceFiles.WithoutReferences.Key - let key = { new ICacheKey<_, _> with member _.GetLabel() = file.FileName |> shortPath - member _.GetKey() = projectKey.GetKey(), file.FileName + member _.GetKey() = projectSnapshot.ProjectCore.Key, file.FileName member _.GetVersion() = - projectKey.GetVersion(), + projectSnapshot.ParsingVersion, file.SourceHash, // TODO: is there a situation where this is not enough and we need to have them separate? file.IsLastCompiland && file.IsExe @@ -918,7 +914,7 @@ type internal TransparentCompiler "ComputeParseFile" [| Activity.Tags.fileName, file.FileName |> shortPath - Activity.Tags.version, file.SourceHash + Activity.Tags.version, file.SourceHash.ToBuilder().ToArray() |> Md5Hasher.toString |] // TODO: might need to deal with exceptions here: @@ -1450,7 +1446,7 @@ type internal TransparentCompiler let ComputeParseAndCheckAllFilesInProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = caches.ParseAndCheckAllFilesInProject.Get( - projectSnapshot.Key, + projectSnapshot.FullKey, node { use _ = Activity.start @@ -1478,7 +1474,7 @@ type internal TransparentCompiler let ComputeProjectExtras (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = caches.ProjectExtras.Get( - projectSnapshot.WithoutImplFilesThatHaveSignatures.Key, + projectSnapshot.SignatureKey, node { let! results, finalInfo = ComputeParseAndCheckAllFilesInProject bootstrapInfo projectSnapshot @@ -1571,7 +1567,7 @@ type internal TransparentCompiler let ComputeAssemblyData (projectSnapshot: FSharpProjectSnapshot) fileName = caches.AssemblyData.Get( - projectSnapshot.WithoutImplFilesThatHaveSignatures.Key, + projectSnapshot.SignatureKey, node { let availableOnDiskModifiedTime = if FileSystem.FileExistsShim fileName then @@ -1606,7 +1602,7 @@ type internal TransparentCompiler let ComputeParseAndCheckProject (projectSnapshot: FSharpProjectSnapshot) = caches.ParseAndCheckProject.Get( - projectSnapshot.Key, + projectSnapshot.FullKey, node { match! ComputeBootstrapInfo projectSnapshot with diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs index a2c40b64b81..f57c85e6d0e 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs @@ -1,33 +1,33 @@ module FSharpChecker.ProjectSnapshot open Xunit - -open FSharp.Compiler.CodeAnalysis +open System +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot [] let WithoutImplFilesThatHaveSignatures () = - let snapshot: FSharpProjectSnapshot = { - ProjectFileName = Unchecked.defaultof<_> - ProjectId = Unchecked.defaultof<_> - SourceFiles = [ + let snapshot = FSharpProjectSnapshot.Create( + projectFileName = "Dummy.fsproj", + projectId = None, + sourceFiles = [ { FileName = "A.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } { FileName = "C.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } - ] - ReferencesOnDisk = Unchecked.defaultof<_> - OtherOptions = Unchecked.defaultof<_> - ReferencedProjects = Unchecked.defaultof<_> - IsIncompleteTypeCheckEnvironment = Unchecked.defaultof<_> - UseScriptResolutionRules = Unchecked.defaultof<_> - LoadTime = Unchecked.defaultof<_> - UnresolvedReferences = Unchecked.defaultof<_> - OriginalLoadReferences = Unchecked.defaultof<_> - Stamp = Unchecked.defaultof<_> - } + ], + referencesOnDisk = [], + otherOptions = [], + referencedProjects = [], + isIncompleteTypeCheckEnvironment = true, + useScriptResolutionRules = false, + loadTime = DateTime(1234, 5, 6), + unresolvedReferences = None, + originalLoadReferences = [], + stamp = None + ) let result = snapshot.WithoutImplFilesThatHaveSignatures @@ -35,29 +35,31 @@ let WithoutImplFilesThatHaveSignatures () = Assert.Equal(expected, result.SourceFileNames |> List.toArray) + Assert.Equal(result.FullVersion, snapshot.SignatureVersion) + [] let WithoutImplFilesThatHaveSignaturesExceptLastOne () = - let snapshot: FSharpProjectSnapshot = { - ProjectFileName = Unchecked.defaultof<_> - ProjectId = Unchecked.defaultof<_> - SourceFiles = [ + let snapshot = FSharpProjectSnapshot.Create( + projectFileName = "Dummy.fsproj", + projectId = None, + sourceFiles = [ { FileName = "A.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } { FileName = "C.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } - ] - ReferencesOnDisk = Unchecked.defaultof<_> - OtherOptions = Unchecked.defaultof<_> - ReferencedProjects = Unchecked.defaultof<_> - IsIncompleteTypeCheckEnvironment = Unchecked.defaultof<_> - UseScriptResolutionRules = Unchecked.defaultof<_> - LoadTime = Unchecked.defaultof<_> - UnresolvedReferences = Unchecked.defaultof<_> - OriginalLoadReferences = Unchecked.defaultof<_> - Stamp = Unchecked.defaultof<_> - } + ], + referencesOnDisk = [], + otherOptions = [], + referencedProjects = [], + isIncompleteTypeCheckEnvironment = true, + useScriptResolutionRules = false, + loadTime = DateTime(1234, 5, 6), + unresolvedReferences = None, + originalLoadReferences = [], + stamp = None + ) let result = snapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne @@ -65,31 +67,36 @@ let WithoutImplFilesThatHaveSignaturesExceptLastOne () = Assert.Equal(expected, result.SourceFileNames |> List.toArray) + Assert.Equal(result.FullVersion, snapshot.LastFileVersion) + [] let WithoutImplFilesThatHaveSignaturesExceptLastOne_2 () = - let snapshot: FSharpProjectSnapshot = { - ProjectFileName = Unchecked.defaultof<_> - ProjectId = Unchecked.defaultof<_> - SourceFiles = [ + let snapshot = FSharpProjectSnapshot.Create( + projectFileName = "Dummy.fsproj", + projectId = None, + sourceFiles = [ { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } - ] - ReferencesOnDisk = Unchecked.defaultof<_> - OtherOptions = Unchecked.defaultof<_> - ReferencedProjects = Unchecked.defaultof<_> - IsIncompleteTypeCheckEnvironment = Unchecked.defaultof<_> - UseScriptResolutionRules = Unchecked.defaultof<_> - LoadTime = Unchecked.defaultof<_> - UnresolvedReferences = Unchecked.defaultof<_> - OriginalLoadReferences = Unchecked.defaultof<_> - Stamp = Unchecked.defaultof<_> - } + ], + referencesOnDisk = [], + otherOptions = [], + referencedProjects = [], + isIncompleteTypeCheckEnvironment = true, + useScriptResolutionRules = false, + loadTime = DateTime(1234, 5, 6), + unresolvedReferences = None, + originalLoadReferences = [], + stamp = None + ) let result = snapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne let expected = [| "A.fs"; "B.fs"; "C.fs" |] Assert.Equal(expected, result.SourceFileNames |> List.toArray) + + Assert.Equal(result.FullVersion, snapshot.LastFileVersion) + diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index a6aa727a8c4..4b3e88a19b0 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -836,9 +836,9 @@ let Fuzzing' signatureFiles = builder.DeleteProjectDir() -//[] -//[] -//[] +[] +[] +[] let GiraffeFuzzing signatureFiles = let seed = System.Random().Next() //let seed = 1044159179 diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index 80499cfb49f..a8d338fe0c2 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -11,6 +11,7 @@ open System.Reflection open FSharp.Compiler.Interactive.Shell open FSharp.Compiler.IO open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot open FSharp.Compiler.Diagnostics open FSharp.Compiler.Text #if NETCOREAPP diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index a8d4ce8f821..22b7f064934 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -25,6 +25,7 @@ open System.Threading.Tasks open System.Xml open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot open FSharp.Compiler.Diagnostics open FSharp.Compiler.Text diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 20aee8e2bfe..3f3e66e5803 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -9,6 +9,7 @@ open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot open FSharp.Compiler.Symbols open FSharp.Compiler.BuildGraph From 0b132b1d242bf3605b1bc628a65bbf7a5b4e5b22 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 5 Dec 2023 15:11:12 +0100 Subject: [PATCH 166/222] FSharpProjectSnapshotBase<_> --- src/Compiler/Facilities/AsyncMemoize.fs | 2 +- src/Compiler/Facilities/Hashing.fs | 2 +- src/Compiler/Service/BackgroundCompiler.fs | 1 + src/Compiler/Service/FSharpProjectSnapshot.fs | 619 +++++++----------- src/Compiler/Service/TransparentCompiler.fs | 131 ++-- .../FSharp.Compiler.ComponentTests.fsproj | 1 + .../FSharpChecker/ProjectSnapshot.fs | 190 +++--- .../FSharpChecker/TransparentCompiler.fs | 32 + tests/FSharp.Test.Utilities/CompilerAssert.fs | 8 +- .../ProjectGeneration.fs | 17 +- .../LanguageService/WorkspaceExtensions.fs | 8 +- 11 files changed, 456 insertions(+), 555 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 83e86c02b15..75914afb783 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -497,7 +497,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let stats = [| if errors + failed > 0 then - " /_!_\ " + " (_!_) " if errors > 0 then $"| ERRORS: {errors} " else "" if failed > 0 then $"| FAILED: {failed} " else "" $"| hits: {hits}{hitRatio} " diff --git a/src/Compiler/Facilities/Hashing.fs b/src/Compiler/Facilities/Hashing.fs index 8f67b8e89fd..a8afcf2371e 100644 --- a/src/Compiler/Facilities/Hashing.fs +++ b/src/Compiler/Facilities/Hashing.fs @@ -46,7 +46,7 @@ module internal Md5Hasher = let private md5 = new ThreadLocal<_>(fun () -> System.Security.Cryptography.MD5.Create()) - let private computeHash (bytes: byte array) = md5.Value.ComputeHash(bytes) + let computeHash (bytes: byte array) = md5.Value.ComputeHash(bytes) let empty = Array.empty diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 98b03f393db..b110962bdb3 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -36,6 +36,7 @@ open FSharp.Compiler.Text open FSharp.Compiler.Text.Range open FSharp.Compiler.TcGlobals open FSharp.Compiler.BuildGraph +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot type SourceTextHash = int64 type CacheStamp = int64 diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 8cdd5664d6f..4ddc584258b 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -3,7 +3,7 @@ // Open up the compiler as an incremental service for parsing, // type checking and intellisense-like environment-reporting. -module FSharp.Compiler.CodeAnalysis.ProjectSnapshot +module internal FSharp.Compiler.CodeAnalysis.ProjectSnapshot open System open System.Collections.Generic @@ -18,6 +18,15 @@ open Internal.Utilities.Collections open System.Threading.Tasks open Internal.Utilities.Hashing open System.Collections.Immutable +open System.Runtime.CompilerServices +open FSharp.Compiler.Syntax +open FSharp.Compiler.Diagnostics +open FSharp.Compiler.DiagnosticsLogger + +type IFileSnapshot = + abstract member FileName: string + abstract member Version: byte array + abstract member IsSignatureFile: bool [] @@ -27,114 +36,110 @@ module internal Helpers = // TODO: is this robust enough? fileName[fileName.Length - 1] = 'i' + let addFileName (file: IFileSnapshot) = Md5Hasher.addString file.FileName -[] -type internal FSharpFileSnapshot = - { - FileName: string - Version: string - GetSource: unit -> Task - } + let addFileNameAndVersion (file: IFileSnapshot) = addFileName file >> Md5Hasher.addBytes file.Version + + let signatureHash projectCoreVersion (sourceFiles: IFileSnapshot seq) = + let mutable lastFile = "" - member this.IsSignatureFile = this.FileName |> isSignatureFile + ((projectCoreVersion, Set.empty), sourceFiles) + ||> Seq.fold (fun (res, sigs) file -> + if file.IsSignatureFile then + lastFile <- file.FileName + res |> addFileNameAndVersion file , sigs |> Set.add file.FileName + else + let sigFileName = $"{file.FileName}i" + + if sigs.Contains sigFileName then + res |> addFileName file, sigs |> Set.remove sigFileName + else + lastFile <- file.FileName + res |> addFileNameAndVersion file, sigs) + |> fst, + lastFile + + +type FSharpFileSnapshot( + FileName: string, + Version: string, + GetSource: unit -> Task) = + + static member Create(fileName: string, version: string, getSource: unit -> Task) = + FSharpFileSnapshot(fileName, version, getSource) + + member public _.FileName = FileName + member _.Version = Version + member _.GetSource() = GetSource() + + member val IsSignatureFile = FileName |> isSignatureFile + + member _.GetFileName() = FileName override this.Equals(o) = match o with | :? FSharpFileSnapshot as o -> o.FileName = this.FileName && o.Version = this.Version | _ -> false - override this.GetHashCode() = this.Key.GetHashCode() + override this.GetHashCode() = this.FileName.GetHashCode() + this.Version.GetHashCode() - member this.Key = this :> ICacheKey<_, _> + interface IFileSnapshot with + member this.FileName = this.FileName + member this.Version = this.Version |> System.Text.Encoding.UTF8.GetBytes + member this.IsSignatureFile = this.IsSignatureFile - interface ICacheKey with - member this.GetLabel() = this.FileName |> shortPath - member this.GetKey() = this.FileName - member this.GetVersion() = this.Version -type FSharpFileSnapshotWithSource = - { - FileName: string - SourceHash: ImmutableArray - Source: ISourceText - IsLastCompiland: bool - IsExe: bool - } - member this.IsSignatureFile = this.FileName |> isSignatureFile +type FSharpFileSnapshotWithSource( + FileName: string, + SourceHash: ImmutableArray, + Source: ISourceText, + IsLastCompiland: bool, + IsExe: bool) = -type ReferenceOnDisk = - { Path: string; LastModified: DateTime } + let version = lazy (SourceHash.ToBuilder().ToArray()) + let stringVersion = lazy (version.Value |> BitConverter.ToString) -type ProjectSnapshotKey = string * string + member val Version = version.Value + member val StringVersion = stringVersion.Value + member val IsSignatureFile = FileName |> isSignatureFile -[] -type internal ProjectCore( - ProjectFileName: string, - ProjectId: string option, - ReferencesOnDisk: ReferenceOnDisk list, - OtherOptions: string list, - ReferencedProjects: FSharpReferencedProjectSnapshot list, - IsIncompleteTypeCheckEnvironment: bool, - UseScriptResolutionRules: bool, - LoadTime: DateTime, - UnresolvedReferences: FSharpUnresolvedReferencesSet option, - OriginalLoadReferences: (range * string * string) list, - Stamp: int64 option) = + member _.FileName = FileName + member _.Source = Source + member _.IsLastCompiland = IsLastCompiland + member _.IsExe = IsExe - let hashForParsing = lazy ( - Md5Hasher.empty - |> Md5Hasher.addString ProjectFileName - |> Md5Hasher.addStrings OtherOptions - |> Md5Hasher.addBool IsIncompleteTypeCheckEnvironment - |> Md5Hasher.addBool UseScriptResolutionRules) + interface IFileSnapshot with + member this.FileName = this.FileName + member this.Version = this.Version + member this.IsSignatureFile = this.IsSignatureFile - let fullHash = lazy ( - hashForParsing.Value - |> Md5Hasher.addStrings (ReferencesOnDisk |> Seq.map (fun r -> r.Path)) - |> Md5Hasher.addBytes' ( - ReferencedProjects - |> Seq.map (fun (FSharpReference (_name, p)) -> p.SignatureVersion) - )) - let commandLineOptions = lazy ( - seq { - for r in ReferencesOnDisk do - $"-r:{r.Path}" - - yield! OtherOptions - } - |> Seq.toList) +type FSharpParsedFile( + FileName: string, + SyntaxTreeHash: byte array, + ParsedInput: ParsedInput, + ParseErrors: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] + ) = - let outputFileName = lazy ( - OtherOptions - |> List.tryFind (fun x -> x.StartsWith("-o:")) - |> Option.map (fun x -> x.Substring(3))) + member _.FileName = FileName + member _.ParsedInput = ParsedInput + member _.ParseErrors = ParseErrors - let key = lazy (ProjectFileName, outputFileName.Value |> Option.defaultValue "") + member val IsSignatureFile = FileName |> isSignatureFile - member val ProjectDirectory = Path.GetDirectoryName(ProjectFileName) - member _.OutputFileName = outputFileName.Value - member _.Key = key.Value + interface IFileSnapshot with + member this.FileName = this.FileName + member this.Version = SyntaxTreeHash + member this.IsSignatureFile = this.IsSignatureFile - member _.Version = fullHash.Value - member _.VersionForParsing = hashForParsing.Value +type ReferenceOnDisk = + { Path: string; LastModified: DateTime } - member _.CommandLineOptions = commandLineOptions.Value +type ProjectSnapshotKey = string * string - member _.ProjectFileName = ProjectFileName - member _.ProjectId = ProjectId - member _.ReferencesOnDisk = ReferencesOnDisk - member _.OtherOptions = OtherOptions - member _.ReferencedProjects = ReferencedProjects - member _.IsIncompleteTypeCheckEnvironment = IsIncompleteTypeCheckEnvironment - member _.UseScriptResolutionRules = UseScriptResolutionRules - member _.LoadTime = LoadTime - member _.UnresolvedReferences = UnresolvedReferences - member _.OriginalLoadReferences = OriginalLoadReferences - member _.Stamp = Stamp -and [] internal FSharpProjectSnapshot private (projectCore: ProjectCore, sourceFiles: FSharpFileSnapshot list) = +type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot> (projectCore: ProjectCore, sourceFiles: 'T list) = let noFileVersionsHash = lazy ( projectCore.Version @@ -149,7 +154,7 @@ and [] internal FSharpProjectSnapshot private (projectCore: Projec let fullHash = lazy ( projectCore.Version - |> Md5Hasher.addStrings (sourceFiles |> Seq.collect (fun x -> seq { x.FileName; x.Version }))) + |> Md5Hasher.addStrings (sourceFiles |> Seq.collect (fun x -> seq { x.FileName; x.Version |> Md5Hasher.toString }))) let fullKey = lazy ( { new ICacheKey<_, _> with @@ -158,37 +163,14 @@ and [] internal FSharpProjectSnapshot private (projectCore: Projec member this.GetVersion() = fullHash.Value |> Md5Hasher.toString }) - let addHash (file: FSharpFileSnapshot) hash = + let addHash (file: 'T) hash = hash |> Md5Hasher.addString file.FileName - |> Md5Hasher.addString file.Version - - let signatureHash = lazy ( - - let mutable lastFile = "" - - ((projectCore.Version, Set.empty), sourceFiles) - ||> Seq.fold (fun (res, sigs) file -> - if file.IsSignatureFile then - lastFile <- file.FileName - res |> addHash file, sigs |> Set.add file.FileName - else - let sigFileName = $"{file.FileName}i" + |> Md5Hasher.addBytes file.Version - if sigs.Contains sigFileName then - res, sigs |> Set.remove sigFileName - else - lastFile <- file.FileName - res |> addHash file, sigs) - |> fst, - lastFile) + let signatureHash = lazy (signatureHash projectCore.Version (sourceFiles |> Seq.map (fun x -> x :> IFileSnapshot))) - let signatureKey = lazy ( - { new ICacheKey<_, _> with - member this.GetLabel() = this.ToString() - member this.GetKey() = projectCore.Key - member this.GetVersion() = signatureHash.Value |> fst |> Md5Hasher.toString - }) + let signatureKey = lazy (projectCore.CacheKeyWith ("Signature", signatureHash.Value |> fst |> Md5Hasher.toString)) let lastFileHash = lazy ( let lastFile = sourceFiles |> List.last @@ -196,15 +178,15 @@ and [] internal FSharpProjectSnapshot private (projectCore: Projec (if f = lastFile.FileName then sigHash else - sigHash |> addHash lastFile), + sigHash |> Md5Hasher.addBytes lastFile.Version), lastFile) let lastFileKey = lazy ( let hash, f = lastFileHash.Value { new ICacheKey<_, _> with - member this.GetLabel() = this.ToString() - member this.GetKey() = f.FileName, projectCore.Key - member this.GetVersion() = hash |> Md5Hasher.toString + member _.GetLabel() = $"{projectCore.Label} (last file)" + member _.GetKey() = f.FileName, projectCore.Key + member _.GetVersion() = hash |> Md5Hasher.toString }) let sourceFileNames = lazy (sourceFiles |> List.map (fun x -> x.FileName)) @@ -212,7 +194,7 @@ and [] internal FSharpProjectSnapshot private (projectCore: Projec static member Create( projectFileName: string, projectId: string option, - sourceFiles: FSharpFileSnapshot list, + sourceFiles: 'T list, referencesOnDisk: ReferenceOnDisk list, otherOptions: string list, referencedProjects: FSharpReferencedProjectSnapshot list, @@ -235,7 +217,7 @@ and [] internal FSharpProjectSnapshot private (projectCore: Projec unresolvedReferences, originalLoadReferences, stamp) - FSharpProjectSnapshot(projectCore, sourceFiles) + FSharpProjectSnapshotBase(projectCore, sourceFiles) member _.ProjectFileName = projectCore.ProjectFileName member _.ProjectId = projectCore.ProjectId @@ -264,7 +246,7 @@ and [] internal FSharpProjectSnapshot private (projectCore: Projec |> List.tryFindIndex (fun x -> x.FileName = fileName) |> Option.defaultWith (fun () -> failwith (sprintf "Unable to find file %s in project %s" fileName projectCore.ProjectFileName)) - member private _.With(sourceFiles: FSharpFileSnapshot list) = FSharpProjectSnapshot(projectCore, sourceFiles) + member private _.With(sourceFiles: 'T list) = FSharpProjectSnapshotBase(projectCore, sourceFiles) member this.UpTo fileIndex = this.With sourceFiles[..fileIndex] @@ -278,43 +260,6 @@ and [] internal FSharpProjectSnapshot private (projectCore: Projec |> List.sort |> List.choose (fun x -> sourceFiles |> List.tryItem x)) - member this.WithoutImplFilesThatHaveSignatures = - let files = - (([], Set.empty), sourceFiles) - ||> Seq.fold (fun (res, sigs) file -> - if file.IsSignatureFile then - file :: res, sigs |> Set.add file.FileName - else - let sigFileName = $"{file.FileName}i" - - if sigs.Contains sigFileName then - res, sigs |> Set.remove sigFileName - else - file :: res, sigs) - |> fst - |> List.rev - - this.With files - - member this.WithoutImplFilesThatHaveSignaturesExceptLastOne = - let lastFile = sourceFiles |> List.last - - let snapshotWithoutImplFilesThatHaveSignatures = - this.WithoutImplFilesThatHaveSignatures - - if - lastFile.IsSignatureFile - || snapshotWithoutImplFilesThatHaveSignatures.SourceFiles |> List.last = lastFile - then - this.WithoutImplFilesThatHaveSignatures - else - snapshotWithoutImplFilesThatHaveSignatures.With( - snapshotWithoutImplFilesThatHaveSignatures.SourceFiles @ [ lastFile ]) - - - member this.WithoutFileVersions = - this.With(sourceFiles |> List.map (fun x -> { x with Version = "" })) - member this.WithoutSourceFiles = this.With [] override this.ToString() = @@ -325,20 +270,6 @@ and [] internal FSharpProjectSnapshot private (projectCore: Projec // TODO: DateTime.Now - member this.GetDebugVersion() : FSharpProjectSnapshotDebugVersion = - { - ProjectFileName = this.ProjectFileName - SourceFiles = [ for f in this.SourceFiles -> f.FileName, f.Version ] - ReferencesOnDisk = this.ReferencesOnDisk - OtherOptions = this.OtherOptions - ReferencedProjects = - [ - for FSharpReference(_, p) in this.ReferencedProjects -> p.WithoutImplFilesThatHaveSignatures.GetDebugVersion() - ] - IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment - UseScriptResolutionRules = this.UseScriptResolutionRules - } - member this.FullVersion = fullHash.Value member this.SignatureVersion = signatureHash.Value |> fst member this.LastFileVersion = lastFileHash.Value |> fst @@ -360,155 +291,173 @@ and [] internal FSharpProjectSnapshot private (projectCore: Projec //TODO: cache it here? member this.FileKey(fileName: string) = this.UpTo(fileName).LastFileKey + member this.FileKey(index: FileIndex) = this.UpTo(index).LastFileKey -and internal FSharpProjectSnapshotWithSources( - projectSnapshot: FSharpProjectSnapshot, - sourceFiles: FSharpFileSnapshotWithSource list - ) = + static member FromOptions(options: FSharpProjectOptions, getFileSnapshot, ?snapshotAccumulator) = + let snapshotAccumulator = defaultArg snapshotAccumulator (Dictionary()) - let projectCore = projectSnapshot.ProjectCore + async { - let hashedVersion = lazy ( - projectCore.Version - |> Md5Hasher.addStrings (sourceFiles |> Seq.map (fun x -> x.FileName)) - |> Md5Hasher.addBytes' (sourceFiles |> Seq.map (fun x -> x.SourceHash.ToBuilder().ToArray()))) + // TODO: check if options is a good key here + if not (snapshotAccumulator.ContainsKey options) then - let fullKey = lazy ( - { new ICacheKey<_, _> with - member this.GetLabel() = this.ToString() - member this.GetKey() = projectCore.Key - member this.GetVersion() = hashedVersion.Value |> Md5Hasher.toString - }) + let! sourceFiles = options.SourceFiles |> Seq.map (getFileSnapshot options) |> Async.Parallel - let addHash (file: FSharpFileSnapshotWithSource) hash = - hash - |> Md5Hasher.addString file.FileName - |> Md5Hasher.addBytes <| file.SourceHash.ToBuilder().ToArray() + let! referencedProjects = + options.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProject.FSharpReference(outputName, options) -> + Some( + async { + let! snapshot = FSharpProjectSnapshotBase<_>.FromOptions(options, getFileSnapshot, snapshotAccumulator) + return FSharpReferencedProjectSnapshot.FSharpReference(outputName, snapshot) + } + ) + // TODO: other types + | _ -> None) + |> Async.Sequential - let signatureHash = lazy ( + let referencesOnDisk, otherOptions = + options.OtherOptions + |> Array.partition (fun x -> x.StartsWith("-r:")) + |> map1Of2 ( + Array.map (fun x -> + let path = x.Substring(3) - let mutable lastFile = "" + { + Path = path + LastModified = FileSystem.GetLastWriteTimeShim(path) + }) + ) - ((projectCore.Version, Set.empty), sourceFiles) - ||> Seq.fold (fun (res, sigs) file -> - if file.IsSignatureFile then - lastFile <- file.FileName - res |> addHash file, sigs |> Set.add file.FileName - else - let sigFileName = $"{file.FileName}i" + let snapshot = + FSharpProjectSnapshot.Create( + projectFileName = options.ProjectFileName, + projectId = options.ProjectId, + sourceFiles = (sourceFiles |> List.ofArray), + referencesOnDisk = (referencesOnDisk |> List.ofArray), + otherOptions = (otherOptions |> List.ofArray), + referencedProjects = (referencedProjects |> List.ofArray), + isIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment, + useScriptResolutionRules = options.UseScriptResolutionRules, + loadTime = options.LoadTime, + unresolvedReferences = options.UnresolvedReferences, + originalLoadReferences = options.OriginalLoadReferences, + stamp = options.Stamp + ) - if sigs.Contains sigFileName then - res, sigs |> Set.remove sigFileName - else - lastFile <- file.FileName - res |> addHash file, sigs) - |> fst, - lastFile) + snapshotAccumulator.Add(options, snapshot) - let signatureKey = lazy ( - { new ICacheKey<_, _> with - member this.GetLabel() = this.ToString() - member this.GetKey() = projectCore.Key - member this.GetVersion() = signatureHash.Value |> fst |> Md5Hasher.toString - }) + return snapshotAccumulator[options] + } - let lastFileHash = lazy ( - let lastFile = sourceFiles |> List.last - let sigHash, f = signatureHash.Value - (if f = lastFile.FileName then - sigHash - else - sigHash |> addHash lastFile), - lastFile) + static member GetFileSnapshotFromDisk _ fileName = + async { + let timeStamp = FileSystem.GetLastWriteTimeShim(fileName) + let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() - let lastFileKey = lazy ( - let hash, f = lastFileHash.Value - { new ICacheKey<_, _> with - member this.GetLabel() = this.ToString() - member this.GetKey() = f.FileName, projectCore.Key - member this.GetVersion() = hash |> Md5Hasher.toString - }) + return FSharpFileSnapshot.Create(fileName, timeStamp.Ticks.ToString(), fun () -> Task.FromResult(SourceText.ofString contents)) + } - member _.ProjectSnapshot = projectSnapshot - member _.SourceFiles = sourceFiles + static member FromOptions(options: FSharpProjectOptions) = + FSharpProjectSnapshot.FromOptions(options, FSharpProjectSnapshot.GetFileSnapshotFromDisk) - member this.IndexOf fileName = - this.SourceFiles - |> List.tryFindIndex (fun x -> x.FileName = fileName) - |> Option.defaultWith (fun () -> - failwith (sprintf "Unable to find file %s in project %s" fileName this.ProjectSnapshot.ProjectFileName)) + static member FromOptions(options: FSharpProjectOptions, fileName: string, fileVersion: int, sourceText: ISourceText) = - member private _.With(sourceFiles: FSharpFileSnapshotWithSource list) = FSharpProjectSnapshotWithSources(projectSnapshot, sourceFiles) + let getFileSnapshot _ fName = + if fName = fileName then + async.Return + (FSharpFileSnapshot.Create( + fileName, + $"{fileVersion}{sourceText.GetHashCode().ToString()}", + fun () -> Task.FromResult sourceText)) + else + FSharpProjectSnapshot.GetFileSnapshotFromDisk () fName - member this.UpTo(fileIndex: FileIndex) = this.With sourceFiles[..fileIndex] + FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) - member this.UpTo fileName = this.UpTo(this.IndexOf fileName) - member this.WithoutImplFilesThatHaveSignatures = - let files = - (([], Set.empty), this.SourceFiles) - ||> Seq.fold (fun (res, sigs) file -> - if file.IsSignatureFile then - file :: res, sigs |> Set.add file.FileName - else - let sigFileName = $"{file.FileName}i" +and FSharpProjectSnapshot = FSharpProjectSnapshotBase +and FSharpProjectSnapshotWithSources = FSharpProjectSnapshotBase - if sigs.Contains sigFileName then - res, sigs |> Set.remove sigFileName - else - file :: res, sigs) - |> fst - |> List.rev - this.With files +and ProjectCore( + ProjectFileName: string, + ProjectId: string option, + ReferencesOnDisk: ReferenceOnDisk list, + OtherOptions: string list, + ReferencedProjects: FSharpReferencedProjectSnapshot list, + IsIncompleteTypeCheckEnvironment: bool, + UseScriptResolutionRules: bool, + LoadTime: DateTime, + UnresolvedReferences: FSharpUnresolvedReferencesSet option, + OriginalLoadReferences: (range * string * string) list, + Stamp: int64 option) as self = - member this.WithoutImplFilesThatHaveSignaturesExceptLastOne = - let lastFile = this.SourceFiles |> List.last + let hashForParsing = lazy ( + Md5Hasher.empty + |> Md5Hasher.addString ProjectFileName + |> Md5Hasher.addStrings OtherOptions + |> Md5Hasher.addBool IsIncompleteTypeCheckEnvironment + |> Md5Hasher.addBool UseScriptResolutionRules) - let snapshotWithoutImplFilesThatHaveSignatures = - this.WithoutImplFilesThatHaveSignatures + let fullHash = lazy ( + hashForParsing.Value + |> Md5Hasher.addStrings (ReferencesOnDisk |> Seq.map (fun r -> r.Path)) + |> Md5Hasher.addBytes' ( + ReferencedProjects + |> Seq.map (fun (FSharpReference (_name, p)) -> p.SignatureVersion) + )) - if - lastFile.IsSignatureFile - || snapshotWithoutImplFilesThatHaveSignatures.SourceFiles |> List.last = lastFile - then - this.WithoutImplFilesThatHaveSignatures - else - this.With( snapshotWithoutImplFilesThatHaveSignatures.SourceFiles @ [ lastFile ]) + let commandLineOptions = lazy ( + seq { + for r in ReferencesOnDisk do + $"-r:{r.Path}" - member this.GetDebugVersion() = - { - ProjectSnapshotVersion = this.ProjectSnapshot.WithoutFileVersions.GetDebugVersion() - SourceVersions = this.SourceFiles |> List.map (fun x -> x.SourceHash.ToBuilder().ToArray() |> BitConverter.ToString) + yield! OtherOptions } + |> Seq.toList) - member this.FullKey = fullKey.Value - member this.SignatureKey = signatureKey.Value - member this.LastFileKey = lastFileKey.Value - member this.FileKey(fileName: string) = this.UpTo(fileName).LastFileKey - + let outputFileName = lazy ( + OtherOptions + |> List.tryFind (fun x -> x.StartsWith("-o:")) + |> Option.map (fun x -> x.Substring(3))) -and FSharpProjectSnapshotWithSourcesDebugVersion = - { - ProjectSnapshotVersion: FSharpProjectSnapshotDebugVersion - SourceVersions: string list - } + let key = lazy (ProjectFileName, outputFileName.Value |> Option.defaultValue "") + + member val ProjectDirectory = Path.GetDirectoryName(ProjectFileName) + member _.OutputFileName = outputFileName.Value + member _.Key = key.Value + member _.Version = fullHash.Value + member _.Label = ProjectFileName |> shortPath + member _.VersionForParsing = hashForParsing.Value + + member _.CommandLineOptions = commandLineOptions.Value -and FSharpProjectSnapshotWithSourcesVersion = FSharpProjectSnapshotWithSourcesDebugVersion - -and FSharpProjectSnapshotDebugVersion = - { - ProjectFileName: string - SourceFiles: (string * string) list - ReferencesOnDisk: ReferenceOnDisk list - OtherOptions: string list - ReferencedProjects: FSharpProjectSnapshotDebugVersion list - IsIncompleteTypeCheckEnvironment: bool - UseScriptResolutionRules: bool + member _.ProjectFileName = ProjectFileName + member _.ProjectId = ProjectId + member _.ReferencesOnDisk = ReferencesOnDisk + member _.OtherOptions = OtherOptions + member _.ReferencedProjects = ReferencedProjects + member _.IsIncompleteTypeCheckEnvironment = IsIncompleteTypeCheckEnvironment + member _.UseScriptResolutionRules = UseScriptResolutionRules + member _.LoadTime = LoadTime + member _.UnresolvedReferences = UnresolvedReferences + member _.OriginalLoadReferences = OriginalLoadReferences + member _.Stamp = Stamp + + member _.CacheKeyWith(label, version) = { new ICacheKey<_, _> with + member _.GetLabel() = $"{label} ({self.Label})" + member _.GetKey() = self.Key + member _.GetVersion() = version } -and FSharpProjectSnapshotVersion = string + member _.CacheKeyWith(label, key, version) = { new ICacheKey<_, _> with + member _.GetLabel() = $"{label} ({self.Label})" + member _.GetKey() = key, self.Key + member _.GetVersion() = version + } and [] internal FSharpReferencedProjectSnapshot = internal @@ -546,9 +495,11 @@ and [] internal FSharpReferencedProjectSnapshot = override this.GetHashCode() = this.OutputFile.GetHashCode() -type FSharpProjectSnapshot with - member this.ToOptions() : FSharpProjectOptions = +[] +type Extensions = + [] + static member ToOptions(this: FSharpProjectSnapshot) = { ProjectFileName = this.ProjectFileName ProjectId = this.ProjectId @@ -567,91 +518,3 @@ type FSharpProjectSnapshot with Stamp = this.Stamp } - static member FromOptions(options: FSharpProjectOptions, getFileSnapshot, ?snapshotAccumulator) = - let snapshotAccumulator = defaultArg snapshotAccumulator (Dictionary()) - - async { - - // TODO: check if options is a good key here - if not (snapshotAccumulator.ContainsKey options) then - - let! sourceFiles = options.SourceFiles |> Seq.map (getFileSnapshot options) |> Async.Parallel - - let! referencedProjects = - options.ReferencedProjects - |> Seq.choose (function - | FSharpReferencedProject.FSharpReference(outputName, options) -> - Some( - async { - let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, snapshotAccumulator) - return FSharpReferencedProjectSnapshot.FSharpReference(outputName, snapshot) - } - ) - // TODO: other types - | _ -> None) - |> Async.Sequential - - let referencesOnDisk, otherOptions = - options.OtherOptions - |> Array.partition (fun x -> x.StartsWith("-r:")) - |> map1Of2 ( - Array.map (fun x -> - let path = x.Substring(3) - - { - Path = path - LastModified = FileSystem.GetLastWriteTimeShim(path) - }) - ) - - let snapshot = - FSharpProjectSnapshot.Create( - projectFileName = options.ProjectFileName, - projectId = options.ProjectId, - sourceFiles = (sourceFiles |> List.ofArray), - referencesOnDisk = (referencesOnDisk |> List.ofArray), - otherOptions = (otherOptions |> List.ofArray), - referencedProjects = (referencedProjects |> List.ofArray), - isIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment, - useScriptResolutionRules = options.UseScriptResolutionRules, - loadTime = options.LoadTime, - unresolvedReferences = options.UnresolvedReferences, - originalLoadReferences = options.OriginalLoadReferences, - stamp = options.Stamp - ) - - snapshotAccumulator.Add(options, snapshot) - - return snapshotAccumulator[options] - } - - static member GetFileSnapshotFromDisk _ fileName = - async { - let timeStamp = FileSystem.GetLastWriteTimeShim(fileName) - let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() - - return - { - FileName = fileName - Version = timeStamp.Ticks.ToString() - GetSource = fun () -> Task.FromResult(SourceText.ofString contents) - } - } - - static member FromOptions(options: FSharpProjectOptions) = - FSharpProjectSnapshot.FromOptions(options, FSharpProjectSnapshot.GetFileSnapshotFromDisk) - - static member FromOptions(options: FSharpProjectOptions, fileName: string, fileVersion: int, sourceText: ISourceText) = - - let getFileSnapshot _ fName = - if fName = fileName then - async.Return - { - FileName = fileName - GetSource = fun () -> Task.FromResult sourceText - Version = $"{fileVersion}{sourceText.GetHashCode().ToString()}" - } - else - FSharpProjectSnapshot.GetFileSnapshotFromDisk () fName - - FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) \ No newline at end of file diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 9dd9252ac8e..f68cb41747f 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -45,6 +45,8 @@ open System.Threading open Internal.Utilities.Hashing open FSharp.Compiler.CodeAnalysis.ProjectSnapshot +open System.Runtime.Serialization.Formatters.Binary + type internal FSharpFile = { @@ -135,7 +137,7 @@ type internal DependencyGraphType = [] type internal Extensions = [] - static member Key(fileSnapshots: FSharpFileSnapshotWithSource list, ?extraKeyFlag) = + static member Key<'T when 'T :> IFileSnapshot>(fileSnapshots: 'T list, ?extraKeyFlag) = { new ICacheKey<_, _> with member _.GetLabel() = @@ -154,7 +156,7 @@ type internal Extensions = member _.GetVersion() = Md5Hasher.empty - |> Md5Hasher.addBytes' (fileSnapshots |> Seq.map (fun f -> f.SourceHash.ToBuilder().ToArray())) + |> Md5Hasher.addBytes' (fileSnapshots |> Seq.map (fun f -> f.Version)) |> Md5Hasher.toString } @@ -495,7 +497,7 @@ type internal TransparentCompiler return tcImports, tcInfo } - let getProjectReferences (project: FSharpProjectSnapshot) userOpName = + let getProjectReferences (project: FSharpProjectSnapshotBase<_>) userOpName = [ for r in project.ReferencedProjects do @@ -529,7 +531,7 @@ type internal TransparentCompiler } ] - let ComputeTcConfigBuilder (projectSnapshot: FSharpProjectSnapshot) = + let ComputeTcConfigBuilder (projectSnapshot: FSharpProjectSnapshotBase<_>) = let useSimpleResolutionSwitch = "--simpleresolution" let commandLineArgs = projectSnapshot.CommandLineOptions @@ -787,12 +789,10 @@ type internal TransparentCompiler failwith $"Trying to check a file ({shortPath fileName}) which is not part of the project snapshot. {snapshotFileSummary}" - { - FileName = fileName - Version = (FileSystem.GetLastWriteTimeShim fileName).Ticks.ToString() - GetSource = (fun () -> fileName |> File.ReadAllText |> SourceText.ofString |> Task.FromResult) - }) - + FSharpFileSnapshot.Create( + fileName, + (FileSystem.GetLastWriteTimeShim fileName).Ticks.ToString(), + (fun () -> fileName |> File.ReadAllText |> SourceText.ofString |> Task.FromResult))) { Range = m // TODO: is this used for anything? Source = source @@ -865,13 +865,13 @@ type internal TransparentCompiler let! source = file.Source.GetSource() |> NodeCode.AwaitTask return - { - FileName = file.Source.FileName - Source = source - SourceHash = source.GetChecksum() - IsLastCompiland = file.IsLastCompiland + FSharpFileSnapshotWithSource( + FileName = file.Source.FileName, + Source = source, + SourceHash = source.GetChecksum(), + IsLastCompiland = file.IsLastCompiland, IsExe = file.IsExe - } + ) } let LoadSources (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = @@ -888,11 +888,11 @@ type internal TransparentCompiler |> LoadSource) |> NodeCode.Parallel - return FSharpProjectSnapshotWithSources (projectSnapshot, sources |> Array.toList) + return FSharpProjectSnapshotWithSources (projectSnapshot.ProjectCore, sources |> Array.toList) } - let ComputeParseFile (projectSnapshot: FSharpProjectSnapshot) (file: FSharpFileSnapshotWithSource) = + let ComputeParseFile (projectSnapshot: FSharpProjectSnapshotBase<_>) (tcConfig: TcConfig) (file: FSharpFileSnapshotWithSource) = let key = { new ICacheKey<_, _> with @@ -901,7 +901,7 @@ type internal TransparentCompiler member _.GetVersion() = projectSnapshot.ParsingVersion, - file.SourceHash, + file.StringVersion, // TODO: is there a situation where this is not enough and we need to have them separate? file.IsLastCompiland && file.IsExe } @@ -914,14 +914,9 @@ type internal TransparentCompiler "ComputeParseFile" [| Activity.Tags.fileName, file.FileName |> shortPath - Activity.Tags.version, file.SourceHash.ToBuilder().ToArray() |> Md5Hasher.toString + Activity.Tags.version, file.StringVersion |] - // TODO: might need to deal with exceptions here: - let tcConfigB, _, _ = ComputeTcConfigBuilder projectSnapshot - - let tcConfig = TcConfig.Create(tcConfigB, validate = true) - let diagnosticsLogger = CompilationDiagnosticLogger("Parse", tcConfig.diagnosticsOptions) // Return the disposable object that cleans up @@ -934,9 +929,12 @@ type internal TransparentCompiler let input = ParseOneInputSourceText(tcConfig, lexResourceManager, fileName, flags, diagnosticsLogger, sourceText) + // TODO: Hashing of syntax tree + let inputHash = file.Version + fileParsed.Trigger(fileName, Unchecked.defaultof<_>) - return input, diagnosticsLogger.GetDiagnostics(), sourceText, tcConfig + return FSharpParsedFile(fileName, inputHash, input, diagnosticsLogger.GetDiagnostics()) } ) @@ -1024,7 +1022,7 @@ type internal TransparentCompiler |> Seq.map (fun x -> x.Key, x.Value |> Array.filter (fun node -> not (removeIndexes.Contains node))) |> Graph.make - let removeImplFilesThatHaveSignaturesExceptLastOne (projectSnapshot: FSharpProjectSnapshot) (graph: Graph) = + let removeImplFilesThatHaveSignaturesExceptLastOne (projectSnapshot: FSharpProjectSnapshotBase<_>) (graph: Graph) = let removeIndexes = projectSnapshot.SourceFileNames @@ -1050,40 +1048,44 @@ type internal TransparentCompiler |> Seq.map (fun x -> x.Key, x.Value |> Array.filter (fun node -> not (removeIndexes.Contains node))) |> Graph.make - let ComputeDependencyGraphForFile (tcConfig: TcConfig) (priorSnapshot: FSharpProjectSnapshotWithSources) parsedInputs = + let ComputeDependencyGraphForFile (tcConfig: TcConfig) (priorSnapshot: FSharpProjectSnapshotBase) = let key = priorSnapshot.SourceFiles.Key(DependencyGraphType.File) //let lastFileIndex = (parsedInputs |> Array.length) - 1 //caches.DependencyGraph.Get(key, computeDependencyGraph parsedInputs (Graph.subGraphFor lastFileIndex)) caches.DependencyGraph.Get( key, - computeDependencyGraph tcConfig parsedInputs (removeImplFilesThatHaveSignaturesExceptLastOne priorSnapshot.ProjectSnapshot) + computeDependencyGraph tcConfig (priorSnapshot.SourceFiles |> Seq.map (fun f -> f.ParsedInput)) (removeImplFilesThatHaveSignaturesExceptLastOne priorSnapshot) ) - let ComputeDependencyGraphForProject (tcConfig: TcConfig) (projectSnapshot: FSharpProjectSnapshotWithSources) parsedInputs = + let ComputeDependencyGraphForProject (tcConfig: TcConfig) (projectSnapshot: FSharpProjectSnapshotBase) = + let key = projectSnapshot.SourceFiles.Key(DependencyGraphType.Project) //caches.DependencyGraph.Get(key, computeDependencyGraph parsedInputs (removeImplFilesThatHaveSignatures projectSnapshot)) - caches.DependencyGraph.Get(key, computeDependencyGraph tcConfig parsedInputs id) + caches.DependencyGraph.Get(key, computeDependencyGraph tcConfig (projectSnapshot.SourceFiles |> Seq.map (fun f -> f.ParsedInput)) id) let ComputeTcIntermediate - (projectSnapshot: FSharpProjectSnapshotWithSources) + (projectSnapshot: FSharpProjectSnapshotBase) (dependencyGraph: Graph) + (index: FileIndex) (nodeToCheck: NodeToTypeCheck) - (parsedInput: ParsedInput, parseErrors) bootstrapInfo (prevTcInfo: TcInfo) = ignore dependencyGraph - let key = projectSnapshot.FileKey parsedInput.FileName + + let key = projectSnapshot.FileKey index let _label, _k, _version = key.GetLabel(), key.GetKey(), key.GetVersion() caches.TcIntermediate.Get( key, node { - let input = parsedInput - let fileName = input.FileName + let file = projectSnapshot.SourceFiles[index] + + let input = file.ParsedInput + let fileName = file.FileName use _ = Activity.start @@ -1119,7 +1121,7 @@ type internal TransparentCompiler let sink = TcResultsSinkImpl(tcGlobals) - let hadParseErrors = not (Array.isEmpty parseErrors) + let hadParseErrors = not (Array.isEmpty file.ParseErrors) let input, moduleNamesDict = DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input @@ -1147,7 +1149,7 @@ type internal TransparentCompiler //fileChecked.Trigger fileName let newErrors = - Array.append parseErrors (capturingDiagnosticsLogger.Diagnostics |> List.toArray) + Array.append file.ParseErrors (capturingDiagnosticsLogger.Diagnostics |> List.toArray) fileChecked.Trigger(fileName, Unchecked.defaultof<_>) @@ -1167,7 +1169,6 @@ type internal TransparentCompiler let processGraphNode projectSnapshot bootstrapInfo - (parsedInputs: _ array) dependencyFiles collectSinks (fileNode: NodeToTypeCheck) @@ -1175,13 +1176,11 @@ type internal TransparentCompiler = // TODO: should this be node? async { - match fileNode with | NodeToTypeCheck.PhysicalFile index -> - let input, parseErrors, _, _ = parsedInputs[index] let! tcIntermediate = - ComputeTcIntermediate projectSnapshot dependencyFiles fileNode (input, parseErrors) bootstrapInfo tcInfo + ComputeTcIntermediate projectSnapshot dependencyFiles index fileNode bootstrapInfo tcInfo |> Async.AwaitNodeCode let (Finisher(node = node; finisher = finisher)) = tcIntermediate.finisher @@ -1245,7 +1244,7 @@ type internal TransparentCompiler then failwith $"Oops!!!" - let parsedInput, _parseErrors, _, _ = parsedInputs[index] + let parsedInput = projectSnapshot.SourceFiles[index].ParsedInput let prefixPathOpt = None // Retrieve the type-checked signature information and add it to the TcEnvFromImpls. let partialResult, tcState = @@ -1279,6 +1278,16 @@ type internal TransparentCompiler } + let parseSourceFiles (projectSnapshot: FSharpProjectSnapshotWithSources) tcConfig = + node { + let! parsedInputs = + projectSnapshot.SourceFiles + |> Seq.map (ComputeParseFile projectSnapshot tcConfig) + |> NodeCode.Parallel + + return FSharpProjectSnapshotBase<_>(projectSnapshot.ProjectCore, parsedInputs |> Array.toList) + } + // Type check file and all its dependencies let ComputeTcLastFile (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = let fileName = projectSnapshot.SourceFiles |> List.last |> (fun f -> f.FileName) @@ -1291,19 +1300,15 @@ type internal TransparentCompiler use _ = Activity.start "ComputeTcLastFile" [| Activity.Tags.fileName, file.FileName |> Path.GetFileName |] - let! parsedInputs = - projectSnapshot.SourceFiles - |> Seq.map (ComputeParseFile projectSnapshot.ProjectSnapshot) - |> NodeCode.Parallel + let! projectSnapshot = parseSourceFiles projectSnapshot bootstrapInfo.TcConfig let! graph, dependencyFiles = - ComputeDependencyGraphForFile bootstrapInfo.TcConfig projectSnapshot (parsedInputs |> Array.map p14) - //ComputeDependencyGraphForProject priorSnapshot (parsedInputs |> Array.map p13) + ComputeDependencyGraphForFile bootstrapInfo.TcConfig projectSnapshot let! results, tcInfo = processTypeCheckingGraph graph - (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles false) + (processGraphNode projectSnapshot bootstrapInfo dependencyFiles false) bootstrapInfo.InitialTcInfo |> NodeCode.AwaitAsync @@ -1313,16 +1318,16 @@ type internal TransparentCompiler } ) - let getParseResult (projectSnapshot: FSharpProjectSnapshot) creationDiags file = + let getParseResult (projectSnapshot: FSharpProjectSnapshot) creationDiags file (tcConfig: TcConfig) = node { - let! parseTree, parseDiagnostics, sourceText, tcConfig = ComputeParseFile projectSnapshot file + let! parsedFile = ComputeParseFile projectSnapshot tcConfig file let parseDiagnostics = DiagnosticHelpers.CreateDiagnostics( tcConfig.diagnosticsOptions, false, file.FileName, - parseDiagnostics, + parsedFile.ParseErrors, suggestNamesForErrors, tcConfig.flatErrors, None @@ -1333,12 +1338,11 @@ type internal TransparentCompiler return FSharpParseFileResults( diagnostics = diagnostics, - input = parseTree, + input = parsedFile.ParsedInput, parseHadErrors = (parseDiagnostics.Length > 0), // TODO: check if we really need this in parse results dependencyFiles = [||] - ), - sourceText + ) } let emptyParseResult fileName diagnostics = @@ -1362,7 +1366,7 @@ type internal TransparentCompiler let! snapshotWithSources = LoadSources bootstrapInfo priorSnapshot let file = snapshotWithSources.SourceFiles |> List.last - let! parseResults, _sourceText = getParseResult projectSnapshot creationDiags file + let! parseResults = getParseResult projectSnapshot creationDiags file bootstrapInfo.TcConfig let! result, tcInfo = ComputeTcLastFile bootstrapInfo snapshotWithSources @@ -1452,21 +1456,18 @@ type internal TransparentCompiler Activity.start "ComputeParseAndCheckAllFilesInProject" [| - Activity.Tags.project, projectSnapshot.ProjectSnapshot.ProjectFileName |> Path.GetFileName + Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] - let! parsedInputs = - projectSnapshot.SourceFiles - |> Seq.map (ComputeParseFile projectSnapshot.ProjectSnapshot) - |> NodeCode.Parallel + let! projectSnapshot = parseSourceFiles projectSnapshot bootstrapInfo.TcConfig let! graph, dependencyFiles = - ComputeDependencyGraphForProject bootstrapInfo.TcConfig projectSnapshot (parsedInputs |> Array.map p14) + ComputeDependencyGraphForProject bootstrapInfo.TcConfig projectSnapshot return! processTypeCheckingGraph graph - (processGraphNode projectSnapshot bootstrapInfo parsedInputs dependencyFiles true) + (processGraphNode projectSnapshot bootstrapInfo dependencyFiles true) bootstrapInfo.InitialTcInfo |> NodeCode.AwaitAsync } @@ -1779,7 +1780,7 @@ type internal TransparentCompiler } let! file = file |> LoadSource - let! parseResult, _ = getParseResult projectSnapshot Seq.empty file + let! parseResult = getParseResult projectSnapshot Seq.empty file tcConfig return parseResult } diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 02efbea54b7..8829ee0ba19 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -19,6 +19,7 @@ $(DefineConstants);DEBUG true + true diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs index f57c85e6d0e..90e28cc63ac 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/ProjectSnapshot.fs @@ -5,98 +5,100 @@ open System open FSharp.Compiler.CodeAnalysis.ProjectSnapshot -[] -let WithoutImplFilesThatHaveSignatures () = - - let snapshot = FSharpProjectSnapshot.Create( - projectFileName = "Dummy.fsproj", - projectId = None, - sourceFiles = [ - { FileName = "A.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } - { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } - { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } - { FileName = "C.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } - { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } - ], - referencesOnDisk = [], - otherOptions = [], - referencedProjects = [], - isIncompleteTypeCheckEnvironment = true, - useScriptResolutionRules = false, - loadTime = DateTime(1234, 5, 6), - unresolvedReferences = None, - originalLoadReferences = [], - stamp = None - ) - - let result = snapshot.WithoutImplFilesThatHaveSignatures - - let expected = [| "A.fsi"; "B.fs"; "C.fsi" |] - - Assert.Equal(expected, result.SourceFileNames |> List.toArray) - - Assert.Equal(result.FullVersion, snapshot.SignatureVersion) - -[] -let WithoutImplFilesThatHaveSignaturesExceptLastOne () = - - let snapshot = FSharpProjectSnapshot.Create( - projectFileName = "Dummy.fsproj", - projectId = None, - sourceFiles = [ - { FileName = "A.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } - { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } - { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } - { FileName = "C.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } - { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } - ], - referencesOnDisk = [], - otherOptions = [], - referencedProjects = [], - isIncompleteTypeCheckEnvironment = true, - useScriptResolutionRules = false, - loadTime = DateTime(1234, 5, 6), - unresolvedReferences = None, - originalLoadReferences = [], - stamp = None - ) - - let result = snapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne - - let expected = [| "A.fsi"; "B.fs"; "C.fsi"; "C.fs" |] - - Assert.Equal(expected, result.SourceFileNames |> List.toArray) - - Assert.Equal(result.FullVersion, snapshot.LastFileVersion) - - -[] -let WithoutImplFilesThatHaveSignaturesExceptLastOne_2 () = - - let snapshot = FSharpProjectSnapshot.Create( - projectFileName = "Dummy.fsproj", - projectId = None, - sourceFiles = [ - { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } - { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } - { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } - ], - referencesOnDisk = [], - otherOptions = [], - referencedProjects = [], - isIncompleteTypeCheckEnvironment = true, - useScriptResolutionRules = false, - loadTime = DateTime(1234, 5, 6), - unresolvedReferences = None, - originalLoadReferences = [], - stamp = None - ) - - let result = snapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne - - let expected = [| "A.fs"; "B.fs"; "C.fs" |] - - Assert.Equal(expected, result.SourceFileNames |> List.toArray) - - Assert.Equal(result.FullVersion, snapshot.LastFileVersion) +// TODO: restore tests + +//[] +//let WithoutImplFilesThatHaveSignatures () = + +// let snapshot = FSharpProjectSnapshot.Create( +// projectFileName = "Dummy.fsproj", +// projectId = None, +// sourceFiles = [ +// { FileName = "A.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "C.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// ], +// referencesOnDisk = [], +// otherOptions = [], +// referencedProjects = [], +// isIncompleteTypeCheckEnvironment = true, +// useScriptResolutionRules = false, +// loadTime = DateTime(1234, 5, 6), +// unresolvedReferences = None, +// originalLoadReferences = [], +// stamp = None +// ) + +// let result = snapshot.WithoutImplFilesThatHaveSignatures + +// let expected = [| "A.fsi"; "B.fs"; "C.fsi" |] + +// Assert.Equal(expected, result.SourceFileNames |> List.toArray) + +// Assert.Equal(result.FullVersion, snapshot.SignatureVersion) + +//[] +//let WithoutImplFilesThatHaveSignaturesExceptLastOne () = + +// let snapshot = FSharpProjectSnapshot.Create( +// projectFileName = "Dummy.fsproj", +// projectId = None, +// sourceFiles = [ +// { FileName = "A.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "C.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// ], +// referencesOnDisk = [], +// otherOptions = [], +// referencedProjects = [], +// isIncompleteTypeCheckEnvironment = true, +// useScriptResolutionRules = false, +// loadTime = DateTime(1234, 5, 6), +// unresolvedReferences = None, +// originalLoadReferences = [], +// stamp = None +// ) + +// let result = snapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne + +// let expected = [| "A.fsi"; "B.fs"; "C.fsi"; "C.fs" |] + +// Assert.Equal(expected, result.SourceFileNames |> List.toArray) + +// Assert.Equal(result.FullVersion, snapshot.LastFileVersion) + + +//[] +//let WithoutImplFilesThatHaveSignaturesExceptLastOne_2 () = + +// let snapshot = FSharpProjectSnapshot.Create( +// projectFileName = "Dummy.fsproj", +// projectId = None, +// sourceFiles = [ +// { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> } +// ], +// referencesOnDisk = [], +// otherOptions = [], +// referencedProjects = [], +// isIncompleteTypeCheckEnvironment = true, +// useScriptResolutionRules = false, +// loadTime = DateTime(1234, 5, 6), +// unresolvedReferences = None, +// originalLoadReferences = [], +// stamp = None +// ) + +// let result = snapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne + +// let expected = [| "A.fs"; "B.fs"; "C.fs" |] + +// Assert.Equal(expected, result.SourceFileNames |> List.toArray) + +// Assert.Equal(result.FullVersion, snapshot.LastFileVersion) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 4b3e88a19b0..ba75f2c7fe1 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -12,6 +12,7 @@ open FSharp.Compiler.Diagnostics open Xunit open FSharp.Test.ProjectGeneration +open FSharp.Test.ProjectGeneration.Helpers open System.IO open Microsoft.CodeAnalysis open System @@ -861,3 +862,34 @@ let GiraffeFuzzing signatureFiles = fuzzingTest seed testsProject + +module ParsedInputHashing = + + let source = """ + +type T = { A: int; B: string } + +module Stuff = + + // Some comment + let f x = x + 75 +""" + + let getParseResult source = + let fileName, snapshot, checker = singleFileChecker source + checker.ParseFile(fileName, snapshot) |> Async.RunSynchronously + + //[] + let ``Hash stays the same when whitespace changes`` () = + + //let parseResult = getParseResult source + + //let hash = parseResult.ParseTree |> parsedInputHash |> BitConverter.ToString + + //let parseResult2 = getParseResult (source + "\n \n") + + //let hash2 = parseResult2.ParseTree |> parsedInputHash |> BitConverter.ToString + + //Assert.Equal(hash, hash2) + + () \ No newline at end of file diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index a8d338fe0c2..1c21a60b93f 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -896,15 +896,15 @@ Updated automatically, please check diffs in your pull request, changes must be if useTransparentCompiler then let getFileSnapshot _ fileName = async.Return - { - FileName = fileName - Version = "1" + (FSharpFileSnapshot( + FileName = fileName, + Version = "1", GetSource = fun () -> task { match! getSourceText fileName with | Some source -> return source | None -> return failwith $"couldn't get source for {fileName}" } - } + )) let snapshot = FSharpProjectSnapshot.FromOptions(projectOptions, getFileSnapshot) |> Async.RunSynchronously diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 22b7f064934..69f882c4514 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -559,12 +559,12 @@ module ProjectOperations = use md5 = System.Security.Cryptography.MD5.Create() let inputBytes = Encoding.UTF8.GetBytes(source.ToString()) let hash = md5.ComputeHash(inputBytes) |> Array.map (fun b -> b.ToString("X2")) |> String.concat "" - - return { - FileName = filePath - Version = hash + + return FSharpFileSnapshot( + FileName = filePath, + Version = hash, GetSource = fun () -> source |> Task.FromResult - } + ) } let checkFileWithTransparentCompiler fileId (project: SyntheticProject) (checker: FSharpChecker) = @@ -747,9 +747,10 @@ module Helpers = let fileName = "test.fs" let getSource _ fileName = - { FileName = fileName - Version = "1" - GetSource = fun () -> source |> SourceText.ofString |> Task.FromResult } + FSharpFileSnapshot( + FileName = fileName, + Version = "1", + GetSource = fun () -> source |> SourceText.ofString |> Task.FromResult ) |> async.Return let checker = FSharpChecker.Create( diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 3f3e66e5803..87aa9641235 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -173,11 +173,11 @@ module private CheckerExtensions = async.Return(version.ToString(), getSource) return - { - FileName = path - Version = version + FSharpFileSnapshot( + FileName = path, + Version = version, GetSource = getSource - } + ) } let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) From 618fbf682fab84b66ddbf92d4426977a6f01c32f Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 6 Dec 2023 12:23:44 +0100 Subject: [PATCH 167/222] repro --- src/Compiler/Facilities/AsyncMemoize.fs | 53 ++++++++++++++++--- src/Compiler/Service/FSharpProjectSnapshot.fs | 6 +-- src/Compiler/Service/TransparentCompiler.fs | 14 ++--- .../CompilerService/AsyncMemoize.fs | 3 +- .../FSharpChecker/TransparentCompiler.fs | 16 ++++++ .../ProjectGeneration.fs | 26 +++++++++ 6 files changed, 101 insertions(+), 17 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 75914afb783..59ed21314c9 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -10,6 +10,7 @@ open System.Threading.Tasks open FSharp.Compiler.BuildGraph open FSharp.Compiler.Diagnostics open FSharp.Compiler.DiagnosticsLogger +open System.Runtime.CompilerServices [] module internal Utils = @@ -31,7 +32,7 @@ module internal Utils = type internal StateUpdate<'TValue> = | CancelRequest | OriginatorCanceled - | JobCompleted of 'TValue * (PhasedDiagnostic * FSharpDiagnosticSeverity) list + | JobCompleted of 'TValue * (PhasedDiagnostic * FSharpDiagnosticSeverity) list | JobFailed of exn * (PhasedDiagnostic * FSharpDiagnosticSeverity) list type internal MemoizeReply<'TValue> = @@ -46,6 +47,8 @@ type internal MemoizeRequest<'TValue> = type internal Job<'TValue> = | Running of TaskCompletionSource<'TValue> * CancellationTokenSource * NodeCode<'TValue> * DateTime * ResizeArray | Completed of 'TValue * (PhasedDiagnostic * FSharpDiagnosticSeverity) list + | Canceled of DateTime + | Failed of DateTime * exn // TODO: probably we don't need to keep this member this.DebuggerDisplay = match this with @@ -58,6 +61,8 @@ type internal Job<'TValue> = $"Running since {ts.ToShortTimeString()}{cancellation}" | Completed(value, diags) -> $"Completed {value}" + (if diags.Length > 0 then $" ({diags.Length})" else "") + | Canceled _ -> "Canceled" + | Failed(_, ex) -> $"Failed {ex}" type internal JobEvent = | Started @@ -74,6 +79,17 @@ type internal ICacheKey<'TKey, 'TVersion> = abstract member GetVersion: unit -> 'TVersion abstract member GetLabel: unit -> string +[] +type Extensions = + + [] + static member internal WithExtraVersion(cacheKey: ICacheKey<_, _>, extraVersion) = + { new ICacheKey<_, _> with + member _.GetLabel() = cacheKey.GetLabel() + member _.GetKey() = cacheKey.GetKey() + member _.GetVersion() = cacheKey.GetVersion(), extraVersion + } + type private KeyData<'TKey, 'TVersion> = { Label: string @@ -146,6 +162,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T requiredToKeep = (function | Running _ -> true + | Job.Canceled at when at > DateTime.Now.AddMinutes -5.0 -> true + | Job.Failed(at, _) when at > DateTime.Now.AddMinutes -5.0 -> true | _ -> false), event = (function @@ -225,7 +243,9 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T Existing tcs.Task - | GetOrCompute(computation, ct), None -> + | GetOrCompute(computation, ct), None + | GetOrCompute(computation, ct), Some(Job.Canceled _) + | GetOrCompute(computation, ct), Some(Job.Failed _) -> Interlocked.Increment &started |> ignore incrRequestCount key @@ -283,7 +303,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cancelRegistration key cts.Cancel() tcs.TrySetCanceled() |> ignore - cache.Remove(key.Key, key.Version) + // Remember the job in case it completes after cancellation + cache.Set(key.Key, key.Version, key.Label, Job.Canceled DateTime.Now) requestCounts.Remove key |> ignore log (Canceled, key) Interlocked.Increment &canceled |> ignore @@ -330,7 +351,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cancelRegistration key cts.Cancel() tcs.TrySetCanceled() |> ignore - cache.Remove(key.Key, key.Version) + // Remember the job in case it completes after cancellation + cache.Set(key.Key, key.Version, key.Label, Job.Canceled DateTime.Now) requestCounts.Remove key |> ignore log (Canceled, key) Interlocked.Increment &canceled |> ignore @@ -340,12 +362,16 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T // Probably in some cases cancellation can be fired off even after we just unregistered it | CancelRequest, None | CancelRequest, Some(Completed _) + | CancelRequest, Some(Job.Canceled _) + | CancelRequest, Some(Job.Failed _) | OriginatorCanceled, None - | OriginatorCanceled, Some(Completed _) -> () + | OriginatorCanceled, Some(Completed _) + | OriginatorCanceled, Some(Job.Canceled _) + | OriginatorCanceled, Some(Job.Failed _) -> () | JobFailed(ex, diags), Some(Running(tcs, _cts, _c, _ts, loggers)) -> cancelRegistration key - cache.Remove(key.Key, key.Version) + cache.Set(key.Key, key.Version, key.Label, Job.Failed(DateTime.Now, ex)) requestCounts.Remove key |> ignore log (Failed, key) Interlocked.Increment &failed |> ignore @@ -376,6 +402,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T if tcs.TrySetResult result = false then internalError key.Label "Invalid state: Completed job already completed" + // Sometimes job can be canceled but it still manages to complete (or fail) + | JobFailed _, Some (Job.Canceled _) + | JobCompleted _, Some (Job.Canceled _) -> () + // Job can't be evicted from cache while it's running because then subsequent requesters would be waiting forever | JobFailed _, None -> internalError key.Label "Invalid state: Running job missing in cache (failed)" @@ -386,6 +416,12 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | JobCompleted(_result, _diags), Some(Completed(_job, _diags2)) -> internalError key.Label "Invalid state: Double-Completed job" + + | JobFailed(ex, _diags), Some(Job.Failed(_, ex2)) -> + internalError key.Label $"Invalid state: Double-Failed job \n%A{ex} \n%A{ex2}" + + | JobCompleted(_result, _diags), Some(Job.Failed(_, ex2)) -> + internalError key.Label $"Invalid state: Completed Failed job \n%A{ex2}" }) } @@ -478,7 +514,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T cache.GetValues() |> Seq.countBy (function | _, _, Running _ -> "Running" - | _, _, Completed _ -> "Completed") + | _, _, Completed _ -> "Completed" + | _, _, Job.Canceled _ -> "Canceled" + | _, _, Job.Failed _ -> "Failed" + ) |> Map let running = diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 4ddc584258b..2cec4b5f2a2 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -147,7 +147,7 @@ type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot> (projectCore: Projec let noFileVersionsKey = lazy ( { new ICacheKey<_, _> with - member this.GetLabel() = this.ToString() + member this.GetLabel() = projectCore.Label member this.GetKey() = projectCore.Key member this.GetVersion() = noFileVersionsHash.Value |> Md5Hasher.toString }) @@ -158,7 +158,7 @@ type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot> (projectCore: Projec let fullKey = lazy ( { new ICacheKey<_, _> with - member this.GetLabel() = this.ToString() + member this.GetLabel() = projectCore.Label member this.GetKey() = projectCore.Key member this.GetVersion() = fullHash.Value |> Md5Hasher.toString }) @@ -184,7 +184,7 @@ type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot> (projectCore: Projec let lastFileKey = lazy ( let hash, f = lastFileHash.Value { new ICacheKey<_, _> with - member _.GetLabel() = $"{projectCore.Label} (last file)" + member _.GetLabel() = $"{f.FileName} ({projectCore.Label})" member _.GetKey() = f.FileName, projectCore.Key member _.GetVersion() = hash |> Md5Hasher.toString }) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index f68cb41747f..d0f7b2f2a74 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1074,8 +1074,8 @@ type internal TransparentCompiler ignore dependencyGraph + let key = projectSnapshot.FileKey(index) - let key = projectSnapshot.FileKey index let _label, _k, _version = key.GetLabel(), key.GetKey(), key.GetVersion() caches.TcIntermediate.Get( @@ -1193,12 +1193,14 @@ type internal TransparentCompiler if tcInfo.stateContainsNodes |> Set.contains fileNode then failwith $"Oops!" - if - tcInfo.stateContainsNodes - |> Set.contains (NodeToTypeCheck.ArtificialImplFile(index - 1)) - then + //if + // tcInfo.stateContainsNodes + // Signature files don't have to be right above the impl file... if we need this check then + // we need to do it differently + // |> Set.contains (NodeToTypeCheck.ArtificialImplFile(index - 1)) + //then // TODO: this can actually happen, probably related to adding new files or moving files around - failwith $"Oops???" + //failwith $"Oops???" let partialResult, tcState = finisher tcInfo.tcState diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index c1571387fe8..ff6ebfecbc3 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -526,4 +526,5 @@ let ``We get diagnostics from the job that failed`` () = [] let ``What if requestor cancels and their diagnosticsLogger gets disposed?``() = - failwith "TODO" \ No newline at end of file + failwith "TODO" + () \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index ba75f2c7fe1..199687756e6 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -863,6 +863,22 @@ let GiraffeFuzzing signatureFiles = fuzzingTest seed testsProject +[] +[] +[] +let ``File moving test`` signatureFiles = + let giraffe = if signatureFiles then "giraffe-signatures" else "Giraffe" + let giraffeDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ giraffe ++ "src" ++ "Giraffe" + let giraffeProject = SyntheticProject.CreateFromRealProject giraffeDir + let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } + + giraffeProject.Workflow { + checkFile "Json" expectOk + moveFile "Json" 1 Down + checkFile "Json" expectOk + } + + module ParsedInputHashing = let source = """ diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 69f882c4514..feb26fcc928 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -822,6 +822,8 @@ let SaveAndCheckProject project checker = Cursor = None } } +type MoveFileDirection = Up | Down + type ProjectWorkflowBuilder ( initialProject: SyntheticProject, @@ -1046,6 +1048,30 @@ type ProjectWorkflowBuilder return { ctx with Signatures = ctx.Signatures.Add(fileId, newSignature) } } + [] + member this.MoveFile(workflow: Async, fileId: string, count, direction: MoveFileDirection) = + + workflow + |> mapProject (fun project -> + let index = + project.SourceFiles + |> List.tryFindIndex (fun f -> f.Id = fileId) + |> Option.defaultWith (fun () -> failwith $"File {fileId} not found") + + let dir = if direction = Up then -1 else 1 + let newIndex = index + count * dir + + if newIndex < 0 || newIndex > project.SourceFiles.Length - 1 then + failwith $"Cannot move file {fileId} {count} times {direction} as it would be out of bounds" + + let file = project.SourceFiles.[index] + let newFiles = + project.SourceFiles + |> List.filter (fun f -> f.Id <> fileId) + |> List.insertAt newIndex file + + { project with SourceFiles = newFiles }) + /// Find a symbol using the provided range, mimicking placing a cursor on it in IDE scenarios [] member this.PlaceCursor(workflow: Async, fileId, line, colAtEndOfNames, fullLine, symbolNames) = From bfadbd62c2edadaf6e7766c8f67daf23f36d33a3 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 6 Dec 2023 18:27:41 +0100 Subject: [PATCH 168/222] Moved stuff around --- src/Compiler/Service/FSharpProjectSnapshot.fs | 18 +- src/Compiler/Service/TransparentCompiler.fs | 266 +++++++++--------- .../FSharpChecker/TransparentCompiler.fs | 1 + 3 files changed, 146 insertions(+), 139 deletions(-) diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 2cec4b5f2a2..369d97d62e9 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -68,6 +68,12 @@ type FSharpFileSnapshot( static member Create(fileName: string, version: string, getSource: unit -> Task) = FSharpFileSnapshot(fileName, version, getSource) + static member CreateFromFileSystem(fileName: string) = + FSharpFileSnapshot( + fileName, + FileSystem.GetLastWriteTimeShim(fileName).Ticks.ToString(), + fun () -> fileName |> File.ReadAllText |> SourceText.ofString |> Task.FromResult) + member public _.FileName = FileName member _.Version = Version member _.GetSource() = GetSource() @@ -410,6 +416,8 @@ and ProjectCore( |> Seq.map (fun (FSharpReference (_name, p)) -> p.SignatureVersion) )) + let fullHashString = lazy (fullHash.Value |> Md5Hasher.toString) + let commandLineOptions = lazy ( seq { for r in ReferencesOnDisk do @@ -425,6 +433,10 @@ and ProjectCore( |> Option.map (fun x -> x.Substring(3))) let key = lazy (ProjectFileName, outputFileName.Value |> Option.defaultValue "") + let cacheKey = lazy ({ new ICacheKey<_, _> with + member _.GetLabel() = self.Label + member _.GetKey() = self.Key + member _.GetVersion() = fullHashString.Value }) member val ProjectDirectory = Path.GetDirectoryName(ProjectFileName) member _.OutputFileName = outputFileName.Value @@ -450,15 +462,17 @@ and ProjectCore( member _.CacheKeyWith(label, version) = { new ICacheKey<_, _> with member _.GetLabel() = $"{label} ({self.Label})" member _.GetKey() = self.Key - member _.GetVersion() = version + member _.GetVersion() = fullHashString.Value, version } member _.CacheKeyWith(label, key, version) = { new ICacheKey<_, _> with member _.GetLabel() = $"{label} ({self.Label})" member _.GetKey() = key, self.Key - member _.GetVersion() = version + member _.GetVersion() = fullHashString.Value, version } + member _.CacheKey = cacheKey.Value + and [] internal FSharpReferencedProjectSnapshot = internal | FSharpReference of projectOutputFile: string * options: FSharpProjectSnapshot diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index d0f7b2f2a74..adcfa65404c 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -48,13 +48,13 @@ open FSharp.Compiler.CodeAnalysis.ProjectSnapshot open System.Runtime.Serialization.Formatters.Binary -type internal FSharpFile = - { - Range: range - Source: FSharpFileSnapshot - IsLastCompiland: bool - IsExe: bool - } +//type internal FSharpFile = +// { +// Range: range +// Source: FSharpFileSnapshot +// IsLastCompiland: bool +// IsExe: bool +// } /// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. [] @@ -112,18 +112,25 @@ type internal BootstrapInfo = TcImports: TcImports TcGlobals: TcGlobals InitialTcInfo: TcInfo - SourceFiles: FSharpFile list + + // TODO: Figure out how these work and if they need to be added to the snapshot... + LoadedSources: (range * FSharpFileSnapshot) list + + // TODO: Might be a bit more complicated if we want to support adding files to the project via OtherOptions + // ExtraSourceFilesAfter: FSharpFileSnapshot list + LoadClosure: LoadClosure option + LastFileName: string } - member this.GetFile fileName = - this.SourceFiles - |> List.tryFind (fun f -> f.Source.FileName = fileName) - |> Option.defaultWith (fun _ -> - failwith ( - $"File {fileName} not found in project snapshot. Files in project: \n\n" - + (this.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n") - )) + //member this.GetFile fileName = + // this.SourceFiles + // |> List.tryFind (fun f -> f.Source.FileName = fileName) + // |> Option.defaultWith (fun _ -> + // failwith ( + // $"File {fileName} not found in project snapshot. Files in project: \n\n" + // + (this.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n") + // )) type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string @@ -616,11 +623,11 @@ type internal TransparentCompiler tcConfigB, sourceFilesNew, loadClosureOpt - /// Bootstrap info that does not depend on contents of the files - let ComputeBootstrapInfoStatic (projectSnapshot: FSharpProjectSnapshot) = + /// Bootstrap info that does not depend source files + let ComputeBootstrapInfoStatic (projectSnapshot: ProjectCore, tcConfig: TcConfig, assemblyName: string, loadClosureOpt) = caches.BootstrapInfoStatic.Get( - projectSnapshot.NoFileVersionsKey, + projectSnapshot.CacheKeyWith("BootstrapInfoStatic", assemblyName), node { use _ = Activity.start @@ -630,47 +637,7 @@ type internal TransparentCompiler "references", projectSnapshot.ReferencedProjects.Length.ToString() |] - let tcConfigB, sourceFiles, loadClosureOpt = ComputeTcConfigBuilder projectSnapshot - // If this is a builder for a script, re-apply the settings inferred from the - // script and its load closure to the configuration. - // - // NOTE: it would probably be cleaner and more accurate to re-run the load closure at this point. - let setupConfigFromLoadClosure () = - match loadClosureOpt with - | Some loadClosure -> - let dllReferences = - [ - for reference in tcConfigB.referencedDLLs do - // If there's (one or more) resolutions of closure references then yield them all - match - loadClosure.References - |> List.tryFind (fun (resolved, _) -> resolved = reference.Text) - with - | Some(resolved, closureReferences) -> - for closureReference in closureReferences do - yield AssemblyReference(closureReference.originalReference.Range, resolved, None) - | None -> yield reference - ] - - tcConfigB.referencedDLLs <- [] - - tcConfigB.primaryAssembly <- - (if loadClosure.UseDesktopFramework then - PrimaryAssembly.Mscorlib - else - PrimaryAssembly.System_Runtime) - // Add one by one to remove duplicates - dllReferences - |> List.iter (fun dllReference -> tcConfigB.AddReferencedAssemblyByPath(dllReference.Range, dllReference.Text)) - - tcConfigB.knownUnresolvedReferences <- loadClosure.UnresolvedReferences - | None -> () - - setupConfigFromLoadClosure () - - let tcConfig = TcConfig.Create(tcConfigB, validate = true) - let outfile, _, assemblyName = tcConfigB.DecideNames sourceFiles // Resolve assemblies and create the framework TcImports. This caches a level of "system" references. No type providers are // included in these references. @@ -692,16 +659,6 @@ type internal TransparentCompiler let importsInvalidatedByTypeProvider = Event() - // Check for the existence of loaded sources and prepend them to the sources list if present. - let sourceFiles = - tcConfig.GetAvailableLoadedSources() - @ (sourceFiles |> List.map (fun s -> rangeStartup, s)) - - // Mark up the source files with an indicator flag indicating if they are the last source file in the project - let sourceFiles = - let flags, isExe = tcConfig.ComputeCanContainEntryPoint(sourceFiles |> List.map snd) - ((sourceFiles, flags) ||> List.map2 (fun (m, nm) flag -> (m, nm, (flag, isExe)))) - let basicDependencies = [ for UnresolvedAssemblyReference(referenceText, _) in unresolvedReferences do @@ -743,62 +700,105 @@ type internal TransparentCompiler ) return - assemblyName, - outfile, - sourceFiles, - tcConfig, tcImports, tcGlobals, initialTcInfo, - loadClosureOpt, importsInvalidatedByTypeProvider } ) let computeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = node { - let! assemblyName, - outFile, - sourceFiles, - tcConfig, - tcImports, + + let tcConfigB, sourceFiles, loadClosureOpt = ComputeTcConfigBuilder projectSnapshot + + // If this is a builder for a script, re-apply the settings inferred from the + // script and its load closure to the configuration. + // + // NOTE: it would probably be cleaner and more accurate to re-run the load closure at this point. + let setupConfigFromLoadClosure () = + match loadClosureOpt with + | Some loadClosure -> + let dllReferences = + [ + for reference in tcConfigB.referencedDLLs do + // If there's (one or more) resolutions of closure references then yield them all + match + loadClosure.References + |> List.tryFind (fun (resolved, _) -> resolved = reference.Text) + with + | Some(resolved, closureReferences) -> + for closureReference in closureReferences do + yield AssemblyReference(closureReference.originalReference.Range, resolved, None) + | None -> yield reference + ] + + tcConfigB.referencedDLLs <- [] + + tcConfigB.primaryAssembly <- + (if loadClosure.UseDesktopFramework then + PrimaryAssembly.Mscorlib + else + PrimaryAssembly.System_Runtime) + // Add one by one to remove duplicates + dllReferences + |> List.iter (fun dllReference -> tcConfigB.AddReferencedAssemblyByPath(dllReference.Range, dllReference.Text)) + + tcConfigB.knownUnresolvedReferences <- loadClosure.UnresolvedReferences + | None -> () + + setupConfigFromLoadClosure () + + let tcConfig = TcConfig.Create(tcConfigB, validate = true) + let outFile, _, assemblyName = tcConfigB.DecideNames sourceFiles + + let! tcImports, tcGlobals, initialTcInfo, - loadClosureOpt, - _importsInvalidatedByTypeProvider = ComputeBootstrapInfoStatic projectSnapshot - - let fileSnapshots = Map [ for f in projectSnapshot.SourceFiles -> f.FileName, f ] - - let sourceFiles = - sourceFiles - |> List.map (fun (m, fileName, (isLastCompiland, isExe)) -> - let source = - fileSnapshots.TryFind fileName - |> Option.defaultWith (fun () -> - // TODO: does this commonly happen? - - // It can happen when source files are inferred from command line options and are not part of FSharpProjectOptions.SourceFiles - which we use to create the Snapshot - - let snapshotFileSummary = - match projectSnapshot.SourceFiles with - | [] -> "The project snapshot has no source files." - | files -> - "The project snapshot contains the following files:\n" - + (files |> Seq.map (fun x -> x.FileName |> shortPath) |> String.concat "\n") - - failwith - $"Trying to check a file ({shortPath fileName}) which is not part of the project snapshot. {snapshotFileSummary}" - - FSharpFileSnapshot.Create( - fileName, - (FileSystem.GetLastWriteTimeShim fileName).Ticks.ToString(), - (fun () -> fileName |> File.ReadAllText |> SourceText.ofString |> Task.FromResult))) - { - Range = m // TODO: is this used for anything? - Source = source - IsLastCompiland = isLastCompiland - IsExe = isExe - }) + _importsInvalidatedByTypeProvider = ComputeBootstrapInfoStatic (projectSnapshot.ProjectCore, tcConfig, assemblyName, loadClosureOpt) + + // Check for the existence of loaded sources and prepend them to the sources list if present. + let loadedSources = + tcConfig.GetAvailableLoadedSources() + |> List.map (fun (m, fileName) -> m, FSharpFileSnapshot.CreateFromFileSystem(fileName)) + + + //// Mark up the source files with an indicator flag indicating if they are the last source file in the project + //let sourceFiles = + // let flags, isExe = tcConfig.ComputeCanContainEntryPoint(sourceFiles |> List.map snd) + // ((sourceFiles, flags) ||> List.map2 (fun (m, nm) flag -> (m, nm, (flag, isExe)))) + + + //let sourceFiles = + // sourceFiles + // |> List.map (fun (m, fileName, (isLastCompiland, isExe)) -> + // let source = + // fileSnapshots.TryFind fileName + // |> Option.defaultWith (fun () -> + // // TODO: does this commonly happen? + + // // It can happen when source files are inferred from command line options and are not part of FSharpProjectOptions.SourceFiles - which we use to create the Snapshot + + // let snapshotFileSummary = + // match projectSnapshot.SourceFiles with + // | [] -> "The project snapshot has no source files." + // | files -> + // "The project snapshot contains the following files:\n" + // + (files |> Seq.map (fun x -> x.FileName |> shortPath) |> String.concat "\n") + + // failwith + // $"Trying to check a file ({shortPath fileName}) which is not part of the project snapshot. {snapshotFileSummary}" + + // FSharpFileSnapshot.Create( + // fileName, + // (FileSystem.GetLastWriteTimeShim fileName).Ticks.ToString(), + // (fun () -> fileName |> File.ReadAllText |> SourceText.ofString |> Task.FromResult))) + // { + // Range = m // TODO: is this used for anything? + // Source = source + // IsLastCompiland = isLastCompiland + // IsExe = isExe + // }) return Some @@ -809,8 +809,9 @@ type internal TransparentCompiler TcImports = tcImports TcGlobals = tcGlobals InitialTcInfo = initialTcInfo - SourceFiles = sourceFiles + LoadedSources = loadedSources LoadClosure = loadClosureOpt + LastFileName = sourceFiles |> List.last //ImportsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider } } @@ -818,7 +819,7 @@ type internal TransparentCompiler let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) = caches.BootstrapInfo.Get( - projectSnapshot.FullKey, + projectSnapshot.NoFileVersionsKey, node { use _ = Activity.start "ComputeBootstrapInfo" [| Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |] @@ -860,32 +861,28 @@ type internal TransparentCompiler ) // TODO: Not sure if we should cache this. For VS probably not. Maybe it can be configurable by FCS user. - let LoadSource (file: FSharpFile) = + let LoadSource (file: FSharpFileSnapshot) isExe isLastCompiland = node { - let! source = file.Source.GetSource() |> NodeCode.AwaitTask + let! source = file.GetSource() |> NodeCode.AwaitTask return FSharpFileSnapshotWithSource( - FileName = file.Source.FileName, + FileName = file.FileName, Source = source, SourceHash = source.GetChecksum(), - IsLastCompiland = file.IsLastCompiland, - IsExe = file.IsExe + IsLastCompiland = isLastCompiland, + IsExe = isExe ) } let LoadSources (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = node { - let sourceFileMap = - bootstrapInfo.SourceFiles |> Seq.map (fun f -> f.Source.FileName, f) |> Map + let isExe = bootstrapInfo.TcConfig.target.IsExe let! sources = projectSnapshot.SourceFiles |> Seq.map (fun f -> - f.FileName - |> sourceFileMap.TryFind - |> Option.defaultWith (fun _ -> failwith $"File {f.FileName} not found in {projectSnapshot}") - |> LoadSource) + LoadSource f isExe (f.FileName = bootstrapInfo.LastFileName)) |> NodeCode.Parallel return FSharpProjectSnapshotWithSources (projectSnapshot.ProjectCore, sources |> Array.toList) @@ -1763,25 +1760,20 @@ type internal TransparentCompiler // Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] // TODO: might need to deal with exceptions here: - let tcConfigB, _, _ = ComputeTcConfigBuilder projectSnapshot + let tcConfigB, sourceFileNames, _ = ComputeTcConfigBuilder projectSnapshot let tcConfig = TcConfig.Create(tcConfigB, validate = true) - let index, fileSnapshot = + let _index, fileSnapshot = projectSnapshot.SourceFiles |> Seq.mapi pair |> Seq.tryFind (fun (_, f) -> f.FileName = fileName) |> Option.defaultWith (fun () -> failwith $"File not found: {fileName}") - let file = - { - Range = rangeStartup - Source = fileSnapshot - IsLastCompiland = index = projectSnapshot.SourceFiles.Length - 1 - IsExe = tcConfig.target.IsExe - } + let isExe = tcConfig.target.IsExe + let isLastCompiland = fileName = (sourceFileNames |> List.last) - let! file = file |> LoadSource + let! file = LoadSource fileSnapshot isExe isLastCompiland let! parseResult = getParseResult projectSnapshot Seq.empty file tcConfig return parseResult } diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 199687756e6..9d6affcd85d 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -873,6 +873,7 @@ let ``File moving test`` signatureFiles = let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } giraffeProject.Workflow { + clearCache checkFile "Json" expectOk moveFile "Json" 1 Down checkFile "Json" expectOk From 2bf0ea7e69a06c882adbad0af0f406b6113eca27 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 7 Dec 2023 12:42:16 +0100 Subject: [PATCH 169/222] remove parallelism limit --- src/Compiler/Service/TransparentCompiler.fs | 11 ++++++----- .../FSharpChecker/TransparentCompiler.fs | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index adcfa65404c..492b45d1912 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -331,8 +331,8 @@ type internal TransparentCompiler let mutable caches = CompilerCaches(100) // TODO: do we need this? - let maxTypeCheckingParallelism = max 1 (Environment.ProcessorCount / 2) - let maxParallelismSemaphore = new SemaphoreSlim(maxTypeCheckingParallelism) + //let maxTypeCheckingParallelism = max 1 (Environment.ProcessorCount / 2) + //let maxParallelismSemaphore = new SemaphoreSlim(maxTypeCheckingParallelism) // We currently share one global dependency provider for all scripts for the FSharpChecker. // For projects, one is used per project. @@ -1123,10 +1123,10 @@ type internal TransparentCompiler let input, moduleNamesDict = DeduplicateParsedInputModuleName prevTcInfo.moduleNamesDict input - let! ct = NodeCode.CancellationToken + //let! ct = NodeCode.CancellationToken try - do! maxParallelismSemaphore.WaitAsync(ct) |> NodeCode.AwaitTask + //do! maxParallelismSemaphore.WaitAsync(ct) |> NodeCode.AwaitTask let! finisher = CheckOneInputWithCallback @@ -1159,7 +1159,8 @@ type internal TransparentCompiler sink = sink } finally - maxParallelismSemaphore.Release() |> ignore + () + //maxParallelismSemaphore.Release() |> ignore } ) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 9d6affcd85d..c64b8832ead 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -873,7 +873,7 @@ let ``File moving test`` signatureFiles = let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } giraffeProject.Workflow { - clearCache + // clearCache -- for better tracing checkFile "Json" expectOk moveFile "Json" 1 Down checkFile "Json" expectOk From 6f3d604cc156a16d18abfe17d675039d4e16c744 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 7 Dec 2023 14:59:15 +0100 Subject: [PATCH 170/222] Add Cancellable.UsingToken --- src/Compiler/Service/TransparentCompiler.fs | 36 ++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index e9409277790..e85452abea3 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1745,6 +1745,8 @@ type internal TransparentCompiler node { //use _ = // Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] + let! ct = NodeCode.CancellationToken + use _ = Cancellable.UsingToken(ct) // TODO: might need to deal with exceptions here: let tcConfigB, sourceFileNames, _ = ComputeTcConfigBuilder projectSnapshot @@ -1799,6 +1801,9 @@ type internal TransparentCompiler userOpName: string ) : NodeCode = node { + let! ct = NodeCode.CancellationToken + use _ = Cancellable.UsingToken(ct) + let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync @@ -1820,6 +1825,9 @@ type internal TransparentCompiler userOpName: string ) : NodeCode = node { + let! ct = NodeCode.CancellationToken + use _ = Cancellable.UsingToken(ct) + let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync @@ -1857,6 +1865,9 @@ type internal TransparentCompiler userOpName: string ) : NodeCode> = node { + let! ct = NodeCode.CancellationToken + use _ = Cancellable.UsingToken(ct) + ignore canInvalidateProject let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync @@ -1864,13 +1875,16 @@ type internal TransparentCompiler } member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = - node { return! this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) } + this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) member _.FrameworkImportsCache: FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache member this.GetAssemblyData(options: FSharpProjectOptions, fileName, userOpName: string) : NodeCode = node { + let! ct = NodeCode.CancellationToken + use _ = Cancellable.UsingToken(ct) + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync return! this.GetAssemblyData(snapshot, fileName, userOpName) } @@ -1890,6 +1904,9 @@ type internal TransparentCompiler userOpName: string ) : NodeCode = node { + let! ct = NodeCode.CancellationToken + use _ = Cancellable.UsingToken(ct) + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync match! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) with @@ -1904,6 +1921,9 @@ type internal TransparentCompiler userOpName: string ) : NodeCode = node { + let! ct = NodeCode.CancellationToken + use _ = Cancellable.UsingToken(ct) + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync return! this.ParseFile(fileName, snapshot, userOpName) } @@ -1917,6 +1937,8 @@ type internal TransparentCompiler ) : NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> = node { ignore builder + let! ct = NodeCode.CancellationToken + use _ = Cancellable.UsingToken(ct) let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, 1, sourceText) @@ -1969,6 +1991,9 @@ type internal TransparentCompiler ) : NodeCode = node { ignore userOpName + let! ct = NodeCode.CancellationToken + use _ = Cancellable.UsingToken(ct) + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync return! ComputeSemanticClassification(fileName, snapshot) } @@ -1991,6 +2016,9 @@ type internal TransparentCompiler userOpName: string ) : NodeCode = node { + let! ct = NodeCode.CancellationToken + use _ = Cancellable.UsingToken(ct) + let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync @@ -2003,6 +2031,9 @@ type internal TransparentCompiler member this.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = node { + let! ct = NodeCode.CancellationToken + use _ = Cancellable.UsingToken(ct) + ignore userOpName let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync return! ComputeParseAndCheckProject snapshot @@ -2010,6 +2041,9 @@ type internal TransparentCompiler member this.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = node { + let! ct = NodeCode.CancellationToken + use _ = Cancellable.UsingToken(ct) + ignore userOpName return! ComputeParseAndCheckProject projectSnapshot } From 4b94a0e2d45542ba753cae115a84b065c14601a9 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 7 Dec 2023 17:14:11 +0100 Subject: [PATCH 171/222] wip --- src/Compiler/Service/FSharpProjectSnapshot.fs | 12 ++ .../LanguageService/WorkspaceExtensions.fs | 186 +++++++++++++----- 2 files changed, 144 insertions(+), 54 deletions(-) diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 101e2f935cc..7180a0f1024 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -266,6 +266,18 @@ type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Project member private _.With(sourceFiles: 'T list) = FSharpProjectSnapshotBase(projectCore, sourceFiles) + member this.Replace(changedSourceFiles: 'T list) = + // TODO: validate if changed files are not present in the original list? + + let sourceFiles = + sourceFiles + |> List.map (fun x -> + match changedSourceFiles |> List.tryFind (fun y -> y.FileName = x.FileName) with + | Some y -> y + | None -> x) + + this.With(sourceFiles) + member this.UpTo fileIndex = this.With sourceFiles[..fileIndex] member this.UpTo fileName = this.UpTo(this.IndexOf fileName) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 87aa9641235..c3a69b6d5fb 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -91,12 +91,17 @@ module internal FSharpProjectSnapshotSerialization = open FSharpProjectSnapshotSerialization +open System.Collections.Concurrent [] module private CheckerExtensions = let snapshotCache = AsyncMemoize(5000, 500, "SnapshotCache") + let latestSnapshots = ConcurrentDictionary() + + let exist xs = xs |> Seq.isEmpty |> not + let getFSharpOptionsForProject (this: Project) = if not this.IsFSharp then raise (OperationCanceledException("Project is not a FSharp project.")) @@ -120,91 +125,164 @@ module private CheckerExtensions = return ProjectCache.Projects.GetValue(this, ConditionalWeakTable<_, _>.CreateValueCallback(fun _ -> result)) } - let getProjectSnapshot (snapshotAccumulatorOpt) (project: Project) = + let documentToSnapshot (document: Document) = cancellableTask { + let! version = document.GetTextVersionAsync() - let! _, _, _, options = getFSharpOptionsForProject project - - let solution = project.Solution - - let projects = - solution.Projects - |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) - |> Map - - let getFileSnapshot (options: FSharpProjectOptions) path = - async { - let project = projects.TryFind options.ProjectFileName + let getSource () = + task { + let! sourceText = document.GetTextAsync() + return sourceText.ToFSharpSourceText() + } - if project.IsNone then - System.Diagnostics.Trace.TraceError( - "Could not find project {0} in solution {1}", - options.ProjectFileName, - solution.FilePath - ) + return + FSharpFileSnapshot( + FileName = document.FilePath, + Version = version.ToString(), + GetSource = getSource + ) + } - let documentOpt = project |> Option.bind (Map.tryFind path) + let createProjectSnapshot (snapshotAccumulatorOpt) (project: Project) (options: FSharpProjectOptions option) = + cancellableTask { - let! version, getSource = - match documentOpt with - | Some document -> - async { + let! options = + match options with + | Some options -> CancellableTask.singleton options + | None -> + cancellableTask { + let! _, _, _, options = getFSharpOptionsForProject project + return options + } - let! version = document.GetTextVersionAsync() |> Async.AwaitTask + let updatedSnapshot = + match latestSnapshots.TryGetValue project.Id with + | true, (oldProject, oldOptions, oldSnapshot) when FSharpProjectOptions.AreSameForChecking(options, oldOptions) -> + let changes = project.GetChanges(oldProject) + if changes.GetAddedDocuments() |> exist || + changes.GetRemovedDocuments() |> exist || + changes.GetAddedMetadataReferences() |> exist || + changes.GetRemovedMetadataReferences() |> exist || + changes.GetAddedProjectReferences() |> exist || + changes.GetRemovedProjectReferences() |> exist then + // if any of that happened, we create it from scratch + System.Diagnostics.Trace.TraceWarning "Project change not covered by options - suspicious" + None + + else + // we build it from the previous one + + let changedDocuments = changes.GetChangedDocuments() |> Seq.toList + + System.Diagnostics.Trace.TraceInformation $"Incremental update of FSharpProjectSnapshot ({oldSnapshot.ProjectCore.Label}) - {changedDocuments.Length} changed documents" + + changedDocuments + |> Seq.map (project.GetDocument >> documentToSnapshot) + |> CancellableTask.whenAll + |> CancellableTask.map (Array.toList >> oldSnapshot.Replace) + |> Some + + | _ -> None + + let! newSnapshot = + + match updatedSnapshot with + | Some snapshot -> snapshot + | _ -> + cancellableTask { + + + let solution = project.Solution + + let projects = + solution.Projects + |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) + |> Map + + let getFileSnapshot (options: FSharpProjectOptions) path = + async { + let project = projects.TryFind options.ProjectFileName + + if project.IsNone then + System.Diagnostics.Trace.TraceError( + "Could not find project {0} in solution {1}", + options.ProjectFileName, + solution.FilePath + ) + + let documentOpt = project |> Option.bind (Map.tryFind path) + + let! version, getSource = + match documentOpt with + | Some document -> + async { + + let! version = document.GetTextVersionAsync() |> Async.AwaitTask + + let getSource () = + task { + let! sourceText = document.GetTextAsync() + return sourceText.ToFSharpSourceText() + } + + return version.ToString(), getSource - let getSource () = - task { - let! sourceText = document.GetTextAsync() - return sourceText.ToFSharpSourceText() } + | None -> + // This happens with files that are read from /obj - return version.ToString(), getSource + // Fall back to file system + let version = System.IO.File.GetLastWriteTimeUtc(path) - } - | None -> - // This happens with files that are read from /obj + let getSource () = + task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceText.ofString } - // Fall back to file system - let version = System.IO.File.GetLastWriteTimeUtc(path) + async.Return(version.ToString(), getSource) - let getSource () = - task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceText.ofString } + return + FSharpFileSnapshot( + FileName = path, + Version = version, + GetSource = getSource + ) + } - async.Return(version.ToString(), getSource) + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) - return - FSharpFileSnapshot( - FileName = path, - Version = version, - GetSource = getSource - ) - } + //let _json = dumpToJson snapshot + + System.Diagnostics.Trace.TraceInformation $"Created new FSharpProjectSnapshot ({snapshot.ProjectCore.Label})" - let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) + return snapshot + } - let _json = dumpToJson snapshot + latestSnapshots.AddOrUpdate(project.Id, (project, options, newSnapshot), (fun _ _ -> (project, options, newSnapshot))) |> ignore - return snapshot + return newSnapshot } - let getProjectSnapshotForDocument (document: Document, options: FSharpProjectOptions) = + let getOrCreateSnapshotForProject (project: Project) options snapshotAccumulatorOpt = let key = { new ICacheKey<_, _> with - member _.GetKey() = document.Project.Id - member _.GetVersion() = document.Project - member _.GetLabel() = options.ProjectFileName + member _.GetKey() = project.Id + member _.GetVersion() = project + member _.GetLabel() = project.FilePath } snapshotCache.Get( key, node { let! ct = NodeCode.CancellationToken - return! getProjectSnapshot None document.Project ct |> NodeCode.AwaitTask + return! createProjectSnapshot snapshotAccumulatorOpt project options ct |> NodeCode.AwaitTask } ) |> Async.AwaitNodeCode + + let getProjectSnapshotForDocument (document: Document, options: FSharpProjectOptions) = + getOrCreateSnapshotForProject document.Project (Some options) None + type FSharpChecker with /// Parse the source text from the Roslyn document. @@ -540,4 +618,4 @@ type Project with member this.GetFSharpCompilationOptionsAsync() = this |> getFSharpOptionsForProject member this.GetFSharpProjectSnapshot(?snapshotAccumulator) = - this |> getProjectSnapshot snapshotAccumulator + cancellableTask { return! getOrCreateSnapshotForProject this None snapshotAccumulator } From 8eb24e5acc1a004cf61399142ee23055a557a6fa Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 8 Dec 2023 13:26:12 +0100 Subject: [PATCH 172/222] this might work --- .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 2 +- .../UnusedOpensDiagnosticAnalyzer.fs | 2 +- .../LanguageService/WorkspaceExtensions.fs | 212 ++++++++++-------- 3 files changed, 121 insertions(+), 95 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index f8de2140da1..ccaf60daa19 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -27,7 +27,7 @@ type internal UnusedDeclarationsAnalyzer [] () = cancellableTask { - do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) + // do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof (UnusedDeclarationsAnalyzer)) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index eca26429312..61a9d9d28b0 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -42,7 +42,7 @@ type internal UnusedOpensDiagnosticAnalyzer [] () = Tasks.Task.FromResult(ImmutableArray.Empty) else cancellableTask { - do Trace.TraceInformation("{0:n3} (start) UnusedOpensAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) + // do Trace.TraceInformation("{0:n3} (start) UnusedOpensAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) let! sourceText = document.GetTextAsync() let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges document diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index c3a69b6d5fb..92a9c932967 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -35,7 +35,6 @@ type Solution with member internal this.GetFSharpWorkspaceService() = this.Workspace.Services.GetRequiredService() - module internal FSharpProjectSnapshotSerialization = let serializeFileSnapshot (snapshot: FSharpFileSnapshot) = @@ -52,32 +51,36 @@ module internal FSharpProjectSnapshotSerialization = let rec serializeReferencedProject (reference: FSharpReferencedProjectSnapshot) = let output = JObject() + match reference with - | FSharpReference (projectOutputFile, snapshot) -> + | FSharpReference(projectOutputFile, snapshot) -> output.Add("projectOutputFile", projectOutputFile) output.Add("snapshot", serializeSnapshot snapshot) + output and serializeSnapshot (snapshot: FSharpProjectSnapshot) = - + let output = JObject() output.Add("ProjectFileName", snapshot.ProjectFileName) output.Add("ProjectId", (snapshot.ProjectId |> Option.defaultValue null |> JToken.FromObject)) - output.Add("SourceFiles", snapshot.SourceFiles |> Seq.map serializeFileSnapshot |> JArray ) - output.Add("ReferencesOnDisk", snapshot.ReferencesOnDisk |> Seq.map serializeReferenceOnDisk |> JArray ) + output.Add("SourceFiles", snapshot.SourceFiles |> Seq.map serializeFileSnapshot |> JArray) + output.Add("ReferencesOnDisk", snapshot.ReferencesOnDisk |> Seq.map serializeReferenceOnDisk |> JArray) output.Add("OtherOptions", JArray(snapshot.OtherOptions)) - output.Add("ReferencedProjects", snapshot.ReferencedProjects |> Seq.map serializeReferencedProject |> JArray ) + output.Add("ReferencedProjects", snapshot.ReferencedProjects |> Seq.map serializeReferencedProject |> JArray) output.Add("IsIncompleteTypeCheckEnvironment", snapshot.IsIncompleteTypeCheckEnvironment) output.Add("UseScriptResolutionRules", snapshot.UseScriptResolutionRules) output.Add("LoadTime", snapshot.LoadTime) // output.Add("UnresolvedReferences", snapshot.UnresolvedReferences) - output.Add("OriginalLoadReferences", + output.Add( + "OriginalLoadReferences", snapshot.OriginalLoadReferences - |> Seq.map (fun (r:Text.range, a, b) -> - JArray(r.FileName, r.Start, r.End, a, b)) |> JArray) + |> Seq.map (fun (r: Text.range, a, b) -> JArray(r.FileName, r.Start, r.End, a, b)) + |> JArray + ) - output.Add("Stamp", (snapshot.Stamp |> (Option.defaultValue 0) |> JToken.FromObject )) + output.Add("Stamp", (snapshot.Stamp |> (Option.defaultValue 0) |> JToken.FromObject)) output @@ -88,7 +91,6 @@ module internal FSharpProjectSnapshotSerialization = let json = jObject.ToString(Formatting.Indented) json - open FSharpProjectSnapshotSerialization open System.Collections.Concurrent @@ -96,9 +98,9 @@ open System.Collections.Concurrent [] module private CheckerExtensions = - let snapshotCache = AsyncMemoize(5000, 500, "SnapshotCache") + let snapshotCache = AsyncMemoize(1000, 500, "SnapshotCache") - let latestSnapshots = ConcurrentDictionary() + let latestSnapshots = ConcurrentDictionary<_, _>() let exist xs = xs |> Seq.isEmpty |> not @@ -125,7 +127,7 @@ module private CheckerExtensions = return ProjectCache.Projects.GetValue(this, ConditionalWeakTable<_, _>.CreateValueCallback(fun _ -> result)) } - let documentToSnapshot (document: Document) = + let documentToSnapshot (document: Document) = cancellableTask { let! version = document.GetTextVersionAsync() @@ -135,128 +137,150 @@ module private CheckerExtensions = return sourceText.ToFSharpSourceText() } - return - FSharpFileSnapshot( - FileName = document.FilePath, - Version = version.ToString(), - GetSource = getSource - ) + return FSharpFileSnapshot(FileName = document.FilePath, Version = version.ToString(), GetSource = getSource) } + let getReferencedProjectVersions (project: Project) = + project.GetAllProjectsThisProjectDependsOn() + |> Seq.map (fun r ct -> r.GetDependentSemanticVersionAsync(ct)) + |> CancellableTask.whenAll + |> CancellableTask.map (Seq.map (fun x -> x.ToString()) >> Set) + let createProjectSnapshot (snapshotAccumulatorOpt) (project: Project) (options: FSharpProjectOptions option) = cancellableTask { - let! options = + let! options = match options with | Some options -> CancellableTask.singleton options - | None -> + | None -> cancellableTask { let! _, _, _, options = getFSharpOptionsForProject project return options } - let updatedSnapshot = + let! projectVersion = project.GetDependentSemanticVersionAsync() + + let! referenceVersions = getReferencedProjectVersions project + + let updatedSnapshot = match latestSnapshots.TryGetValue project.Id with - | true, (oldProject, oldOptions, oldSnapshot) when FSharpProjectOptions.AreSameForChecking(options, oldOptions) -> + | true, (_, oldProjectVersion, _, _, oldSnapshot) when projectVersion = oldProjectVersion -> + Some(CancellableTask.singleton oldSnapshot) + | true, (_, _, oldReferenceVersions, _, _) when referenceVersions <> oldReferenceVersions -> + System.Diagnostics.Trace.TraceWarning "Reference versions changed" + None + + | true, (oldProject, _, _oldProjectVersion, oldOptions, oldSnapshot: FSharpProjectSnapshot) when + FSharpProjectOptions.AreSameForChecking(options, oldOptions) + -> + let changes = project.GetChanges(oldProject) - if changes.GetAddedDocuments() |> exist || - changes.GetRemovedDocuments() |> exist || - changes.GetAddedMetadataReferences() |> exist || - changes.GetRemovedMetadataReferences() |> exist || - changes.GetAddedProjectReferences() |> exist || - changes.GetRemovedProjectReferences() |> exist then - // if any of that happened, we create it from scratch - System.Diagnostics.Trace.TraceWarning "Project change not covered by options - suspicious" - None - - else - // we build it from the previous one + + if + changes.GetAddedDocuments() |> exist + || changes.GetRemovedDocuments() |> exist + || changes.GetAddedMetadataReferences() |> exist + || changes.GetRemovedMetadataReferences() |> exist + || changes.GetAddedProjectReferences() |> exist + || changes.GetRemovedProjectReferences() |> exist + then + // if any of that happened, we create it from scratch + System.Diagnostics.Trace.TraceWarning "Project change not covered by options - suspicious" + None + + else + // we build it from the previous one let changedDocuments = changes.GetChangedDocuments() |> Seq.toList - - System.Diagnostics.Trace.TraceInformation $"Incremental update of FSharpProjectSnapshot ({oldSnapshot.ProjectCore.Label}) - {changedDocuments.Length} changed documents" + + System.Diagnostics.Trace.TraceInformation + $"Incremental update of FSharpProjectSnapshot ({oldSnapshot.ProjectCore.Label}) - {changedDocuments.Length} changed documents" + + if changedDocuments.Length = 0 then + // this is suspicious + let _breakpoint = "here" + () changedDocuments |> Seq.map (project.GetDocument >> documentToSnapshot) |> CancellableTask.whenAll |> CancellableTask.map (Array.toList >> oldSnapshot.Replace) |> Some - + | _ -> None - let! newSnapshot = + let! newSnapshot = match updatedSnapshot with | Some snapshot -> snapshot - | _ -> - cancellableTask { - + | _ -> + cancellableTask { - let solution = project.Solution + let solution = project.Solution - let projects = - solution.Projects - |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) - |> Map + let projects = + solution.Projects + |> Seq.map (fun p -> p.FilePath, p.Documents |> Seq.map (fun d -> d.FilePath, d) |> Map) + |> Map - let getFileSnapshot (options: FSharpProjectOptions) path = - async { - let project = projects.TryFind options.ProjectFileName + let getFileSnapshot (options: FSharpProjectOptions) path = + async { + let project = projects.TryFind options.ProjectFileName - if project.IsNone then - System.Diagnostics.Trace.TraceError( - "Could not find project {0} in solution {1}", - options.ProjectFileName, - solution.FilePath - ) + if project.IsNone then + System.Diagnostics.Trace.TraceError( + "Could not find project {0} in solution {1}", + options.ProjectFileName, + solution.FilePath + ) - let documentOpt = project |> Option.bind (Map.tryFind path) + let documentOpt = project |> Option.bind (Map.tryFind path) - let! version, getSource = - match documentOpt with - | Some document -> - async { + let! version, getSource = + match documentOpt with + | Some document -> + async { - let! version = document.GetTextVersionAsync() |> Async.AwaitTask + let! version = document.GetTextVersionAsync() |> Async.AwaitTask - let getSource () = - task { - let! sourceText = document.GetTextAsync() - return sourceText.ToFSharpSourceText() - } + let getSource () = + task { + let! sourceText = document.GetTextAsync() + return sourceText.ToFSharpSourceText() + } - return version.ToString(), getSource + return version.ToString(), getSource - } - | None -> - // This happens with files that are read from /obj + } + | None -> + // This happens with files that are read from /obj - // Fall back to file system - let version = System.IO.File.GetLastWriteTimeUtc(path) + // Fall back to file system + let version = System.IO.File.GetLastWriteTimeUtc(path) - let getSource () = - task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceText.ofString } + let getSource () = + task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceText.ofString } - async.Return(version.ToString(), getSource) + async.Return(version.ToString(), getSource) - return - FSharpFileSnapshot( - FileName = path, - Version = version, - GetSource = getSource - ) - } + return FSharpFileSnapshot(FileName = path, Version = version, GetSource = getSource) + } - let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) + let! snapshot = + FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) - //let _json = dumpToJson snapshot + //let _json = dumpToJson snapshot - System.Diagnostics.Trace.TraceInformation $"Created new FSharpProjectSnapshot ({snapshot.ProjectCore.Label})" + System.Diagnostics.Trace.TraceInformation $"Created new FSharpProjectSnapshot ({snapshot.ProjectCore.Label})" - return snapshot + return snapshot } - latestSnapshots.AddOrUpdate(project.Id, (project, options, newSnapshot), (fun _ _ -> (project, options, newSnapshot))) |> ignore + let latestSnapshotData = + project, projectVersion, referenceVersions, options, newSnapshot + + latestSnapshots.AddOrUpdate(project.Id, latestSnapshotData, (fun _ _ -> latestSnapshotData)) + |> ignore return newSnapshot } @@ -274,15 +298,17 @@ module private CheckerExtensions = key, node { let! ct = NodeCode.CancellationToken - return! createProjectSnapshot snapshotAccumulatorOpt project options ct |> NodeCode.AwaitTask + + return! + createProjectSnapshot snapshotAccumulatorOpt project options ct + |> NodeCode.AwaitTask } ) |> Async.AwaitNodeCode - + let getProjectSnapshotForDocument (document: Document, options: FSharpProjectOptions) = - getOrCreateSnapshotForProject document.Project (Some options) None + getOrCreateSnapshotForProject document.Project (Some options) None - type FSharpChecker with /// Parse the source text from the Roslyn document. From 4b400081476a16bedca69a03872a5313c5e323f3 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 12 Dec 2023 19:34:22 +0100 Subject: [PATCH 173/222] ISourceTextNew to keep backwards compatibility --- src/Compiler/Facilities/prim-lexing.fs | 38 ++++++++++++++++++- src/Compiler/Facilities/prim-lexing.fsi | 8 ++++ src/Compiler/Service/FSharpProjectSnapshot.fs | 10 ++--- ...vice.SurfaceArea.netstandard20.release.bsl | 16 ++------ tests/FSharp.Test.Utilities/CompilerAssert.fs | 2 +- .../ProjectGeneration.fs | 6 +-- .../CompilerServiceBenchmarks/SourceText.fs | 1 - .../src/FSharp.Editor/Common/Extensions.fs | 4 +- .../LanguageService/LanguageService.fs | 9 ++++- .../LanguageService/WorkspaceExtensions.fs | 2 +- 10 files changed, 67 insertions(+), 29 deletions(-) diff --git a/src/Compiler/Facilities/prim-lexing.fs b/src/Compiler/Facilities/prim-lexing.fs index 4f204d33203..54f3b51eec1 100644 --- a/src/Compiler/Facilities/prim-lexing.fs +++ b/src/Compiler/Facilities/prim-lexing.fs @@ -33,6 +33,9 @@ type ISourceText = abstract GetSubTextFromRange: range: range -> string +type ISourceTextNew = + inherit ISourceText + abstract GetChecksum: unit -> System.Collections.Immutable.ImmutableArray [] @@ -72,7 +75,7 @@ type StringText(str: string) = override _.ToString() = str - interface ISourceText with + interface ISourceTextNew with member _.Item with get index = str[index] @@ -156,6 +159,39 @@ type StringText(str: string) = module SourceText = let ofString str = StringText(str) :> ISourceText + +module SourceTextNew = + + let ofString str = StringText(str) :> ISourceTextNew + + let ofISourceText (sourceText: ISourceText) = { + new ISourceTextNew with + member _.Item + with get index = sourceText[index] + + member _.GetLineString(x) = sourceText.GetLineString(x) + + member _.GetLineCount() = sourceText.GetLineCount() + + member _.GetLastCharacterPosition() = sourceText.GetLastCharacterPosition() + + member _.GetSubTextString(x, y) = sourceText.GetSubTextString(x, y) + + member _.SubTextEquals(x, y) = sourceText.SubTextEquals(x, y) + + member _.Length = sourceText.Length + + member _.ContentEquals(x) = sourceText.ContentEquals(x) + + member _.CopyTo(a, b, c, d) = sourceText.CopyTo(a, b, c, d) + + member _.GetSubTextFromRange(x) = sourceText.GetSubTextFromRange(x) + + member _.GetChecksum() = + // TODO: something better... + sourceText.ToString() |> Md5Hasher.hashString |> ImmutableArray.Create + } + // NOTE: the code in this file is a drop-in replacement runtime for Lexing.fs from the FsLexYacc repository namespace Internal.Utilities.Text.Lexing diff --git a/src/Compiler/Facilities/prim-lexing.fsi b/src/Compiler/Facilities/prim-lexing.fsi index 207822ff404..1686c858d21 100644 --- a/src/Compiler/Facilities/prim-lexing.fsi +++ b/src/Compiler/Facilities/prim-lexing.fsi @@ -39,6 +39,9 @@ type ISourceText = /// Throws an exception when the input range is outside the file boundaries. abstract GetSubTextFromRange: range: range -> string +type ISourceTextNew = + inherit ISourceText + abstract GetChecksum: unit -> System.Collections.Immutable.ImmutableArray /// Functions related to ISourceText objects @@ -47,6 +50,11 @@ module SourceText = /// Creates an ISourceText object from the given string val ofString: string -> ISourceText +module SourceTextNew = + + val ofString: string -> ISourceTextNew + val ofISourceText: ISourceText -> ISourceTextNew + // // NOTE: the code in this file is a drop-in replacement runtime for Lexing.fsi from the FsLexYacc repository // and is referenced by generated code for the three FsLex generated lexers in the F# compiler. diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 101e2f935cc..884fe2832df 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -59,16 +59,16 @@ module internal Helpers = |> fst, lastFile -type FSharpFileSnapshot(FileName: string, Version: string, GetSource: unit -> Task) = +type FSharpFileSnapshot(FileName: string, Version: string, GetSource: unit -> Task) = - static member Create(fileName: string, version: string, getSource: unit -> Task) = + static member Create(fileName: string, version: string, getSource: unit -> Task) = FSharpFileSnapshot(fileName, version, getSource) static member CreateFromFileSystem(fileName: string) = FSharpFileSnapshot( fileName, FileSystem.GetLastWriteTimeShim(fileName).Ticks.ToString(), - fun () -> fileName |> File.ReadAllText |> SourceText.ofString |> Task.FromResult + fun () -> fileName |> File.ReadAllText |> SourceTextNew.ofString |> Task.FromResult ) member public _.FileName = FileName @@ -378,7 +378,7 @@ type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Project let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() return - FSharpFileSnapshot.Create(fileName, timeStamp.Ticks.ToString(), (fun () -> Task.FromResult(SourceText.ofString contents))) + FSharpFileSnapshot.Create(fileName, timeStamp.Ticks.ToString(), (fun () -> Task.FromResult(SourceTextNew.ofString contents))) } static member FromOptions(options: FSharpProjectOptions) = @@ -392,7 +392,7 @@ type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Project FSharpFileSnapshot.Create( fileName, $"{fileVersion}{sourceText.GetHashCode().ToString()}", - fun () -> Task.FromResult sourceText + fun () -> Task.FromResult (SourceTextNew.ofISourceText sourceText) ) ) else diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 79ee3a0cf92..b79eedc2532 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -1967,16 +1967,6 @@ FSharp.Compiler.CodeAnalysis.DocumentSource: FSharp.Compiler.CodeAnalysis.Docume FSharp.Compiler.CodeAnalysis.DocumentSource: Int32 Tag FSharp.Compiler.CodeAnalysis.DocumentSource: Int32 get_Tag() FSharp.Compiler.CodeAnalysis.DocumentSource: System.String ToString() -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 braceMatchCacheSize -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 checkFileInProjectCacheSize -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 frameworkTcImportsCacheStrongSize -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_braceMatchCacheSize() -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_checkFileInProjectCacheSize() -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_frameworkTcImportsCacheStrongSize() -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_parseFileCacheSize() -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_projectCacheSizeDefault() -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 parseFileCacheSize -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 projectCacheSizeDefault FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer+Succeeded: FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults Item FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer+Succeeded: FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults get_Item() FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer+Tags: Int32 Aborted @@ -2304,8 +2294,6 @@ FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_baggage() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_itemSpec() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String itemSpec FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.String],System.String], System.String) -FSharp.Compiler.CodeAnalysis.ParseCacheLockToken: Void .ctor() -FSharp.Compiler.CodeAnalysis.ScriptClosureCacheToken: Void .ctor() FSharp.Compiler.CompilerEnvironment: Boolean IsCheckerSupportedSubcategory(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsCompilable(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsScriptFile(System.String) @@ -10198,12 +10186,12 @@ FSharp.Compiler.Text.ISourceText: Char get_Item(Int32) FSharp.Compiler.Text.ISourceText: Int32 GetLineCount() FSharp.Compiler.Text.ISourceText: Int32 Length FSharp.Compiler.Text.ISourceText: Int32 get_Length() -FSharp.Compiler.Text.ISourceText: System.Collections.Immutable.ImmutableArray`1[System.Byte] GetChecksum() FSharp.Compiler.Text.ISourceText: System.String GetLineString(Int32) FSharp.Compiler.Text.ISourceText: System.String GetSubTextFromRange(FSharp.Compiler.Text.Range) FSharp.Compiler.Text.ISourceText: System.String GetSubTextString(Int32, Int32) FSharp.Compiler.Text.ISourceText: System.Tuple`2[System.Int32,System.Int32] GetLastCharacterPosition() FSharp.Compiler.Text.ISourceText: Void CopyTo(Int32, Char[], Int32, Int32) +FSharp.Compiler.Text.ISourceTextNew: System.Collections.Immutable.ImmutableArray`1[System.Byte] GetChecksum() FSharp.Compiler.Text.Line: Int32 fromZ(Int32) FSharp.Compiler.Text.Line: Int32 toZ(Int32) FSharp.Compiler.Text.NavigableTaggedText: FSharp.Compiler.Text.Range Range @@ -10283,6 +10271,8 @@ FSharp.Compiler.Text.RangeModule: System.Tuple`2[System.String,System.Tuple`2[Sy FSharp.Compiler.Text.RangeModule: System.Tuple`2[System.Tuple`2[System.Int32,System.Int32],System.Tuple`2[System.Int32,System.Int32]] toZ(FSharp.Compiler.Text.Range) FSharp.Compiler.Text.RangeModule: Void outputRange(System.IO.TextWriter, FSharp.Compiler.Text.Range) FSharp.Compiler.Text.SourceText: FSharp.Compiler.Text.ISourceText ofString(System.String) +FSharp.Compiler.Text.SourceTextNew: FSharp.Compiler.Text.ISourceTextNew ofISourceText(FSharp.Compiler.Text.ISourceText) +FSharp.Compiler.Text.SourceTextNew: FSharp.Compiler.Text.ISourceTextNew ofString(System.String) FSharp.Compiler.Text.TaggedText: FSharp.Compiler.Text.TextTag Tag FSharp.Compiler.Text.TaggedText: FSharp.Compiler.Text.TextTag get_Tag() FSharp.Compiler.Text.TaggedText: System.String Text diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index 1c21a60b93f..55b1f0d441a 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -901,7 +901,7 @@ Updated automatically, please check diffs in your pull request, changes must be Version = "1", GetSource = fun () -> task { match! getSourceText fileName with - | Some source -> return source + | Some source -> return SourceTextNew.ofISourceText source | None -> return failwith $"couldn't get source for {fileName}" } )) diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index feb26fcc928..16a33735b05 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -542,7 +542,7 @@ module ProjectOperations = filePath |> project.FindByPath |> renderSourceFile project - |> SourceText.ofString + |> SourceTextNew.ofString let internal getFileSnapshot (project: SyntheticProject) _options (path: string) = async { @@ -750,7 +750,7 @@ module Helpers = FSharpFileSnapshot( FileName = fileName, Version = "1", - GetSource = fun () -> source |> SourceText.ofString |> Task.FromResult ) + GetSource = fun () -> source |> SourceTextNew.ofString |> Task.FromResult ) |> async.Return let checker = FSharpChecker.Create( @@ -846,7 +846,7 @@ type ProjectWorkflowBuilder let mutable activity = None let mutable tracerProvider = None - let getSource f = f |> getSourceText latestProject |> Some |> async.Return + let getSource f = f |> getSourceText latestProject :> ISourceText |> Some |> async.Return let checker = defaultArg diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs index e4525694d18..817e95fd044 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs @@ -99,7 +99,6 @@ module internal SourceText = let lastLine = this.GetLineString(range.EndLine - 1) sb.Append(lastLine.Substring(0, range.EndColumn)).ToString() - member _.GetChecksum() = sourceText.GetChecksum() } sourceText diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index 6bbe0f969f4..b0eb7305713 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -87,7 +87,7 @@ module private SourceText = let combineValues (values: seq<'T>) = (0, values) ||> Seq.fold (fun hash value -> combine (value.GetHashCode()) hash) - let weakTable = ConditionalWeakTable() + let weakTable = ConditionalWeakTable() let create (sourceText: SourceText) = let sourceText = @@ -113,7 +113,7 @@ module private SourceText = |> Hash.combine sourceText.Length override _.ToString() = sourceText.ToString() - interface ISourceText with + interface ISourceTextNew with member _.Item with get index = sourceText.[index] diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 18bf6aa4171..0cae7987877 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -25,6 +25,7 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp open Microsoft.CodeAnalysis.Host.Mef open Microsoft.VisualStudio.FSharp.Editor.Telemetry open CancellableTasks +open FSharp.Compiler.Text #nowarn "9" // NativePtr.toNativeInt #nowarn "57" // Experimental stuff @@ -190,8 +191,12 @@ type internal FSharpWorkspaceServiceFactory [] captureIdentifiersWhenParsing = enableFastFindReferences, documentSource = (if enableLiveBuffers then - DocumentSource.Custom getSource - else + (DocumentSource.Custom (fun filename -> + async { + match! getSource filename with + | Some source -> return Some(source :> ISourceText) + | None -> return None })) + else DocumentSource.FileSystem), useSyntaxTreeCache = useSyntaxTreeCache, useTransparentCompiler = useTransparentCompiler diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 87aa9641235..fbd2b756fe5 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -168,7 +168,7 @@ module private CheckerExtensions = let version = System.IO.File.GetLastWriteTimeUtc(path) let getSource () = - task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceText.ofString } + task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceTextNew.ofString } async.Return(version.ToString(), getSource) From e8fe90ed5ea94da4318d42e560f6ba4a1e9f3a1e Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 18 Dec 2023 09:11:03 +0100 Subject: [PATCH 174/222] f --- src/Compiler/Facilities/AsyncMemoize.fs | 2 +- src/Compiler/Facilities/prim-lexing.fs | 4 +-- src/Compiler/Service/FSharpProjectSnapshot.fs | 8 +++-- src/Compiler/Service/TransparentCompiler.fs | 20 ++++++------ .../LanguageService/LanguageService.fs | 13 ++++---- .../LanguageService/WorkspaceExtensions.fs | 31 +++++++++---------- 6 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 2837ffd99b7..1db9db1517c 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -281,7 +281,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let ex = exn (message) failures.Add(key, ex) Interlocked.Increment &errors |> ignore - raise ex + // raise ex -- Suppose there's no need to raise here - where does it even go? let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = task { diff --git a/src/Compiler/Facilities/prim-lexing.fs b/src/Compiler/Facilities/prim-lexing.fs index c1d615fd413..6b927ef4a96 100644 --- a/src/Compiler/Facilities/prim-lexing.fs +++ b/src/Compiler/Facilities/prim-lexing.fs @@ -165,8 +165,8 @@ module SourceTextNew = let ofString str = StringText(str) :> ISourceTextNew - let ofISourceText (sourceText: ISourceText) = { - new ISourceTextNew with + let ofISourceText (sourceText: ISourceText) = + { new ISourceTextNew with member _.Item with get index = sourceText[index] diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 884fe2832df..09db032279d 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -378,7 +378,11 @@ type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Project let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() return - FSharpFileSnapshot.Create(fileName, timeStamp.Ticks.ToString(), (fun () -> Task.FromResult(SourceTextNew.ofString contents))) + FSharpFileSnapshot.Create( + fileName, + timeStamp.Ticks.ToString(), + (fun () -> Task.FromResult(SourceTextNew.ofString contents)) + ) } static member FromOptions(options: FSharpProjectOptions) = @@ -392,7 +396,7 @@ type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Project FSharpFileSnapshot.Create( fileName, $"{fileVersion}{sourceText.GetHashCode().ToString()}", - fun () -> Task.FromResult (SourceTextNew.ofISourceText sourceText) + fun () -> Task.FromResult(SourceTextNew.ofISourceText sourceText) ) ) else diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index e85452abea3..358f436604b 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1803,7 +1803,7 @@ type internal TransparentCompiler node { let! ct = NodeCode.CancellationToken use _ = Cancellable.UsingToken(ct) - + let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync @@ -1827,7 +1827,7 @@ type internal TransparentCompiler node { let! ct = NodeCode.CancellationToken use _ = Cancellable.UsingToken(ct) - + let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync @@ -1867,7 +1867,7 @@ type internal TransparentCompiler node { let! ct = NodeCode.CancellationToken use _ = Cancellable.UsingToken(ct) - + ignore canInvalidateProject let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync @@ -1884,7 +1884,7 @@ type internal TransparentCompiler node { let! ct = NodeCode.CancellationToken use _ = Cancellable.UsingToken(ct) - + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync return! this.GetAssemblyData(snapshot, fileName, userOpName) } @@ -1906,7 +1906,7 @@ type internal TransparentCompiler node { let! ct = NodeCode.CancellationToken use _ = Cancellable.UsingToken(ct) - + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync match! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) with @@ -1923,7 +1923,7 @@ type internal TransparentCompiler node { let! ct = NodeCode.CancellationToken use _ = Cancellable.UsingToken(ct) - + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync return! this.ParseFile(fileName, snapshot, userOpName) } @@ -1993,7 +1993,7 @@ type internal TransparentCompiler ignore userOpName let! ct = NodeCode.CancellationToken use _ = Cancellable.UsingToken(ct) - + let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync return! ComputeSemanticClassification(fileName, snapshot) } @@ -2018,7 +2018,7 @@ type internal TransparentCompiler node { let! ct = NodeCode.CancellationToken use _ = Cancellable.UsingToken(ct) - + let! snapshot = FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync @@ -2033,7 +2033,7 @@ type internal TransparentCompiler node { let! ct = NodeCode.CancellationToken use _ = Cancellable.UsingToken(ct) - + ignore userOpName let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync return! ComputeParseAndCheckProject snapshot @@ -2043,7 +2043,7 @@ type internal TransparentCompiler node { let! ct = NodeCode.CancellationToken use _ = Cancellable.UsingToken(ct) - + ignore userOpName return! ComputeParseAndCheckProject projectSnapshot } diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 0cae7987877..b492cf1f35f 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -191,12 +191,13 @@ type internal FSharpWorkspaceServiceFactory [] captureIdentifiersWhenParsing = enableFastFindReferences, documentSource = (if enableLiveBuffers then - (DocumentSource.Custom (fun filename -> - async { - match! getSource filename with - | Some source -> return Some(source :> ISourceText) - | None -> return None })) - else + (DocumentSource.Custom(fun filename -> + async { + match! getSource filename with + | Some source -> return Some(source :> ISourceText) + | None -> return None + })) + else DocumentSource.FileSystem), useSyntaxTreeCache = useSyntaxTreeCache, useTransparentCompiler = useTransparentCompiler diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index fbd2b756fe5..73293002580 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -35,7 +35,6 @@ type Solution with member internal this.GetFSharpWorkspaceService() = this.Workspace.Services.GetRequiredService() - module internal FSharpProjectSnapshotSerialization = let serializeFileSnapshot (snapshot: FSharpFileSnapshot) = @@ -52,32 +51,36 @@ module internal FSharpProjectSnapshotSerialization = let rec serializeReferencedProject (reference: FSharpReferencedProjectSnapshot) = let output = JObject() + match reference with - | FSharpReference (projectOutputFile, snapshot) -> + | FSharpReference(projectOutputFile, snapshot) -> output.Add("projectOutputFile", projectOutputFile) output.Add("snapshot", serializeSnapshot snapshot) + output and serializeSnapshot (snapshot: FSharpProjectSnapshot) = - + let output = JObject() output.Add("ProjectFileName", snapshot.ProjectFileName) output.Add("ProjectId", (snapshot.ProjectId |> Option.defaultValue null |> JToken.FromObject)) - output.Add("SourceFiles", snapshot.SourceFiles |> Seq.map serializeFileSnapshot |> JArray ) - output.Add("ReferencesOnDisk", snapshot.ReferencesOnDisk |> Seq.map serializeReferenceOnDisk |> JArray ) + output.Add("SourceFiles", snapshot.SourceFiles |> Seq.map serializeFileSnapshot |> JArray) + output.Add("ReferencesOnDisk", snapshot.ReferencesOnDisk |> Seq.map serializeReferenceOnDisk |> JArray) output.Add("OtherOptions", JArray(snapshot.OtherOptions)) - output.Add("ReferencedProjects", snapshot.ReferencedProjects |> Seq.map serializeReferencedProject |> JArray ) + output.Add("ReferencedProjects", snapshot.ReferencedProjects |> Seq.map serializeReferencedProject |> JArray) output.Add("IsIncompleteTypeCheckEnvironment", snapshot.IsIncompleteTypeCheckEnvironment) output.Add("UseScriptResolutionRules", snapshot.UseScriptResolutionRules) output.Add("LoadTime", snapshot.LoadTime) // output.Add("UnresolvedReferences", snapshot.UnresolvedReferences) - output.Add("OriginalLoadReferences", + output.Add( + "OriginalLoadReferences", snapshot.OriginalLoadReferences - |> Seq.map (fun (r:Text.range, a, b) -> - JArray(r.FileName, r.Start, r.End, a, b)) |> JArray) + |> Seq.map (fun (r: Text.range, a, b) -> JArray(r.FileName, r.Start, r.End, a, b)) + |> JArray + ) - output.Add("Stamp", (snapshot.Stamp |> (Option.defaultValue 0) |> JToken.FromObject )) + output.Add("Stamp", (snapshot.Stamp |> (Option.defaultValue 0) |> JToken.FromObject)) output @@ -88,7 +91,6 @@ module internal FSharpProjectSnapshotSerialization = let json = jObject.ToString(Formatting.Indented) json - open FSharpProjectSnapshotSerialization @@ -172,12 +174,7 @@ module private CheckerExtensions = async.Return(version.ToString(), getSource) - return - FSharpFileSnapshot( - FileName = path, - Version = version, - GetSource = getSource - ) + return FSharpFileSnapshot(FileName = path, Version = version, GetSource = getSource) } let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) From 29a840fa2aa405f9e4a490feb7c4e76d7552e319 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 19 Dec 2023 17:31:44 +0100 Subject: [PATCH 175/222] Cancellation tracking and improvements --- src/Compiler/Facilities/AsyncMemoize.fs | 48 +++++++++-- src/Compiler/Service/FSharpProjectSnapshot.fs | 2 + src/Compiler/Service/TransparentCompiler.fs | 70 ++++++++-------- .../CompilerService/AsyncMemoize.fs | 79 +++++++++++++++---- .../LanguageService/WorkspaceExtensions.fs | 2 - 5 files changed, 136 insertions(+), 65 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 1db9db1517c..4907c3580c0 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -7,6 +7,7 @@ open System.IO open System.Threading open System.Threading.Tasks +open FSharp.Compiler open FSharp.Compiler.BuildGraph open FSharp.Compiler.Diagnostics open FSharp.Compiler.DiagnosticsLogger @@ -29,6 +30,16 @@ module internal Utils = let replayDiagnostics (logger: DiagnosticsLogger) = Seq.iter ((<|) logger.DiagnosticSink) + let (|TaskCancelled|_|) (ex : exn) = + match ex with + | :? System.Threading.Tasks.TaskCanceledException as tce -> Some tce + //| :? System.AggregateException as ae -> + // if ae.InnerExceptions |> Seq.forall (fun e -> e :? System.Threading.Tasks.TaskCanceledException) then + // ae.InnerExceptions |> Seq.tryHead |> Option.map (fun e -> e :?> System.Threading.Tasks.TaskCanceledException) + // else + // None + | _ -> None + type internal StateUpdate<'TValue> = | CancelRequest | OriginatorCanceled @@ -41,7 +52,6 @@ type internal MemoizeReply<'TValue> = type internal MemoizeRequest<'TValue> = | GetOrCompute of NodeCode<'TValue> * CancellationToken - | Sync [] type internal Job<'TValue> = @@ -152,6 +162,13 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let mutable collected = 0 let mutable strengthened = 0 + let mutable cancel_ct_registration_original = 0 + let mutable cancel_exception_original = 0 + let mutable cancel_original_processed = 0 + let mutable cancel_ct_registration_subsequent = 0 + let mutable cancel_exception_subsequent = 0 + let mutable cancel_subsequent_processed = 0 + let failures = ResizeArray() let mutable avgDurationMs = 0.0 @@ -225,7 +242,6 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T return match msg, cached with - | Sync, _ -> New Unchecked.defaultof<_> | GetOrCompute _, Some(Completed(result, diags)) -> Interlocked.Increment &hits |> ignore diags |> replayDiagnostics diagnosticLogger @@ -236,6 +252,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T ct.Register(fun _ -> let _name = name + Interlocked.Increment &cancel_ct_registration_subsequent |> ignore post (key, CancelRequest)) |> saveRegistration key @@ -251,6 +268,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T ct.Register(fun _ -> let _name = name + Interlocked.Increment &cancel_ct_registration_original |> ignore post (key, OriginatorCanceled)) |> saveRegistration key @@ -281,7 +299,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let ex = exn (message) failures.Add(key, ex) Interlocked.Increment &errors |> ignore - // raise ex -- Suppose there's no need to raise here - where does it even go? + // raise ex -- Suppose there's no need to raise here - where does it even go? let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = task { @@ -297,6 +315,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | OriginatorCanceled, Some(Running(tcs, cts, computation, _, _)) -> + Interlocked.Increment &cancel_original_processed |> ignore + decrRequestCount key if requestCounts[key] < 1 then @@ -321,6 +341,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T try // TODO: Should unify starting and restarting + use _ = Cancellable.UsingToken(cts.Token) log (Started, key) Interlocked.Increment &restarted |> ignore System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}" @@ -334,7 +355,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T finally DiagnosticsThreadStatics.DiagnosticsLogger <- currentLogger with - | :? OperationCanceledException -> + | TaskCancelled _ -> + Interlocked.Increment &cancel_exception_subsequent |> ignore post (key, CancelRequest) () | ex -> post (key, (JobFailed(ex, cachingLogger.CapturedDiagnostics))) @@ -345,6 +367,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | CancelRequest, Some(Running(tcs, cts, _c, _, _)) -> + Interlocked.Increment &cancel_subsequent_processed |> ignore + decrRequestCount key if requestCounts[key] < 1 then @@ -467,9 +491,10 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T Async.StartAsTask( async { // TODO: Should unify starting and restarting - log (Started, key) let currentLogger = DiagnosticsThreadStatics.DiagnosticsLogger DiagnosticsThreadStatics.DiagnosticsLogger <- cachingLogger + use _ = Cancellable.UsingToken(internalCt) + log (Started, key) try let! result = computation |> Async.AwaitNodeCode @@ -482,8 +507,16 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T ) |> NodeCode.AwaitTask with - | :? TaskCanceledException - | :? OperationCanceledException as ex -> return raise ex + | TaskCancelled ex -> + // TODO: do we need to do anything else here? Presumably it should be done by the registration on + // the cancellation token or before we triggered our own cancellation + + // Let's send this again just in case. It seems sometimes it's not triggered from the registration? + + Interlocked.Increment &cancel_exception_original |> ignore + + post (key, (OriginatorCanceled)) + return raise ex | ex -> post (key, (JobFailed(ex, cachingLogger.CapturedDiagnostics))) return raise ex @@ -567,3 +600,4 @@ type internal AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue when 'TKey: equalit computation member _.DebuggerDisplay = $"(disabled) requests: {requests}" + \ No newline at end of file diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 09db032279d..2944141da90 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -258,6 +258,8 @@ type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Project member _.SourceFileNames = sourceFileNames.Value + member _.Label = projectCore.Label + member _.IndexOf fileName = sourceFiles |> List.tryFindIndex (fun x -> x.FileName = fileName) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 358f436604b..2bbf2129be1 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -47,14 +47,6 @@ open Internal.Utilities.Hashing open FSharp.Compiler.CodeAnalysis.ProjectSnapshot open System.Runtime.Serialization.Formatters.Binary -//type internal FSharpFile = -// { -// Range: range -// Source: FSharpFileSnapshot -// IsLastCompiland: bool -// IsExe: bool -// } - /// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. [] type internal TcInfo = @@ -122,14 +114,6 @@ type internal BootstrapInfo = LastFileName: string } -//member this.GetFile fileName = -// this.SourceFiles -// |> List.tryFind (fun f -> f.Source.FileName = fileName) -// |> Option.defaultWith (fun _ -> -// failwith ( -// $"File {fileName} not found in project snapshot. Files in project: \n\n" -// + (this.SourceFiles |> Seq.map (fun f -> f.Source.FileName) |> String.concat " \n") -// )) type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string @@ -1556,34 +1540,42 @@ type internal TransparentCompiler caches.AssemblyData.Get( projectSnapshot.SignatureKey, node { - let availableOnDiskModifiedTime = - if FileSystem.FileExistsShim fileName then - Some <| FileSystem.GetLastWriteTimeShim fileName - else - None - - let shouldUseOnDisk = - availableOnDiskModifiedTime - |> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) - let name = projectSnapshot.ProjectFileName |> Path.GetFileNameWithoutExtension + try - if shouldUseOnDisk then - Trace.TraceInformation($"Using assembly on disk: {name}") - return ProjectAssemblyDataResult.Unavailable true - else - match! ComputeBootstrapInfo projectSnapshot with - | None, _ -> - Trace.TraceInformation($"Using assembly on disk (unintentionally): {name}") - return ProjectAssemblyDataResult.Unavailable true - | Some bootstrapInfo, _creationDiags -> + let availableOnDiskModifiedTime = + if FileSystem.FileExistsShim fileName then + Some <| FileSystem.GetLastWriteTimeShim fileName + else + None - let! snapshotWithSources = LoadSources bootstrapInfo projectSnapshot + let shouldUseOnDisk = + availableOnDiskModifiedTime + |> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) - let! _, _, assemblyDataResult, _ = ComputeProjectExtras bootstrapInfo snapshotWithSources - Trace.TraceInformation($"Using in-memory project reference: {name}") + let name = projectSnapshot.ProjectFileName |> Path.GetFileNameWithoutExtension - return assemblyDataResult + if shouldUseOnDisk then + Trace.TraceInformation($"Using assembly on disk: {name}") + return ProjectAssemblyDataResult.Unavailable true + else + match! ComputeBootstrapInfo projectSnapshot with + | None, _ -> + Trace.TraceInformation($"Using assembly on disk (unintentionally): {name}") + return ProjectAssemblyDataResult.Unavailable true + | Some bootstrapInfo, _creationDiags -> + + let! snapshotWithSources = LoadSources bootstrapInfo projectSnapshot + + let! _, _, assemblyDataResult, _ = ComputeProjectExtras bootstrapInfo snapshotWithSources + Trace.TraceInformation($"Using in-memory project reference: {name}") + + return assemblyDataResult + with + | TaskCancelled ex -> return raise ex + | ex -> + errorR(exn($"Error while computing assembly data for project {projectSnapshot.Label}: {ex}")) + return ProjectAssemblyDataResult.Unavailable true } ) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index ff6ebfecbc3..e4747d8739e 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -104,13 +104,13 @@ let ``We can cancel a job`` () = cts1.Cancel() cts2.Cancel() - jobStarted.WaitOne() |> ignore + //jobStarted.WaitOne() |> ignore - Assert.Equal<(JobEvent * int) array>([| Started, key; Started, key |], eventLog |> Seq.toArray ) + //Assert.Equal<(JobEvent * int) array>([| Started, key; Started, key |], eventLog |> Seq.toArray ) cts3.Cancel() - do! Task.Delay 100 + do! Task.Delay 100 Assert.Equal<(JobEvent * int) array>([| Started, key; Started, key; Canceled, key |], eventLog |> Seq.toArray ) } @@ -140,6 +140,54 @@ let ``Job is restarted if first requestor cancels`` () = let key = 1 let _task1 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts1.Token) + jobStarted.WaitOne() |> ignore + + let _task2 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts2.Token) + let _task3 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts3.Token) + + cts1.Cancel() + + do! Task.Delay 100 + cts3.Cancel() + + let! result = _task2 + Assert.Equal(2, result) + + Assert.Equal(TaskStatus.Canceled, _task1.Status) + + let orderedLog = eventLog |> Seq.sortBy fst |> Seq.map snd |> Seq.toList + let expected = [ Started, key; Started, key; Finished, key ] + + Assert.Equal<_ list>(expected, orderedLog) + } + +// [] - if we decide to enable that +let ``Job keeps running if the first requestor cancels`` () = + task { + let jobStarted = new ManualResetEvent(false) + + let computation key = node { + jobStarted.Set() |> ignore + + for _ in 1 .. 5 do + do! Async.Sleep 100 |> NodeCode.AwaitAsync + + return key * 2 + } + + let eventLog = ConcurrentBag() + let memoize = AsyncMemoize() + memoize.OnEvent(fun (e, (_label, k, _version)) -> eventLog.Add (DateTime.Now.Ticks, (e, k))) + + use cts1 = new CancellationTokenSource() + use cts2 = new CancellationTokenSource() + use cts3 = new CancellationTokenSource() + + let key = 1 + + let _task1 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts1.Token) + jobStarted.WaitOne() |> ignore + let _task2 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts2.Token) let _task3 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts3.Token) @@ -156,7 +204,7 @@ let ``Job is restarted if first requestor cancels`` () = Assert.Equal(TaskStatus.Canceled, _task1.Status) let orderedLog = eventLog |> Seq.sortBy fst |> Seq.map snd |> Seq.toList - let expected = [ Started, key; Started, key; Finished, key ] + let expected = [ Started, key; Finished, key ] Assert.Equal<_ list>(expected, orderedLog) } @@ -194,7 +242,7 @@ let ``Job is restarted if first requestor cancels but keeps running if second re cts1.Cancel() - jobStarted.WaitOne() |> ignore + //jobStarted.WaitOne() |> ignore cts2.Cancel() @@ -452,7 +500,7 @@ let ``Preserve thread static diagnostics`` () = [] -let ``Preserve thread static diagnostics already completed job`` () = +let ``Preserve thread static diagnostics already completed job`` () = let cache = AsyncMemoize() @@ -473,8 +521,8 @@ let ``Preserve thread static diagnostics already completed job`` () = use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Optimize) - let! _ = cache.Get(key, job "1" ) |> Async.AwaitNodeCode - let! _ = cache.Get(key, job "2" ) |> Async.AwaitNodeCode + let! _ = cache.Get(key, job "1" ) |> Async.AwaitNodeCode + let! _ = cache.Get(key, job "2" ) |> Async.AwaitNodeCode let diagnosticMessages = diagnosticsLogger.GetDiagnostics() |> Array.map (fun (d, _) -> d.Exception.Message) |> Array.toList @@ -485,7 +533,7 @@ let ``Preserve thread static diagnostics already completed job`` () = [] -let ``We get diagnostics from the job that failed`` () = +let ``We get diagnostics from the job that failed`` () = let cache = AsyncMemoize() @@ -501,7 +549,7 @@ let ``We get diagnostics from the job that failed`` () = return 5 } - let result = + let result = [1; 2] |> Seq.map (fun i -> async { @@ -520,11 +568,8 @@ let ``We get diagnostics from the job that failed`` () = |> Async.Parallel |> Async.StartAsTask |> (fun t -> t.Result) + |> Array.toList - Assert.Equal>([["job 1 error"]; ["job 1 error"]], result) - - -[] -let ``What if requestor cancels and their diagnosticsLogger gets disposed?``() = - failwith "TODO" - () \ No newline at end of file + Assert.True( + result = [["job 1 error"]; ["job 1 error"]] || + result = [["job 2 error"]; ["job 2 error"]] ) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 73293002580..9de8f277ffe 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -179,8 +179,6 @@ module private CheckerExtensions = let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) - let _json = dumpToJson snapshot - return snapshot } From 6447b39b1213c5c04ef086c8e0abd8b5ccd5797e Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 19 Dec 2023 18:57:30 +0100 Subject: [PATCH 176/222] fix fsproj --- src/Compiler/FSharp.Compiler.Service.fsproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 641db223232..1ab3319eb63 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -499,7 +499,7 @@ - + From f02c7a48d17ae0324f46f157fed132136f0b71f2 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 20 Dec 2023 15:52:59 +0100 Subject: [PATCH 177/222] BootrstrapInfoId --- src/Compiler/Service/TransparentCompiler.fs | 17 +++++++++--- src/Compiler/Utilities/Activity.fsi | 3 ++- .../FSharpChecker/TransparentCompiler.fs | 26 +++++++++++++++++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 2bbf2129be1..0206b7c29cb 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -97,6 +97,10 @@ type internal TcIntermediate = /// Things we need to start parsing and checking files for a given project snapshot type internal BootstrapInfo = { + // Each instance gets an Id on creation, unfortunately partial type check results using different instances are not compatible + // So if this needs to be recreated for whatever reason then we need to re type check all files + Id: int + AssemblyName: string OutFile: string TcConfig: TcConfig @@ -606,6 +610,8 @@ type internal TransparentCompiler tcConfigB, sourceFilesNew, loadClosureOpt + let mutable BootstrapInfoIdCounter = 0 + /// Bootstrap info that does not depend source files let ComputeBootstrapInfoStatic (projectSnapshot: ProjectCore, tcConfig: TcConfig, assemblyName: string, loadClosureOpt) = @@ -680,7 +686,9 @@ type internal TransparentCompiler importsInvalidatedByTypeProvider ) - return tcImports, tcGlobals, initialTcInfo, importsInvalidatedByTypeProvider + let bootstrapId = Interlocked.Increment &BootstrapInfoIdCounter + + return bootstrapId, tcImports, tcGlobals, initialTcInfo, importsInvalidatedByTypeProvider } ) @@ -729,7 +737,7 @@ type internal TransparentCompiler let tcConfig = TcConfig.Create(tcConfigB, validate = true) let outFile, _, assemblyName = tcConfigB.DecideNames sourceFiles - let! tcImports, tcGlobals, initialTcInfo, _importsInvalidatedByTypeProvider = + let! bootstrapId, tcImports, tcGlobals, initialTcInfo, _importsInvalidatedByTypeProvider = ComputeBootstrapInfoStatic(projectSnapshot.ProjectCore, tcConfig, assemblyName, loadClosureOpt) // Check for the existence of loaded sources and prepend them to the sources list if present. @@ -776,6 +784,7 @@ type internal TransparentCompiler return Some { + Id = bootstrapId AssemblyName = assemblyName OutFile = outFile TcConfig = tcConfig @@ -785,7 +794,7 @@ type internal TransparentCompiler LoadedSources = loadedSources LoadClosure = loadClosureOpt LastFileName = sourceFiles |> List.last - //ImportsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider + //ImportsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider } } @@ -1051,7 +1060,7 @@ type internal TransparentCompiler ignore dependencyGraph - let key = projectSnapshot.FileKey(index) + let key = projectSnapshot.FileKey(index).WithExtraVersion(bootstrapInfo.Id) let _label, _k, _version = key.GetLabel(), key.GetKey(), key.GetVersion() diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index afce0f3b554..98924a18432 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. namespace FSharp.Compiler.Diagnostics open System diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index c64b8832ead..3a828417710 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -464,6 +464,32 @@ let ``Multi-project`` signatureFiles = } +[] +let ``What happens if bootrstapInfoStatic needs to be recomputed`` () = + + let signatureFiles = true + + let giraffe = if signatureFiles then "giraffe-signatures" else "Giraffe" + let giraffeDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ giraffe ++ "src" ++ "Giraffe" + + let giraffeProject = SyntheticProject.CreateFromRealProject giraffeDir + let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } + + giraffeProject.Workflow { + updateFile "Helpers" (fun f -> { f with SignatureFile = Custom (f.SignatureFile.CustomText + "\n") }) + checkFile "EndpointRouting" expectOk + withChecker (fun checker -> + async { + checker.Caches.BootstrapInfoStatic.Clear() + checker.Caches.BootstrapInfo.Clear() + checker.Caches.FrameworkImports.Clear() + ignore checker + return () + }) + updateFile "Core" (fun f -> { f with SignatureFile = Custom (f.SignatureFile.CustomText + "\n") }) + checkFile "EndpointRouting" expectOk + } + type ProjectAction = Get | Modify of (SyntheticProject -> SyntheticProject) type ProjectModificaiton = Update of int | Add | Remove type ProjectRequest = ProjectAction * AsyncReplyChannel From 1bdf2ea5f6cc2d8f6953d85fc64e4cbd6b856ca6 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 20 Dec 2023 17:10:24 +0100 Subject: [PATCH 178/222] Yaml experiment --- azure-pipelines.yml | 12 ++++++++++-- src/Compiler/Utilities/Activity.fsi | 3 +-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8ee494eeed1..63491e21427 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -225,7 +225,7 @@ stages: regular: _experimental_flag: '' experimental_features: - _experimental_flag: '' + _experimental_flag: 1 steps: - checkout: self clean: true @@ -439,6 +439,10 @@ stages: vs_release: _configuration: Release _testKind: testVs + experimental_features: + _configuration: Release + _testKind: testCoreclr + ${{ if eq(variables['Build.Reason'], 'Flaky, disabled, was PullRequest') }}: inttests_release: _configuration: Release @@ -454,11 +458,15 @@ stages: # yes, this is miserable, but - https://github.com/dotnet/arcade/issues/13239 - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) env: + ${{ if eq(variables['Agent.JobName'], 'Job experimental_features') }}: + FSHARP_EXPERIMENTAL_FEATURES: 1 NativeToolsOnMachine: true displayName: Build / Test condition: ne(variables['_testKind'], 'testIntegration') - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) env: + ${{ if eq(variables['Agent.JobName'], 'Job experimental_features') }}: + FSHARP_EXPERIMENTAL_FEATURES: 1 NativeToolsOnMachine: true displayName: Build / Integration Test continueOnError: true @@ -605,7 +613,7 @@ stages: regular: _experimental_flag: '' experimental_features: - _experimental_flag: '' + _experimental_flag: 1 steps: - checkout: self clean: true diff --git a/src/Compiler/Utilities/Activity.fsi b/src/Compiler/Utilities/Activity.fsi index 98924a18432..afce0f3b554 100644 --- a/src/Compiler/Utilities/Activity.fsi +++ b/src/Compiler/Utilities/Activity.fsi @@ -1,5 +1,4 @@ - -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. namespace FSharp.Compiler.Diagnostics open System From 4c1a1f4ae1e78af52045983586c5720d7b622954 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 20 Dec 2023 17:50:58 +0100 Subject: [PATCH 179/222] Pipeline experiment --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 63491e21427..fb63fb64010 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -458,14 +458,14 @@ stages: # yes, this is miserable, but - https://github.com/dotnet/arcade/issues/13239 - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) env: - ${{ if eq(variables['Agent.JobName'], 'Job experimental_features') }}: + ${{ if eq(variables['Agent.JobName'], 'WindowsCompressedMetadata experimental_features') }}: FSHARP_EXPERIMENTAL_FEATURES: 1 NativeToolsOnMachine: true displayName: Build / Test condition: ne(variables['_testKind'], 'testIntegration') - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) env: - ${{ if eq(variables['Agent.JobName'], 'Job experimental_features') }}: + ${{ if eq(variables['Agent.JobName'], 'WindowsCompressedMetadata experimental_features') }}: FSHARP_EXPERIMENTAL_FEATURES: 1 NativeToolsOnMachine: true displayName: Build / Integration Test From a5c6205a45f1b1e38c49ccb32f5b7481098893e4 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 20 Dec 2023 17:52:40 +0100 Subject: [PATCH 180/222] Skip Giraffe tests if there's no Giraffe --- .../FSharpChecker/TransparentCompiler.fs | 219 ++++-------------- 1 file changed, 41 insertions(+), 178 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 3a828417710..1a18dcbe6c1 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -464,31 +464,6 @@ let ``Multi-project`` signatureFiles = } -[] -let ``What happens if bootrstapInfoStatic needs to be recomputed`` () = - - let signatureFiles = true - - let giraffe = if signatureFiles then "giraffe-signatures" else "Giraffe" - let giraffeDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ giraffe ++ "src" ++ "Giraffe" - - let giraffeProject = SyntheticProject.CreateFromRealProject giraffeDir - let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } - - giraffeProject.Workflow { - updateFile "Helpers" (fun f -> { f with SignatureFile = Custom (f.SignatureFile.CustomText + "\n") }) - checkFile "EndpointRouting" expectOk - withChecker (fun checker -> - async { - checker.Caches.BootstrapInfoStatic.Clear() - checker.Caches.BootstrapInfo.Clear() - checker.Caches.FrameworkImports.Clear() - ignore checker - return () - }) - updateFile "Core" (fun f -> { f with SignatureFile = Custom (f.SignatureFile.CustomText + "\n") }) - checkFile "EndpointRouting" expectOk - } type ProjectAction = Get | Modify of (SyntheticProject -> SyntheticProject) type ProjectModificaiton = Update of int | Add | Remove @@ -714,165 +689,30 @@ let Fuzzing signatureFiles = fuzzingTest seed initialProject -let Fuzzing' signatureFiles = - //let seed = System.Random().Next() - let seed = 1106087513 - let rng = System.Random(int seed) - - let fileCount = 30 - let maxDepsPerFile = 3 - let checkingThreads = 20 - let maxModificationDelayMs = 50 - let maxCheckingDelayMs = 5 - let runTimeMs = 1000 - let signatureFileModificationProbability = 0.25 - - let fileName i = sprintf $"F%03d{i}" - - //let extraCode = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ "src" ++ "Compiler" ++ "Utilities" ++ "EditDistance.fs" |> File.ReadAllLines |> Seq.skip 5 |> String.concat "\n" - let extraCode = "" - - let files = - [| for i in 1 .. fileCount do - let name = fileName i - let deps = [ - for _ in 1 .. maxDepsPerFile do - if i > 1 then - fileName <| rng.Next(1, i) ] - let signature = - match signatureFiles with - | SignatureFiles.Yes -> AutoGenerated - | SignatureFiles.Some when rng.NextDouble() < 0.5 -> AutoGenerated - | _ -> No - - { sourceFile name deps - with - SignatureFile = signature - ExtraSource = extraCode } - |] - - let initialProject = SyntheticProject.Create("TCFuzzing", files) - - let builder = ProjectWorkflowBuilder(initialProject, useTransparentCompiler = true, autoStart = false) - let checker = builder.Checker - - let initialProject = initialProject |> absorbAutoGeneratedSignatures checker |> Async.RunSynchronously - - let projectAgent = MailboxProcessor.Start(fun (inbox: MailboxProcessor) -> - let rec loop project = - async { - let! action, reply = inbox.Receive() - let! project = - match action with - | Modify f -> async { - let p = f project - do! saveProject p false checker - return p } - | Get -> async.Return project - reply.Reply project - return! loop project - } - loop initialProject) - - let getProject () = - projectAgent.PostAndAsyncReply(pair Get) - - let modifyProject f = - projectAgent.PostAndAsyncReply(pair(Modify f)) |> Async.Ignore - - let modificationProbabilities = [ - Update 1, 80 - Update 2, 5 - Update 10, 5 - Add, 2 - Remove, 1 - ] - - let modificationPicker = [| - for op, prob in modificationProbabilities do - for _ in 1 .. prob do - op - |] - - let addComment s = $"{s}\n\n// {rng.NextDouble()}" - let modifyImplFile f = { f with ExtraSource = f.ExtraSource |> addComment } - let modifySigFile f = { f with SignatureFile = Custom (f.SignatureFile.CustomText |> addComment) } - - let getRandomModification () = modificationPicker[rng.Next(0, modificationPicker.Length)] +let reposDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." +let giraffeDir = reposDir ++ "Giraffe" ++ "src" ++ "Giraffe" |> Path.GetFullPath +let giraffeTestsDir = reposDir ++ "Giraffe" ++ "tests" ++ "Giraffe.Tests" |> Path.GetFullPath +let giraffeSignaturesDir = reposDir ++ "giraffe-signatures" ++ "src" ++ "Giraffe" |> Path.GetFullPath +let giraffeSignaturesTestsDir = reposDir ++ "giraffe-signatures" ++ "tests" ++ "Giraffe.Tests" |> Path.GetFullPath - let getRandomFile (project: SyntheticProject) = project.SourceFiles[rng.Next(0, project.SourceFiles.Length)].Id - let log = ConcurrentBag() +type GiraffeTheoryAttribute() = + inherit Xunit.TheoryAttribute() + do + if not (Directory.Exists giraffeDir) then + do base.Skip <- $"Giraffe not found ({giraffeDir}). You can get it here: https://github.com/giraffe-fsharp/Giraffe" + if not (Directory.Exists giraffeSignaturesDir) then + do base.Skip <- $"Giraffe (with signatures) not found ({giraffeSignaturesDir}). You can get it here: https://github.com/nojaf/Giraffe/tree/signatures" - let modificationLoop = async { - while true do - do! Async.Sleep (rng.Next maxModificationDelayMs) - let modify project = - match getRandomModification() with - | Update n -> - let files = Set [ for _ in 1..n -> getRandomFile project ] - (project, files) - ||> Seq.fold (fun p fileId -> - let project, file = project.FindInAllProjects fileId - let opName, f = - if file.HasSignatureFile && rng.NextDouble() < signatureFileModificationProbability - then nameof modifySigFile, modifySigFile - else nameof modifyImplFile, modifyImplFile - log.Add $"{project.Name} -> {fileId} |> {opName}" - p |> updateFileInAnyProject fileId f) - | Add - | Remove -> - // TODO: - project - do! modifyProject modify - } - - let checkingLoop = async { - while true do - let! project = getProject() - let file = project |> getRandomFile - - // TODO: timeout & cancelation - log.Add $"Started checking {file}" - let! result = checker |> checkFile file project - - log.Add $"Checked {file} %A{snd result}" - expectOk result () - - do! Async.Sleep (rng.Next maxCheckingDelayMs) - } - - async { - let! threads = - seq { - Async.StartChild(modificationLoop, runTimeMs) - ignore modificationLoop - for _ in 1..checkingThreads do - Async.StartChild(checkingLoop, runTimeMs) - } - |> Async.Parallel - try - do! threads |> Seq.skip 1 |> Async.Parallel |> Async.Ignore - with - | :? TimeoutException - | :? TaskCanceledException -> () - | :? AggregateException as e when e.InnerExceptions |> Seq.exists (fun e -> e :? TaskCanceledException) -> () - | e -> failwith $"Seed: {seed}\nException: %A{e}" - } |> Async.RunSynchronously - - builder.DeleteProjectDir() - - -[] +[] [] [] let GiraffeFuzzing signatureFiles = let seed = System.Random().Next() //let seed = 1044159179 - let giraffe = if signatureFiles then "giraffe-signatures" else "Giraffe" - let giraffeDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ giraffe ++ "src" ++ "Giraffe" - let giraffeTestsDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ giraffe ++ "tests" ++ "Giraffe.Tests" + let giraffeDir = if signatureFiles then giraffeSignaturesDir else giraffeDir + let giraffeTestsDir = if signatureFiles then giraffeSignaturesTestsDir else giraffeTestsDir let giraffeProject = SyntheticProject.CreateFromRealProject giraffeDir let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } @@ -889,12 +729,12 @@ let GiraffeFuzzing signatureFiles = fuzzingTest seed testsProject -[] + +[] [] [] let ``File moving test`` signatureFiles = - let giraffe = if signatureFiles then "giraffe-signatures" else "Giraffe" - let giraffeDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." ++ giraffe ++ "src" ++ "Giraffe" + let giraffeDir = if signatureFiles then giraffeSignaturesDir else giraffeDir let giraffeProject = SyntheticProject.CreateFromRealProject giraffeDir let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } @@ -906,6 +746,29 @@ let ``File moving test`` signatureFiles = } +[] +[] +let ``What happens if bootrstapInfoStatic needs to be recomputed`` _ = + + let giraffeProject = SyntheticProject.CreateFromRealProject giraffeSignaturesDir + let giraffeProject = { giraffeProject with OtherOptions = "--nowarn:FS3520"::giraffeProject.OtherOptions } + + giraffeProject.Workflow { + updateFile "Helpers" (fun f -> { f with SignatureFile = Custom (f.SignatureFile.CustomText + "\n") }) + checkFile "EndpointRouting" expectOk + withChecker (fun checker -> + async { + checker.Caches.BootstrapInfoStatic.Clear() + checker.Caches.BootstrapInfo.Clear() + checker.Caches.FrameworkImports.Clear() + ignore checker + return () + }) + updateFile "Core" (fun f -> { f with SignatureFile = Custom (f.SignatureFile.CustomText + "\n") }) + checkFile "EndpointRouting" expectOk + } + + module ParsedInputHashing = let source = """ From a786ef1ca3c47675deaec4861e45ebaad151f8d3 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 20 Dec 2023 17:53:06 +0100 Subject: [PATCH 181/222] Choose to use transparent compiler in tests based on experimental env variable --- tests/FSharp.Test.Utilities/CompilerAssert.fs | 4 ++-- tests/FSharp.Test.Utilities/ProjectGeneration.fs | 2 +- tests/service/Common.fs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index 2bc898197fa..7951ba31d5d 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -259,8 +259,8 @@ and Compilation = module rec CompilerAssertHelpers = - // TODO: check both TransparentCompiler and IncrementalBuilder, somehow - let checker = FSharpChecker.Create(suggestNamesForErrors=true, useTransparentCompiler=true) + let useTransparentCompiler = FSharp.Compiler.CompilerConfig.FSharpExperimentalFeaturesEnabledAutomatically + let checker = FSharpChecker.Create(suggestNamesForErrors=true, useTransparentCompiler=useTransparentCompiler) // Unlike C# whose entrypoint is always string[] F# can make an entrypoint with 0 args, or with an array of string[] let mkDefaultArgs (entryPoint:MethodBase) : obj[] = [| diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 16a33735b05..9ecc10f5b26 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -837,7 +837,7 @@ type ProjectWorkflowBuilder ?autoStart ) = - let useTransparentCompiler = defaultArg useTransparentCompiler true + let useTransparentCompiler = defaultArg useTransparentCompiler FSharp.Compiler.CompilerConfig.FSharpExperimentalFeaturesEnabledAutomatically let useGetSource = not useTransparentCompiler && defaultArg useGetSource false let useChangeNotifications = not useTransparentCompiler && defaultArg useChangeNotifications false let autoStart = defaultArg autoStart true diff --git a/tests/service/Common.fs b/tests/service/Common.fs index ec332a4a2c3..9a1d9632ffe 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -31,7 +31,7 @@ type Async with task.Result // Create one global interactive checker instance -let checker = FSharpChecker.Create(useTransparentCompiler=true) // TODO: tests for both versions +let checker = FSharpChecker.Create(useTransparentCompiler=FSharp.Compiler.CompilerConfig.FSharpExperimentalFeaturesEnabledAutomatically) type TempFile(ext, contents: string) = let tmpFile = Path.ChangeExtension(tryCreateTemporaryFileName (), ext) From d41b064569e4b813a250f1bc4f23818222ab986e Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 20 Dec 2023 19:19:09 +0100 Subject: [PATCH 182/222] pipeline experiment --- azure-pipelines.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fb63fb64010..0aab232632d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -458,15 +458,18 @@ stages: # yes, this is miserable, but - https://github.com/dotnet/arcade/issues/13239 - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) env: - ${{ if eq(variables['Agent.JobName'], 'WindowsCompressedMetadata experimental_features') }}: + ${{ if eq(variables['System.JobName'], 'experimental_features') }}: FSHARP_EXPERIMENTAL_FEATURES: 1 + TEST_POTATO: $(System.JobName) + TEST_POTATO_2: $(Agent.JobName) + TEST_POTATO_3: $(System_JobName) + TEST_POTATO_4: $(Agent_JobName) NativeToolsOnMachine: true displayName: Build / Test condition: ne(variables['_testKind'], 'testIntegration') - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) env: - ${{ if eq(variables['Agent.JobName'], 'WindowsCompressedMetadata experimental_features') }}: - FSHARP_EXPERIMENTAL_FEATURES: 1 + FSHARP_EXPERIMENTAL_FEATURES: 1 NativeToolsOnMachine: true displayName: Build / Integration Test continueOnError: true From cad39a6d78d1efdfc02d6a54e1571ccf964bd3a4 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 21 Dec 2023 13:53:56 +0100 Subject: [PATCH 183/222] BackgroundCompiler ParseFile from snapshot --- src/Compiler/Service/BackgroundCompiler.fs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 3fe91c32f2b..6d5602cbfcc 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -1687,8 +1687,9 @@ type internal BackgroundCompiler ) = self.ParseFile(fileName, sourceText, options, cache, flatErrors, userOpName) - member _.ParseFile(_fileName: string, _projectSnapshot: FSharpProjectSnapshot, _userOpName: string) = - raise (System.NotImplementedException()) + member _.ParseFile(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = + let options = projectSnapshot.ToOptions() + self.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName) |> Async.AwaitNodeCode member _.ProjectChecked: IEvent = self.ProjectChecked From 4f8749419aa68d2b677f4af26dab291a7e288b75 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 21 Dec 2023 14:06:30 +0100 Subject: [PATCH 184/222] another yaml experiment --- azure-pipelines.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0aab232632d..6313a696b8d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -458,15 +458,17 @@ stages: # yes, this is miserable, but - https://github.com/dotnet/arcade/issues/13239 - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) env: - ${{ if eq(variables['System.JobName'], 'experimental_features') }}: - FSHARP_EXPERIMENTAL_FEATURES: 1 - TEST_POTATO: $(System.JobName) - TEST_POTATO_2: $(Agent.JobName) - TEST_POTATO_3: $(System_JobName) - TEST_POTATO_4: $(Agent_JobName) NativeToolsOnMachine: true displayName: Build / Test - condition: ne(variables['_testKind'], 'testIntegration') + condition: and( ne(variables['_testKind'], 'testIntegration'), ne(variables['System.JobName'], 'experimental_features') ) + + - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) + env: + FSHARP_EXPERIMENTAL_FEATURES: 1 + NativeToolsOnMachine: true + displayName: Build / Test Experimental Features + condition: and( eq(variables['System.JobName'], 'experimental_features'), ne(variables['_testKind'], 'testIntegration') ) + - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) env: FSHARP_EXPERIMENTAL_FEATURES: 1 From 22ddc6febd0717668cc3ff7c3fd7b272333ba129 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 21 Dec 2023 15:43:19 +0100 Subject: [PATCH 185/222] Proper diagnostics handler for type checks --- src/Compiler/Service/FSharpCheckerResults.fs | 3 ++ src/Compiler/Service/FSharpCheckerResults.fsi | 26 +++++++++++ src/Compiler/Service/FSharpProjectSnapshot.fs | 3 +- src/Compiler/Service/TransparentCompiler.fs | 45 +++++++++++++++---- .../ConstraintSolver/ObjInference.fs | 5 +-- 5 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 991052c3c06..7f1a61c0334 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -2520,6 +2520,9 @@ module internal ParseAndCheckFile = member _.AnyErrors = errorCount > 0 + member _.CollectedPhasedDiagnostics = + [| for struct (diagnostic, severity) in diagnosticsCollector -> diagnostic, severity |] + member _.CollectedDiagnostics(symbolEnv: SymbolEnv option) = [| for struct (diagnostic, severity) in diagnosticsCollector do diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 081aeac9153..a3954d485b3 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -573,6 +573,31 @@ module internal ParseAndCheckFile = ct: CancellationToken -> (range * range)[] + + /// Diagnostics handler for parsing & type checking while processing a single file + type DiagnosticsHandler = + new: + reportErrors: bool * + mainInputFileName: string * + diagnosticsOptions: FSharpDiagnosticOptions * + sourceText: ISourceText * + suggestNamesForErrors: bool * + flatErrors: bool -> + DiagnosticsHandler + + member DiagnosticsLogger: DiagnosticsLogger + + member ErrorCount: int + + member DiagnosticOptions: FSharpDiagnosticOptions with set + + member AnyErrors: bool + + member CollectedPhasedDiagnostics: (PhasedDiagnostic * FSharpDiagnosticSeverity) array + + member CollectedDiagnostics: symbolEnv: SymbolEnv option -> FSharpDiagnostic array + + // An object to typecheck source in a given typechecking environment. // Used internally to provide intellisense over F# Interactive. type internal FsiInteractiveChecker = @@ -586,3 +611,4 @@ type internal FsiInteractiveChecker = module internal FSharpCheckerResultsSettings = val defaultFSharpBinariesDir: string + diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 2944141da90..d4349a00a59 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -113,9 +113,10 @@ type FSharpFileSnapshotWithSource member this.IsSignatureFile = this.IsSignatureFile type FSharpParsedFile - (FileName: string, SyntaxTreeHash: byte array, ParsedInput: ParsedInput, ParseErrors: (PhasedDiagnostic * FSharpDiagnosticSeverity)[]) = + (FileName: string, SyntaxTreeHash: byte array, SourceText: ISourceText, ParsedInput: ParsedInput, ParseErrors: (PhasedDiagnostic * FSharpDiagnosticSeverity)[]) = member _.FileName = FileName + member _.SourceText = SourceText member _.ParsedInput = ParsedInput member _.ParseErrors = ParseErrors diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 0206b7c29cb..59009aab691 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -914,7 +914,7 @@ type internal TransparentCompiler fileParsed.Trigger(fileName, Unchecked.defaultof<_>) - return FSharpParsedFile(fileName, inputHash, input, diagnosticsLogger.GetDiagnostics()) + return FSharpParsedFile(fileName, inputHash, sourceText, input, diagnosticsLogger.GetDiagnostics()) } ) @@ -1088,17 +1088,44 @@ type internal TransparentCompiler let tcGlobals = bootstrapInfo.TcGlobals let tcImports = bootstrapInfo.TcImports - let capturingDiagnosticsLogger = CapturingDiagnosticsLogger("TypeCheck") + let mainInputFileName = file.FileName + let sourceText = file.SourceText + let parsedMainInput = file.ParsedInput - let diagnosticsLogger = - GetDiagnosticsLoggerFilteringByScopedPragmas( - false, - input.ScopedPragmas, + // Initialize the error handler + let errHandler = + ParseAndCheckFile.DiagnosticsHandler( + true, + mainInputFileName, tcConfig.diagnosticsOptions, - capturingDiagnosticsLogger + sourceText, + suggestNamesForErrors, + tcConfig.flatErrors ) - use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.TypeCheck) + use _ = new CompilationGlobalsScope(errHandler.DiagnosticsLogger, BuildPhase.TypeCheck) + + // Apply nowarns to tcConfig (may generate errors, so ensure diagnosticsLogger is installed) + let tcConfig = + ApplyNoWarnsToTcConfig(tcConfig, parsedMainInput, Path.GetDirectoryName mainInputFileName) + + // update the error handler with the modified tcConfig + errHandler.DiagnosticOptions <- tcConfig.diagnosticsOptions + + let diagnosticsLogger = errHandler.DiagnosticsLogger + + + //let capturingDiagnosticsLogger = CapturingDiagnosticsLogger("TypeCheck") + + //let diagnosticsLogger = + // GetDiagnosticsLoggerFilteringByScopedPragmas( + // false, + // input.ScopedPragmas, + // tcConfig.diagnosticsOptions, + // capturingDiagnosticsLogger + // ) + + //use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.TypeCheck) //beforeFileChecked.Trigger fileName @@ -1135,7 +1162,7 @@ type internal TransparentCompiler //fileChecked.Trigger fileName let newErrors = - Array.append file.ParseErrors (capturingDiagnosticsLogger.Diagnostics |> List.toArray) + Array.append file.ParseErrors (errHandler.CollectedPhasedDiagnostics) fileChecked.Trigger(fileName, Unchecked.defaultof<_>) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index 2e7abce968a..32950d8ba4c 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -40,7 +40,7 @@ let x = deserialize "" |> f""", 3, 9, 3, 28 |> withLangVersion80 |> typecheck |> shouldFail - |> withSingleDiagnostic (Warning 3559, Line line1, Col col1, Line line2, Col col2, message) + |> withSingleDiagnostic (Information 3559, Line line1, Col col1, Line line2, Col col2, message) let quotableNoWarningCases = [ @@ -116,8 +116,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere |> withLangVersion80 |> typecheck |> shouldFail - // Informational become warning if explicitly on and not explicitly off - |> withSingleDiagnostic (Warning 3559, Line line1, Col (col1 + 3), Line line2, Col (col2 + 3), message) + |> withSingleDiagnostic (Information 3559, Line line1, Col (col1 + 3), Line line2, Col (col2 + 3), message) [] [] From 56725204ee291548085f33c5b42b6815e290e66a Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 21 Dec 2023 15:43:47 +0100 Subject: [PATCH 186/222] f --- src/Compiler/Facilities/AsyncMemoize.fs | 22 +++++++++---------- src/Compiler/Service/BackgroundCompiler.fs | 4 +++- src/Compiler/Service/FSharpCheckerResults.fs | 4 +++- src/Compiler/Service/FSharpCheckerResults.fsi | 13 +++++------ src/Compiler/Service/FSharpProjectSnapshot.fs | 8 ++++++- src/Compiler/Service/TransparentCompiler.fs | 9 ++++---- 6 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 4907c3580c0..25450de46b9 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -30,15 +30,15 @@ module internal Utils = let replayDiagnostics (logger: DiagnosticsLogger) = Seq.iter ((<|) logger.DiagnosticSink) - let (|TaskCancelled|_|) (ex : exn) = + let (|TaskCancelled|_|) (ex: exn) = match ex with - | :? System.Threading.Tasks.TaskCanceledException as tce -> Some tce - //| :? System.AggregateException as ae -> - // if ae.InnerExceptions |> Seq.forall (fun e -> e :? System.Threading.Tasks.TaskCanceledException) then - // ae.InnerExceptions |> Seq.tryHead |> Option.map (fun e -> e :?> System.Threading.Tasks.TaskCanceledException) - // else - // None - | _ -> None + | :? System.Threading.Tasks.TaskCanceledException as tce -> Some tce + //| :? System.AggregateException as ae -> + // if ae.InnerExceptions |> Seq.forall (fun e -> e :? System.Threading.Tasks.TaskCanceledException) then + // ae.InnerExceptions |> Seq.tryHead |> Option.map (fun e -> e :?> System.Threading.Tasks.TaskCanceledException) + // else + // None + | _ -> None type internal StateUpdate<'TValue> = | CancelRequest @@ -50,8 +50,7 @@ type internal MemoizeReply<'TValue> = | New of CancellationToken | Existing of Task<'TValue> -type internal MemoizeRequest<'TValue> = - | GetOrCompute of NodeCode<'TValue> * CancellationToken +type internal MemoizeRequest<'TValue> = GetOrCompute of NodeCode<'TValue> * CancellationToken [] type internal Job<'TValue> = @@ -299,7 +298,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let ex = exn (message) failures.Add(key, ex) Interlocked.Increment &errors |> ignore - // raise ex -- Suppose there's no need to raise here - where does it even go? + // raise ex -- Suppose there's no need to raise here - where does it even go? let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = task { @@ -600,4 +599,3 @@ type internal AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue when 'TKey: equalit computation member _.DebuggerDisplay = $"(disabled) requests: {requests}" - \ No newline at end of file diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 6d5602cbfcc..3866f8651c7 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -1689,7 +1689,9 @@ type internal BackgroundCompiler member _.ParseFile(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = let options = projectSnapshot.ToOptions() - self.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName) |> Async.AwaitNodeCode + + self.GetBackgroundParseResultsForFileInProject(fileName, options, userOpName) + |> Async.AwaitNodeCode member _.ProjectChecked: IEvent = self.ProjectChecked diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 7f1a61c0334..a293a2a5b7e 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -2521,7 +2521,9 @@ module internal ParseAndCheckFile = member _.AnyErrors = errorCount > 0 member _.CollectedPhasedDiagnostics = - [| for struct (diagnostic, severity) in diagnosticsCollector -> diagnostic, severity |] + [| + for struct (diagnostic, severity) in diagnosticsCollector -> diagnostic, severity + |] member _.CollectedDiagnostics(symbolEnv: SymbolEnv option) = [| diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index a3954d485b3..26781c4356e 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -573,18 +573,17 @@ module internal ParseAndCheckFile = ct: CancellationToken -> (range * range)[] - /// Diagnostics handler for parsing & type checking while processing a single file type DiagnosticsHandler = new: - reportErrors: bool * - mainInputFileName: string * - diagnosticsOptions: FSharpDiagnosticOptions * + reportErrors: bool * + mainInputFileName: string * + diagnosticsOptions: FSharpDiagnosticOptions * sourceText: ISourceText * suggestNamesForErrors: bool * - flatErrors: bool -> + flatErrors: bool -> DiagnosticsHandler - + member DiagnosticsLogger: DiagnosticsLogger member ErrorCount: int @@ -596,7 +595,6 @@ module internal ParseAndCheckFile = member CollectedPhasedDiagnostics: (PhasedDiagnostic * FSharpDiagnosticSeverity) array member CollectedDiagnostics: symbolEnv: SymbolEnv option -> FSharpDiagnostic array - // An object to typecheck source in a given typechecking environment. // Used internally to provide intellisense over F# Interactive. @@ -611,4 +609,3 @@ type internal FsiInteractiveChecker = module internal FSharpCheckerResultsSettings = val defaultFSharpBinariesDir: string - diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index d4349a00a59..ba1b5f15cc2 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -113,7 +113,13 @@ type FSharpFileSnapshotWithSource member this.IsSignatureFile = this.IsSignatureFile type FSharpParsedFile - (FileName: string, SyntaxTreeHash: byte array, SourceText: ISourceText, ParsedInput: ParsedInput, ParseErrors: (PhasedDiagnostic * FSharpDiagnosticSeverity)[]) = + ( + FileName: string, + SyntaxTreeHash: byte array, + SourceText: ISourceText, + ParsedInput: ParsedInput, + ParseErrors: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] + ) = member _.FileName = FileName member _.SourceText = SourceText diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 59009aab691..8c9bf67f8f2 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -118,7 +118,6 @@ type internal BootstrapInfo = LastFileName: string } - type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string [] @@ -794,7 +793,7 @@ type internal TransparentCompiler LoadedSources = loadedSources LoadClosure = loadClosureOpt LastFileName = sourceFiles |> List.last - //ImportsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider + //ImportsInvalidatedByTypeProvider = importsInvalidatedByTypeProvider } } @@ -1103,7 +1102,8 @@ type internal TransparentCompiler tcConfig.flatErrors ) - use _ = new CompilationGlobalsScope(errHandler.DiagnosticsLogger, BuildPhase.TypeCheck) + use _ = + new CompilationGlobalsScope(errHandler.DiagnosticsLogger, BuildPhase.TypeCheck) // Apply nowarns to tcConfig (may generate errors, so ensure diagnosticsLogger is installed) let tcConfig = @@ -1114,7 +1114,6 @@ type internal TransparentCompiler let diagnosticsLogger = errHandler.DiagnosticsLogger - //let capturingDiagnosticsLogger = CapturingDiagnosticsLogger("TypeCheck") //let diagnosticsLogger = @@ -1610,7 +1609,7 @@ type internal TransparentCompiler with | TaskCancelled ex -> return raise ex | ex -> - errorR(exn($"Error while computing assembly data for project {projectSnapshot.Label}: {ex}")) + errorR (exn ($"Error while computing assembly data for project {projectSnapshot.Label}: {ex}")) return ProjectAssemblyDataResult.Unavailable true } ) From 1119434aaf17e3c312b8c0672d7b4079e41cf50f Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 21 Dec 2023 16:04:03 +0100 Subject: [PATCH 187/222] remove failwith --- src/Compiler/TypedTree/TypedTreePickle.fs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Compiler/TypedTree/TypedTreePickle.fs b/src/Compiler/TypedTree/TypedTreePickle.fs index dd0e837f7d4..b5b738f8ada 100644 --- a/src/Compiler/TypedTree/TypedTreePickle.fs +++ b/src/Compiler/TypedTree/TypedTreePickle.fs @@ -762,7 +762,6 @@ let check (ilscope: ILScopeRef) (inMap: NodeInTable<_, _>) = let n = inMap.Get i if not (inMap.IsLinked n) then warning(Error(FSComp.SR.pickleMissingDefinition (i, inMap.Name, ilscope.QualifiedName), range0)) - failwith "It's broken" // Note for compiler developers: to get information about which item this index relates to, // enable the conditional in Pickle.p_osgn_ref to refer to the given index number and recompile // an identical copy of the source for the DLL containing the data being unpickled. A message will From b0afc6e3b7a4ed9c374d096d4c000b1ff341fe43 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 21 Dec 2023 18:42:56 +0100 Subject: [PATCH 188/222] fix test --- .../CompilerService/AsyncMemoize.fs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index e4747d8739e..9d5fc85ae43 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -103,11 +103,9 @@ let ``We can cancel a job`` () = cts1.Cancel() cts2.Cancel() - - //jobStarted.WaitOne() |> ignore - - //Assert.Equal<(JobEvent * int) array>([| Started, key; Started, key |], eventLog |> Seq.toArray ) - + + do! Task.Delay 100 + cts3.Cancel() do! Task.Delay 100 From a21cf8af5e91c177766a4a67f2e2da20f3f5481b Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 22 Dec 2023 12:57:04 +0100 Subject: [PATCH 189/222] apply nowarns --- src/Compiler/Service/TransparentCompiler.fs | 8 ++++++-- .../FSharpChecker/TransparentCompiler.fs | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 8c9bf67f8f2..6093141b917 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1389,13 +1389,17 @@ type internal TransparentCompiler let tcDependencyFiles = [] // TODO add as a set to TcIntermediate - let diagnosticsOptions = bootstrapInfo.TcConfig.diagnosticsOptions - // TODO: Apparently creating diagnostics can produce further diagnostics. So let's capture those too. Hopefully there is a more elegant solution... // Probably diagnostics need to be evaluated during typecheck anyway for proper formatting, which might take care of this too. let extraLogger = CapturingDiagnosticsLogger("DiagnosticsWhileCreatingDiagnostics") use _ = new CompilationGlobalsScope(extraLogger, BuildPhase.TypeCheck) + // Apply nowarns to tcConfig (may generate errors, so ensure diagnosticsLogger is installed) + let tcConfig = + ApplyNoWarnsToTcConfig(bootstrapInfo.TcConfig, parseResults.ParseTree, Path.GetDirectoryName fileName) + + let diagnosticsOptions = tcConfig.diagnosticsOptions + let symbolEnv = SymbolEnv(bootstrapInfo.TcGlobals, tcState.Ccu, Some tcState.CcuSig, bootstrapInfo.TcImports) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 1a18dcbe6c1..549bb354b4b 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -679,7 +679,7 @@ let Fuzzing signatureFiles = ExtraSource = extraCode } |] - let initialProject = SyntheticProject.Create("TCFuzzing", files) + let initialProject = SyntheticProject.Create(files) let builder = ProjectWorkflowBuilder(initialProject, useTransparentCompiler = true, autoStart = false) let checker = builder.Checker @@ -698,7 +698,7 @@ let giraffeSignaturesTestsDir = reposDir ++ "giraffe-signatures" ++ "tests" ++ " type GiraffeTheoryAttribute() = inherit Xunit.TheoryAttribute() - do + do if not (Directory.Exists giraffeDir) then do base.Skip <- $"Giraffe not found ({giraffeDir}). You can get it here: https://github.com/giraffe-fsharp/Giraffe" if not (Directory.Exists giraffeSignaturesDir) then From 7e46a238d4a09c846f814e14c8493c95cd283364 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 22 Dec 2023 14:52:51 +0100 Subject: [PATCH 190/222] disable experimental build --- azure-pipelines.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6313a696b8d..b517ac46dcf 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -225,7 +225,7 @@ stages: regular: _experimental_flag: '' experimental_features: - _experimental_flag: 1 + _experimental_flag: '' steps: - checkout: self clean: true @@ -439,9 +439,9 @@ stages: vs_release: _configuration: Release _testKind: testVs - experimental_features: - _configuration: Release - _testKind: testCoreclr + # experimental_features: + # _configuration: Release + # _testKind: testCoreclr ${{ if eq(variables['Build.Reason'], 'Flaky, disabled, was PullRequest') }}: inttests_release: @@ -461,7 +461,7 @@ stages: NativeToolsOnMachine: true displayName: Build / Test condition: and( ne(variables['_testKind'], 'testIntegration'), ne(variables['System.JobName'], 'experimental_features') ) - + - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) env: FSHARP_EXPERIMENTAL_FEATURES: 1 @@ -471,7 +471,6 @@ stages: - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) env: - FSHARP_EXPERIMENTAL_FEATURES: 1 NativeToolsOnMachine: true displayName: Build / Integration Test continueOnError: true @@ -618,7 +617,7 @@ stages: regular: _experimental_flag: '' experimental_features: - _experimental_flag: 1 + _experimental_flag: '' steps: - checkout: self clean: true From b02eadd1158d973cbcd9d4103e75eda1124c61b2 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 22 Dec 2023 17:47:54 +0100 Subject: [PATCH 191/222] fix test --- tests/FSharp.Test.Utilities/CompilerAssert.fs | 2 +- tests/service/Common.fs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index 7951ba31d5d..b6bb6910fcf 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -259,7 +259,7 @@ and Compilation = module rec CompilerAssertHelpers = - let useTransparentCompiler = FSharp.Compiler.CompilerConfig.FSharpExperimentalFeaturesEnabledAutomatically + let useTransparentCompiler = false//FSharp.Compiler.CompilerConfig.FSharpExperimentalFeaturesEnabledAutomatically let checker = FSharpChecker.Create(suggestNamesForErrors=true, useTransparentCompiler=useTransparentCompiler) // Unlike C# whose entrypoint is always string[] F# can make an entrypoint with 0 args, or with an array of string[] diff --git a/tests/service/Common.fs b/tests/service/Common.fs index 9a1d9632ffe..8516948626a 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -137,7 +137,7 @@ let mkTestFileAndOptions source additionalArgs = let fileSource1 = "module M" FileSystem.OpenFileForWriteShim(fileName).Write(fileSource1) - let args = Array.append (mkProjectCommandLineArgs (dllName, [fileName])) additionalArgs + let args = Array.append (mkProjectCommandLineArgs (dllName, [])) additionalArgs let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = [| fileName |] } fileName, options From 60eb8f0329910ee6f40caa08af55cc84a6f07229 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 22 Dec 2023 18:26:03 +0100 Subject: [PATCH 192/222] fix test --- tests/FSharp.Test.Utilities/ProjectGeneration.fs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 9ecc10f5b26..54c00ebe544 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -907,7 +907,9 @@ type ProjectWorkflowBuilder member this.DeleteProjectDir() = if Directory.Exists initialProject.ProjectDir then - Directory.Delete(initialProject.ProjectDir, true) + try + Directory.Delete(initialProject.ProjectDir, true) + with _ -> () member this.Execute(workflow: Async) = try From 9239b6487cdac5fef69fc04427245c382dced7cb Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 22 Dec 2023 18:30:10 +0100 Subject: [PATCH 193/222] fix --- tests/FSharp.Test.Utilities/CompilerAssert.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index b6bb6910fcf..7951ba31d5d 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -259,7 +259,7 @@ and Compilation = module rec CompilerAssertHelpers = - let useTransparentCompiler = false//FSharp.Compiler.CompilerConfig.FSharpExperimentalFeaturesEnabledAutomatically + let useTransparentCompiler = FSharp.Compiler.CompilerConfig.FSharpExperimentalFeaturesEnabledAutomatically let checker = FSharpChecker.Create(suggestNamesForErrors=true, useTransparentCompiler=useTransparentCompiler) // Unlike C# whose entrypoint is always string[] F# can make an entrypoint with 0 args, or with an array of string[] From c1c5be23d38fb683cc063cb8295ea9411a910d32 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 27 Dec 2023 11:05:31 +0100 Subject: [PATCH 194/222] fix tests --- .../CompilerService/AsyncMemoize.fs | 12 ++++++------ tests/service/MultiProjectAnalysisTests.fs | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 9d5fc85ae43..d64645e8028 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -92,20 +92,20 @@ let ``We can cancel a job`` () = let key = 1 let _task1 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts1.Token) - let _task2 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts2.Token) - let _task3 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts3.Token) jobStarted.WaitOne() |> ignore - jobStarted.Reset() |> ignore + let _task2 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts2.Token) + let _task3 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts3.Token) + Assert.Equal<(JobEvent * int) array>([| Started, key |], eventLog |> Seq.toArray ) cts1.Cancel() cts2.Cancel() - + do! Task.Delay 100 - + cts3.Cancel() do! Task.Delay 100 @@ -240,7 +240,7 @@ let ``Job is restarted if first requestor cancels but keeps running if second re cts1.Cancel() - //jobStarted.WaitOne() |> ignore + jobStarted.WaitOne() |> ignore cts2.Cancel() diff --git a/tests/service/MultiProjectAnalysisTests.fs b/tests/service/MultiProjectAnalysisTests.fs index 9433e7393ae..a306107eb36 100644 --- a/tests/service/MultiProjectAnalysisTests.fs +++ b/tests/service/MultiProjectAnalysisTests.fs @@ -654,7 +654,7 @@ let v = Project2A.C().InternalMember // access an internal symbol FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, [||]) let options = let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with @@ -678,7 +678,7 @@ let v = Project2A.C().InternalMember // access an internal symbol FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, [||]) let options = let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with @@ -775,7 +775,7 @@ let fizzBuzz = function FileSystem.OpenFileForWriteShim(fileName1).Write(fileSource1) let fileNames = [|fileName1|] - let args = mkProjectCommandLineArgs (dllName, fileNames) + let args = mkProjectCommandLineArgs (dllName, [||]) let options = let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } { options with From a78a4d651d01e1d823aa2a87aa085bde536a4891 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 27 Dec 2023 11:15:50 +0100 Subject: [PATCH 195/222] update debug surface area --- ...r.Service.SurfaceArea.netstandard20.debug.bsl | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 42daef7c3b1..6e4d980faa4 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -1967,16 +1967,6 @@ FSharp.Compiler.CodeAnalysis.DocumentSource: FSharp.Compiler.CodeAnalysis.Docume FSharp.Compiler.CodeAnalysis.DocumentSource: Int32 Tag FSharp.Compiler.CodeAnalysis.DocumentSource: Int32 get_Tag() FSharp.Compiler.CodeAnalysis.DocumentSource: System.String ToString() -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 braceMatchCacheSize -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 checkFileInProjectCacheSize -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 frameworkTcImportsCacheStrongSize -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_braceMatchCacheSize() -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_checkFileInProjectCacheSize() -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_frameworkTcImportsCacheStrongSize() -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_parseFileCacheSize() -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 get_projectCacheSizeDefault() -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 parseFileCacheSize -FSharp.Compiler.CodeAnalysis.EnvMisc: Int32 projectCacheSizeDefault FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer+Succeeded: FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults Item FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer+Succeeded: FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults get_Item() FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer+Tags: Int32 Aborted @@ -2304,8 +2294,6 @@ FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_baggage() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_itemSpec() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String itemSpec FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.String],System.String], System.String) -FSharp.Compiler.CodeAnalysis.ParseCacheLockToken: Void .ctor() -FSharp.Compiler.CodeAnalysis.ScriptClosureCacheToken: Void .ctor() FSharp.Compiler.CompilerEnvironment: Boolean IsCheckerSupportedSubcategory(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsCompilable(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsScriptFile(System.String) @@ -10200,12 +10188,12 @@ FSharp.Compiler.Text.ISourceText: Char get_Item(Int32) FSharp.Compiler.Text.ISourceText: Int32 GetLineCount() FSharp.Compiler.Text.ISourceText: Int32 Length FSharp.Compiler.Text.ISourceText: Int32 get_Length() -FSharp.Compiler.Text.ISourceText: System.Collections.Immutable.ImmutableArray`1[System.Byte] GetChecksum() FSharp.Compiler.Text.ISourceText: System.String GetLineString(Int32) FSharp.Compiler.Text.ISourceText: System.String GetSubTextFromRange(FSharp.Compiler.Text.Range) FSharp.Compiler.Text.ISourceText: System.String GetSubTextString(Int32, Int32) FSharp.Compiler.Text.ISourceText: System.Tuple`2[System.Int32,System.Int32] GetLastCharacterPosition() FSharp.Compiler.Text.ISourceText: Void CopyTo(Int32, Char[], Int32, Int32) +FSharp.Compiler.Text.ISourceTextNew: System.Collections.Immutable.ImmutableArray`1[System.Byte] GetChecksum() FSharp.Compiler.Text.Line: Int32 fromZ(Int32) FSharp.Compiler.Text.Line: Int32 toZ(Int32) FSharp.Compiler.Text.NavigableTaggedText: FSharp.Compiler.Text.Range Range @@ -10285,6 +10273,8 @@ FSharp.Compiler.Text.RangeModule: System.Tuple`2[System.String,System.Tuple`2[Sy FSharp.Compiler.Text.RangeModule: System.Tuple`2[System.Tuple`2[System.Int32,System.Int32],System.Tuple`2[System.Int32,System.Int32]] toZ(FSharp.Compiler.Text.Range) FSharp.Compiler.Text.RangeModule: Void outputRange(System.IO.TextWriter, FSharp.Compiler.Text.Range) FSharp.Compiler.Text.SourceText: FSharp.Compiler.Text.ISourceText ofString(System.String) +FSharp.Compiler.Text.SourceTextNew: FSharp.Compiler.Text.ISourceTextNew ofISourceText(FSharp.Compiler.Text.ISourceText) +FSharp.Compiler.Text.SourceTextNew: FSharp.Compiler.Text.ISourceTextNew ofString(System.String) FSharp.Compiler.Text.TaggedText: FSharp.Compiler.Text.TextTag Tag FSharp.Compiler.Text.TaggedText: FSharp.Compiler.Text.TextTag get_Tag() FSharp.Compiler.Text.TaggedText: System.String Text From 2d0e05228eddec62665835d93c972cf87d73c1b2 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 27 Dec 2023 11:56:22 +0100 Subject: [PATCH 196/222] add experimental attributes --- src/Compiler/Service/service.fsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index db6d5736077..7b4cace12d1 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -107,6 +107,7 @@ type public FSharpChecker = fileName: string * sourceText: ISourceText * options: FSharpParsingOptions * ?cache: bool * ?userOpName: string -> Async + [] member internal ParseFile: fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async @@ -203,6 +204,7 @@ type public FSharpChecker = ?userOpName: string -> Async + [] member internal ParseAndCheckFileInProject: fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async @@ -217,6 +219,7 @@ type public FSharpChecker = /// An optional string used for tracing compiler operations associated with this request. member ParseAndCheckProject: options: FSharpProjectOptions * ?userOpName: string -> Async + [] member internal ParseAndCheckProject: projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async @@ -340,6 +343,7 @@ type public FSharpChecker = ?userOpName: string -> Async + [] member internal FindBackgroundReferencesInFile: fileName: string * projectSnapshot: FSharpProjectSnapshot * symbol: FSharpSymbol * ?userOpName: string -> Async @@ -364,6 +368,7 @@ type public FSharpChecker = /// The file name for the file. /// The project snapshot for which we want to get the semantic classification. /// An optional string used for tracing compiler operations associated with this request. + [] member internal GetBackgroundSemanticClassificationForFile: fileName: string * snapshot: FSharpProjectSnapshot * ?userOpName: string -> Async From badc2678ce518ee354c0250cccb4eb5d741d8cca Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 27 Dec 2023 14:07:54 +0100 Subject: [PATCH 197/222] public facing snapshot class --- src/Compiler/Service/BackgroundCompiler.fs | 4 +- src/Compiler/Service/FSharpProjectSnapshot.fs | 309 +++++++++--------- src/Compiler/Service/TransparentCompiler.fs | 90 ++--- .../LanguageService/WorkspaceExtensions.fs | 1 + 4 files changed, 209 insertions(+), 195 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 3866f8651c7..3dbcbf24270 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -1560,7 +1560,7 @@ type internal BackgroundCompiler member _.GetAssemblyData ( - projectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot.FSharpProjectSnapshot, + projectSnapshot: FSharpProjectSnapshot, _fileName: string, userOpName: string ) : NodeCode = @@ -1662,7 +1662,7 @@ type internal BackgroundCompiler ) : NodeCode = node { let fileSnapshot = - projectSnapshot.SourceFiles |> Seq.find (fun f -> f.FileName = fileName) + projectSnapshot.ProjectSnapshot.SourceFiles |> Seq.find (fun f -> f.FileName = fileName) let! sourceText = fileSnapshot.GetSource() |> NodeCode.AwaitTask let options = projectSnapshot.ToOptions() diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index ba1b5f15cc2..2e3faf01840 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -138,7 +138,7 @@ type ReferenceOnDisk = type ProjectSnapshotKey = string * string -type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: ProjectCore, sourceFiles: 'T list) = +type ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: ProjectCore, sourceFiles: 'T list) = let noFileVersionsHash = lazy @@ -207,39 +207,6 @@ type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Project let sourceFileNames = lazy (sourceFiles |> List.map (fun x -> x.FileName)) - static member Create - ( - projectFileName: string, - projectId: string option, - sourceFiles: 'T list, - referencesOnDisk: ReferenceOnDisk list, - otherOptions: string list, - referencedProjects: FSharpReferencedProjectSnapshot list, - isIncompleteTypeCheckEnvironment: bool, - useScriptResolutionRules: bool, - loadTime: DateTime, - unresolvedReferences: FSharpUnresolvedReferencesSet option, - originalLoadReferences: (range * string * string) list, - stamp: int64 option - ) = - - let projectCore = - ProjectCore( - projectFileName, - projectId, - referencesOnDisk, - otherOptions, - referencedProjects, - isIncompleteTypeCheckEnvironment, - useScriptResolutionRules, - loadTime, - unresolvedReferences, - originalLoadReferences, - stamp - ) - - FSharpProjectSnapshotBase(projectCore, sourceFiles) - member _.ProjectFileName = projectCore.ProjectFileName member _.ProjectId = projectCore.ProjectId member _.ReferencesOnDisk = projectCore.ReferencesOnDisk @@ -273,7 +240,7 @@ type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Project |> Option.defaultWith (fun () -> failwith (sprintf "Unable to find file %s in project %s" fileName projectCore.ProjectFileName)) member private _.With(sourceFiles: 'T list) = - FSharpProjectSnapshotBase(projectCore, sourceFiles) + ProjectSnapshotBase(projectCore, sourceFiles) member this.UpTo fileIndex = this.With sourceFiles[..fileIndex] @@ -320,101 +287,8 @@ type FSharpProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Project member this.FileKey(fileName: string) = this.UpTo(fileName).LastFileKey member this.FileKey(index: FileIndex) = this.UpTo(index).LastFileKey - static member FromOptions(options: FSharpProjectOptions, getFileSnapshot, ?snapshotAccumulator) = - let snapshotAccumulator = defaultArg snapshotAccumulator (Dictionary()) - - async { - - // TODO: check if options is a good key here - if not (snapshotAccumulator.ContainsKey options) then - - let! sourceFiles = options.SourceFiles |> Seq.map (getFileSnapshot options) |> Async.Parallel - - let! referencedProjects = - options.ReferencedProjects - |> Seq.choose (function - | FSharpReferencedProject.FSharpReference(outputName, options) -> - Some( - async { - let! snapshot = - FSharpProjectSnapshotBase<_> - .FromOptions(options, getFileSnapshot, snapshotAccumulator) - - return FSharpReferencedProjectSnapshot.FSharpReference(outputName, snapshot) - } - ) - // TODO: other types - | _ -> None) - |> Async.Sequential - - let referencesOnDisk, otherOptions = - options.OtherOptions - |> Array.partition (fun x -> x.StartsWith("-r:")) - |> map1Of2 ( - Array.map (fun x -> - let path = x.Substring(3) - - { - Path = path - LastModified = FileSystem.GetLastWriteTimeShim(path) - }) - ) - - let snapshot = - FSharpProjectSnapshot.Create( - projectFileName = options.ProjectFileName, - projectId = options.ProjectId, - sourceFiles = (sourceFiles |> List.ofArray), - referencesOnDisk = (referencesOnDisk |> List.ofArray), - otherOptions = (otherOptions |> List.ofArray), - referencedProjects = (referencedProjects |> List.ofArray), - isIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment, - useScriptResolutionRules = options.UseScriptResolutionRules, - loadTime = options.LoadTime, - unresolvedReferences = options.UnresolvedReferences, - originalLoadReferences = options.OriginalLoadReferences, - stamp = options.Stamp - ) - - snapshotAccumulator.Add(options, snapshot) - - return snapshotAccumulator[options] - } - - static member GetFileSnapshotFromDisk _ fileName = - async { - let timeStamp = FileSystem.GetLastWriteTimeShim(fileName) - let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() - - return - FSharpFileSnapshot.Create( - fileName, - timeStamp.Ticks.ToString(), - (fun () -> Task.FromResult(SourceTextNew.ofString contents)) - ) - } - - static member FromOptions(options: FSharpProjectOptions) = - FSharpProjectSnapshot.FromOptions(options, FSharpProjectSnapshot.GetFileSnapshotFromDisk) - - static member FromOptions(options: FSharpProjectOptions, fileName: string, fileVersion: int, sourceText: ISourceText) = - - let getFileSnapshot _ fName = - if fName = fileName then - async.Return( - FSharpFileSnapshot.Create( - fileName, - $"{fileVersion}{sourceText.GetHashCode().ToString()}", - fun () -> Task.FromResult(SourceTextNew.ofISourceText sourceText) - ) - ) - else - FSharpProjectSnapshot.GetFileSnapshotFromDisk () fName - - FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) - -and FSharpProjectSnapshot = FSharpProjectSnapshotBase -and FSharpProjectSnapshotWithSources = FSharpProjectSnapshotBase +and ProjectSnapshot = ProjectSnapshotBase +and ProjectSnapshotWithSources = ProjectSnapshotBase and ProjectCore ( @@ -445,7 +319,7 @@ and ProjectCore |> Md5Hasher.addStrings (ReferencesOnDisk |> Seq.map (fun r -> r.Path)) |> Md5Hasher.addBytes' ( ReferencedProjects - |> Seq.map (fun (FSharpReference(_name, p)) -> p.SignatureVersion) + |> Seq.map (fun (FSharpReference(_name, p)) -> p.ProjectSnapshot.SignatureVersion) )) let fullHashString = lazy (fullHash.Value |> Md5Hasher.toString) @@ -549,24 +423,163 @@ and [] internal FSharpReferencedProjectSnapshot = override this.GetHashCode() = this.OutputFile.GetHashCode() + +and FSharpProjectSnapshot internal(projectSnapshot) = + + member internal _.ProjectSnapshot: ProjectSnapshot = projectSnapshot + + static member Create + ( + projectFileName: string, + projectId: string option, + sourceFiles: FSharpFileSnapshot list, + referencesOnDisk: ReferenceOnDisk list, + otherOptions: string list, + referencedProjects: FSharpReferencedProjectSnapshot list, + isIncompleteTypeCheckEnvironment: bool, + useScriptResolutionRules: bool, + loadTime: DateTime, + unresolvedReferences: FSharpUnresolvedReferencesSet option, + originalLoadReferences: (range * string * string) list, + stamp: int64 option + ) = + + let projectCore = + ProjectCore( + projectFileName, + projectId, + referencesOnDisk, + otherOptions, + referencedProjects, + isIncompleteTypeCheckEnvironment, + useScriptResolutionRules, + loadTime, + unresolvedReferences, + originalLoadReferences, + stamp + ) + + ProjectSnapshotBase(projectCore, sourceFiles) |> FSharpProjectSnapshot + + static member FromOptions(options: FSharpProjectOptions, getFileSnapshot, ?snapshotAccumulator) = + let snapshotAccumulator = defaultArg snapshotAccumulator (Dictionary()) + + async { + + // TODO: check if options is a good key here + if not (snapshotAccumulator.ContainsKey options) then + + let! sourceFiles = options.SourceFiles |> Seq.map (getFileSnapshot options) |> Async.Parallel + + let! referencedProjects = + options.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProject.FSharpReference(outputName, options) -> + Some( + async { + let! snapshot = + FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, snapshotAccumulator) + + return FSharpReferencedProjectSnapshot.FSharpReference(outputName, snapshot) + } + ) + // TODO: other types + | _ -> None) + |> Async.Sequential + + let referencesOnDisk, otherOptions = + options.OtherOptions + |> Array.partition (fun x -> x.StartsWith("-r:")) + |> map1Of2 ( + Array.map (fun x -> + let path = x.Substring(3) + + { + Path = path + LastModified = FileSystem.GetLastWriteTimeShim(path) + }) + ) + + let snapshot = + FSharpProjectSnapshot.Create( + projectFileName = options.ProjectFileName, + projectId = options.ProjectId, + sourceFiles = (sourceFiles |> List.ofArray), + referencesOnDisk = (referencesOnDisk |> List.ofArray), + otherOptions = (otherOptions |> List.ofArray), + referencedProjects = (referencedProjects |> List.ofArray), + isIncompleteTypeCheckEnvironment = options.IsIncompleteTypeCheckEnvironment, + useScriptResolutionRules = options.UseScriptResolutionRules, + loadTime = options.LoadTime, + unresolvedReferences = options.UnresolvedReferences, + originalLoadReferences = options.OriginalLoadReferences, + stamp = options.Stamp + ) + + snapshotAccumulator.Add(options, snapshot) + + return snapshotAccumulator[options] + } + + static member GetFileSnapshotFromDisk _ fileName = + async { + let timeStamp = FileSystem.GetLastWriteTimeShim(fileName) + let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() + + return + FSharpFileSnapshot.Create( + fileName, + timeStamp.Ticks.ToString(), + (fun () -> Task.FromResult(SourceTextNew.ofString contents)) + ) + } + + static member FromOptions(options: FSharpProjectOptions) = + FSharpProjectSnapshot.FromOptions(options, FSharpProjectSnapshot.GetFileSnapshotFromDisk) + + static member FromOptions(options: FSharpProjectOptions, fileName: string, fileVersion: int, sourceText: ISourceText) = + + let getFileSnapshot _ fName = + if fName = fileName then + async.Return( + FSharpFileSnapshot.Create( + fileName, + $"{fileVersion}{sourceText.GetHashCode().ToString()}", + fun () -> Task.FromResult(SourceTextNew.ofISourceText sourceText) + ) + ) + else + FSharpProjectSnapshot.GetFileSnapshotFromDisk () fName + + FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + +let rec snapshotToOptions (projectSnapshot: ProjectSnapshotBase<_>) = + { + ProjectFileName = projectSnapshot.ProjectFileName + ProjectId = projectSnapshot.ProjectId + SourceFiles = projectSnapshot.SourceFiles |> Seq.map (fun x -> x.FileName) |> Seq.toArray + OtherOptions = projectSnapshot.CommandLineOptions |> List.toArray + ReferencedProjects = + projectSnapshot.ReferencedProjects + |> Seq.map (function + | FSharpReference(name, opts) -> FSharpReferencedProject.FSharpReference(name, opts.ProjectSnapshot |> snapshotToOptions)) + |> Seq.toArray + IsIncompleteTypeCheckEnvironment = projectSnapshot.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = projectSnapshot.UseScriptResolutionRules + LoadTime = projectSnapshot.LoadTime + UnresolvedReferences = projectSnapshot.UnresolvedReferences + OriginalLoadReferences = projectSnapshot.OriginalLoadReferences + Stamp = projectSnapshot.Stamp + } + + [] type Extensions = + + [] + static member ToOptions(this: ProjectSnapshot) = + this |> snapshotToOptions + [] static member ToOptions(this: FSharpProjectSnapshot) = - { - ProjectFileName = this.ProjectFileName - ProjectId = this.ProjectId - SourceFiles = this.SourceFiles |> Seq.map (fun x -> x.FileName) |> Seq.toArray - OtherOptions = this.CommandLineOptions |> List.toArray - ReferencedProjects = - this.ReferencedProjects - |> Seq.map (function - | FSharpReference(name, opts) -> FSharpReferencedProject.FSharpReference(name, opts.ToOptions())) - |> Seq.toArray - IsIncompleteTypeCheckEnvironment = this.IsIncompleteTypeCheckEnvironment - UseScriptResolutionRules = this.UseScriptResolutionRules - LoadTime = this.LoadTime - UnresolvedReferences = this.UnresolvedReferences - OriginalLoadReferences = this.OriginalLoadReferences - Stamp = this.Stamp - } + this.ProjectSnapshot |> snapshotToOptions \ No newline at end of file diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 6093141b917..7e7bc83e820 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -490,7 +490,7 @@ type internal TransparentCompiler return tcImports, tcInfo } - let getProjectReferences (project: FSharpProjectSnapshotBase<_>) userOpName = + let getProjectReferences (project: ProjectSnapshotBase<_>) userOpName = [ for r in project.ReferencedProjects do @@ -513,7 +513,7 @@ type internal TransparentCompiler node { Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) - return! self.GetAssemblyData(projectSnapshot, nm, userOpName + ".CheckReferencedProject(" + nm + ")") + return! self.GetAssemblyData(projectSnapshot.ProjectSnapshot, nm, userOpName + ".CheckReferencedProject(" + nm + ")") } member x.TryGetLogicalTimeStamp(cache) = @@ -524,7 +524,7 @@ type internal TransparentCompiler } ] - let ComputeTcConfigBuilder (projectSnapshot: FSharpProjectSnapshotBase<_>) = + let ComputeTcConfigBuilder (projectSnapshot: ProjectSnapshotBase<_>) = let useSimpleResolutionSwitch = "--simpleresolution" let commandLineArgs = projectSnapshot.CommandLineOptions @@ -691,7 +691,7 @@ type internal TransparentCompiler } ) - let computeBootstrapInfoInner (projectSnapshot: FSharpProjectSnapshot) = + let computeBootstrapInfoInner (projectSnapshot: ProjectSnapshot) = node { let tcConfigB, sourceFiles, loadClosureOpt = ComputeTcConfigBuilder projectSnapshot @@ -797,7 +797,7 @@ type internal TransparentCompiler } } - let ComputeBootstrapInfo (projectSnapshot: FSharpProjectSnapshot) = + let ComputeBootstrapInfo (projectSnapshot: ProjectSnapshot) = caches.BootstrapInfo.Get( projectSnapshot.NoFileVersionsKey, @@ -856,7 +856,7 @@ type internal TransparentCompiler ) } - let LoadSources (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshot) = + let LoadSources (bootstrapInfo: BootstrapInfo) (projectSnapshot: ProjectSnapshot) = node { let isExe = bootstrapInfo.TcConfig.target.IsExe @@ -865,11 +865,11 @@ type internal TransparentCompiler |> Seq.map (fun f -> LoadSource f isExe (f.FileName = bootstrapInfo.LastFileName)) |> NodeCode.Parallel - return FSharpProjectSnapshotWithSources(projectSnapshot.ProjectCore, sources |> Array.toList) + return ProjectSnapshotWithSources(projectSnapshot.ProjectCore, sources |> Array.toList) } - let ComputeParseFile (projectSnapshot: FSharpProjectSnapshotBase<_>) (tcConfig: TcConfig) (file: FSharpFileSnapshotWithSource) = + let ComputeParseFile (projectSnapshot: ProjectSnapshotBase<_>) (tcConfig: TcConfig) (file: FSharpFileSnapshotWithSource) = let key = { new ICacheKey<_, _> with @@ -977,7 +977,7 @@ type internal TransparentCompiler return nodeGraph, graph } - let removeImplFilesThatHaveSignatures (projectSnapshot: FSharpProjectSnapshot) (graph: Graph) = + let removeImplFilesThatHaveSignatures (projectSnapshot: ProjectSnapshot) (graph: Graph) = let removeIndexes = projectSnapshot.SourceFileNames @@ -1001,7 +1001,7 @@ type internal TransparentCompiler |> Seq.map (fun x -> x.Key, x.Value |> Array.filter (fun node -> not (removeIndexes.Contains node))) |> Graph.make - let removeImplFilesThatHaveSignaturesExceptLastOne (projectSnapshot: FSharpProjectSnapshotBase<_>) (graph: Graph) = + let removeImplFilesThatHaveSignaturesExceptLastOne (projectSnapshot: ProjectSnapshotBase<_>) (graph: Graph) = let removeIndexes = projectSnapshot.SourceFileNames @@ -1027,7 +1027,7 @@ type internal TransparentCompiler |> Seq.map (fun x -> x.Key, x.Value |> Array.filter (fun node -> not (removeIndexes.Contains node))) |> Graph.make - let ComputeDependencyGraphForFile (tcConfig: TcConfig) (priorSnapshot: FSharpProjectSnapshotBase) = + let ComputeDependencyGraphForFile (tcConfig: TcConfig) (priorSnapshot: ProjectSnapshotBase) = let key = priorSnapshot.SourceFiles.Key(DependencyGraphType.File) //let lastFileIndex = (parsedInputs |> Array.length) - 1 //caches.DependencyGraph.Get(key, computeDependencyGraph parsedInputs (Graph.subGraphFor lastFileIndex)) @@ -1039,7 +1039,7 @@ type internal TransparentCompiler (removeImplFilesThatHaveSignaturesExceptLastOne priorSnapshot) ) - let ComputeDependencyGraphForProject (tcConfig: TcConfig) (projectSnapshot: FSharpProjectSnapshotBase) = + let ComputeDependencyGraphForProject (tcConfig: TcConfig) (projectSnapshot: ProjectSnapshotBase) = let key = projectSnapshot.SourceFiles.Key(DependencyGraphType.Project) //caches.DependencyGraph.Get(key, computeDependencyGraph parsedInputs (removeImplFilesThatHaveSignatures projectSnapshot)) @@ -1049,7 +1049,7 @@ type internal TransparentCompiler ) let ComputeTcIntermediate - (projectSnapshot: FSharpProjectSnapshotBase) + (projectSnapshot: ProjectSnapshotBase) (dependencyGraph: Graph) (index: FileIndex) (nodeToCheck: NodeToTypeCheck) @@ -1286,18 +1286,18 @@ type internal TransparentCompiler } - let parseSourceFiles (projectSnapshot: FSharpProjectSnapshotWithSources) tcConfig = + let parseSourceFiles (projectSnapshot: ProjectSnapshotWithSources) tcConfig = node { let! parsedInputs = projectSnapshot.SourceFiles |> Seq.map (ComputeParseFile projectSnapshot tcConfig) |> NodeCode.Parallel - return FSharpProjectSnapshotBase<_>(projectSnapshot.ProjectCore, parsedInputs |> Array.toList) + return ProjectSnapshotBase<_>(projectSnapshot.ProjectCore, parsedInputs |> Array.toList) } // Type check file and all its dependencies - let ComputeTcLastFile (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = + let ComputeTcLastFile (bootstrapInfo: BootstrapInfo) (projectSnapshot: ProjectSnapshotWithSources) = let fileName = projectSnapshot.SourceFiles |> List.last |> (fun f -> f.FileName) caches.TcLastFile.Get( @@ -1325,7 +1325,7 @@ type internal TransparentCompiler } ) - let getParseResult (projectSnapshot: FSharpProjectSnapshot) creationDiags file (tcConfig: TcConfig) = + let getParseResult (projectSnapshot: ProjectSnapshot) creationDiags file (tcConfig: TcConfig) = node { let! parsedFile = ComputeParseFile projectSnapshot tcConfig file @@ -1356,7 +1356,7 @@ type internal TransparentCompiler let parseTree = EmptyParsedInput(fileName, (false, false)) FSharpParseFileResults(diagnostics, parseTree, true, [||]) - let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = + let ComputeParseAndCheckFileInProject (fileName: string) (projectSnapshot: ProjectSnapshot) = caches.ParseAndCheckFileInProject.Get( projectSnapshot.FileKey fileName, node { @@ -1460,7 +1460,7 @@ type internal TransparentCompiler } ) - let ComputeParseAndCheckAllFilesInProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = + let ComputeParseAndCheckAllFilesInProject (bootstrapInfo: BootstrapInfo) (projectSnapshot: ProjectSnapshotWithSources) = caches.ParseAndCheckAllFilesInProject.Get( projectSnapshot.FullKey, node { @@ -1482,7 +1482,7 @@ type internal TransparentCompiler } ) - let ComputeProjectExtras (bootstrapInfo: BootstrapInfo) (projectSnapshot: FSharpProjectSnapshotWithSources) = + let ComputeProjectExtras (bootstrapInfo: BootstrapInfo) (projectSnapshot: ProjectSnapshotWithSources) = caches.ProjectExtras.Get( projectSnapshot.SignatureKey, node { @@ -1575,7 +1575,7 @@ type internal TransparentCompiler } ) - let ComputeAssemblyData (projectSnapshot: FSharpProjectSnapshot) fileName = + let ComputeAssemblyData (projectSnapshot: ProjectSnapshot) fileName = caches.AssemblyData.Get( projectSnapshot.SignatureKey, node { @@ -1618,7 +1618,7 @@ type internal TransparentCompiler } ) - let ComputeParseAndCheckProject (projectSnapshot: FSharpProjectSnapshot) = + let ComputeParseAndCheckProject (projectSnapshot: ProjectSnapshot) = caches.ParseAndCheckProject.Get( projectSnapshot.FullKey, node { @@ -1692,7 +1692,7 @@ type internal TransparentCompiler } ) - let tryGetSink (fileName: string) (projectSnapshot: FSharpProjectSnapshot) = + let tryGetSink (fileName: string) (projectSnapshot: ProjectSnapshot) = node { match! ComputeBootstrapInfo projectSnapshot with | None, _ -> return None @@ -1705,7 +1705,7 @@ type internal TransparentCompiler return tcInfo.sink |> List.tryHead |> Option.map (fun sink -> sink, bootstrapInfo) } - let ComputeSemanticClassification (fileName: string, projectSnapshot: FSharpProjectSnapshot) = + let ComputeSemanticClassification (fileName: string, projectSnapshot: ProjectSnapshot) = caches.SemanticClassification.Get( projectSnapshot.FileKey fileName, node { @@ -1735,7 +1735,7 @@ type internal TransparentCompiler } ) - let ComputeItemKeyStore (fileName: string, projectSnapshot: FSharpProjectSnapshot) = + let ComputeItemKeyStore (fileName: string, projectSnapshot: ProjectSnapshot) = caches.ItemKeyStore.Get( projectSnapshot.FileKey fileName, node { @@ -1772,7 +1772,7 @@ type internal TransparentCompiler } ) - member _.ParseFile(fileName, projectSnapshot: FSharpProjectSnapshot, _userOpName) = + member _.ParseFile(fileName, projectSnapshot: ProjectSnapshot, _userOpName) = node { //use _ = // Activity.start "ParseFile" [| Activity.Tags.fileName, fileName |> Path.GetFileName |] @@ -1798,11 +1798,11 @@ type internal TransparentCompiler return parseResult } - member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = + member _.ParseAndCheckFileInProject(fileName: string, projectSnapshot: ProjectSnapshot, userOpName: string) = ignore userOpName ComputeParseAndCheckFileInProject fileName projectSnapshot - member _.FindReferencesInFile(fileName: string, projectSnapshot: FSharpProjectSnapshot, symbol: FSharpSymbol, userOpName: string) = + member _.FindReferencesInFile(fileName: string, projectSnapshot: ProjectSnapshot, symbol: FSharpSymbol, userOpName: string) = ignore userOpName node { @@ -1811,7 +1811,7 @@ type internal TransparentCompiler | Some itemKeyStore -> return itemKeyStore.FindAll symbol.Item } - member _.GetAssemblyData(projectSnapshot: FSharpProjectSnapshot, fileName, _userOpName) = + member _.GetAssemblyData(projectSnapshot: ProjectSnapshot, fileName, _userOpName) = ComputeAssemblyData projectSnapshot fileName member _.Caches = caches @@ -1841,7 +1841,7 @@ type internal TransparentCompiler ignore parseResults - let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot.ProjectSnapshot, userOpName) return result } @@ -1865,7 +1865,7 @@ type internal TransparentCompiler ignore parseResults - let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + let! _, result = this.ParseAndCheckFileInProject(fileName, snapshot.ProjectSnapshot, userOpName) return Some result } @@ -1902,11 +1902,11 @@ type internal TransparentCompiler ignore canInvalidateProject let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! this.FindReferencesInFile(fileName, snapshot, symbol, userOpName) + return! this.FindReferencesInFile(fileName, snapshot.ProjectSnapshot, symbol, userOpName) } member this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) = - this.FindReferencesInFile(fileName, projectSnapshot, symbol, userOpName) + this.FindReferencesInFile(fileName, projectSnapshot.ProjectSnapshot, symbol, userOpName) member _.FrameworkImportsCache: FrameworkImportsCache = backgroundCompiler.FrameworkImportsCache @@ -1917,7 +1917,7 @@ type internal TransparentCompiler use _ = Cancellable.UsingToken(ct) let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! this.GetAssemblyData(snapshot, fileName, userOpName) + return! this.GetAssemblyData(snapshot.ProjectSnapshot, fileName, userOpName) } member this.GetAssemblyData @@ -1926,7 +1926,7 @@ type internal TransparentCompiler fileName, userOpName: string ) : NodeCode = - this.GetAssemblyData(projectSnapshot, fileName, userOpName) + this.GetAssemblyData(projectSnapshot.ProjectSnapshot, fileName, userOpName) member this.GetBackgroundCheckResultsForFileInProject ( @@ -1940,7 +1940,7 @@ type internal TransparentCompiler let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - match! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) with + match! this.ParseAndCheckFileInProject(fileName, snapshot.ProjectSnapshot, userOpName) with | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return parseResult, checkResult | parseResult, FSharpCheckFileAnswer.Aborted -> return parseResult, FSharpCheckFileResults.MakeEmpty(fileName, [||], true) } @@ -1956,7 +1956,7 @@ type internal TransparentCompiler use _ = Cancellable.UsingToken(ct) let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! this.ParseFile(fileName, snapshot, userOpName) + return! this.ParseFile(fileName, snapshot.ProjectSnapshot, userOpName) } member this.GetCachedCheckFileResult @@ -1975,7 +1975,7 @@ type internal TransparentCompiler FSharpProjectSnapshot.FromOptions(options, fileName, 1, sourceText) |> NodeCode.AwaitAsync - match! this.ParseAndCheckFileInProject(fileName, snapshot, "GetCachedCheckFileResult") with + match! this.ParseAndCheckFileInProject(fileName, snapshot.ProjectSnapshot, "GetCachedCheckFileResult") with | parseResult, FSharpCheckFileAnswer.Succeeded checkResult -> return Some(parseResult, checkResult) | _, FSharpCheckFileAnswer.Aborted -> return None } @@ -2011,7 +2011,7 @@ type internal TransparentCompiler member this.GetSemanticClassificationForFile(fileName: string, snapshot: FSharpProjectSnapshot, userOpName: string) = node { ignore userOpName - return! ComputeSemanticClassification(fileName, snapshot) + return! ComputeSemanticClassification(fileName, snapshot.ProjectSnapshot) } member this.GetSemanticClassificationForFile @@ -2026,7 +2026,7 @@ type internal TransparentCompiler use _ = Cancellable.UsingToken(ct) let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! ComputeSemanticClassification(fileName, snapshot) + return! ComputeSemanticClassification(fileName, snapshot.ProjectSnapshot) } member this.InvalidateConfiguration(options: FSharpProjectOptions, userOpName: string) : unit = @@ -2054,11 +2054,11 @@ type internal TransparentCompiler FSharpProjectSnapshot.FromOptions(options, fileName, fileVersion, sourceText) |> NodeCode.AwaitAsync - return! this.ParseAndCheckFileInProject(fileName, snapshot, userOpName) + return! this.ParseAndCheckFileInProject(fileName, snapshot.ProjectSnapshot, userOpName) } member this.ParseAndCheckFileInProject(fileName: string, projectSnapshot: FSharpProjectSnapshot, userOpName: string) = - this.ParseAndCheckFileInProject(fileName, projectSnapshot, userOpName) + this.ParseAndCheckFileInProject(fileName, projectSnapshot.ProjectSnapshot, userOpName) member this.ParseAndCheckProject(options: FSharpProjectOptions, userOpName: string) : NodeCode = node { @@ -2067,7 +2067,7 @@ type internal TransparentCompiler ignore userOpName let! snapshot = FSharpProjectSnapshot.FromOptions options |> NodeCode.AwaitAsync - return! ComputeParseAndCheckProject snapshot + return! ComputeParseAndCheckProject snapshot.ProjectSnapshot } member this.ParseAndCheckProject(projectSnapshot: FSharpProjectSnapshot, userOpName: string) : NodeCode = @@ -2076,11 +2076,11 @@ type internal TransparentCompiler use _ = Cancellable.UsingToken(ct) ignore userOpName - return! ComputeParseAndCheckProject projectSnapshot + return! ComputeParseAndCheckProject projectSnapshot.ProjectSnapshot } member this.ParseFile(fileName, projectSnapshot, userOpName) = - this.ParseFile(fileName, projectSnapshot, userOpName) |> Async.AwaitNodeCode + this.ParseFile(fileName, projectSnapshot.ProjectSnapshot, userOpName) |> Async.AwaitNodeCode member this.ParseFile ( diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 9de8f277ffe..3ae49b543b8 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -62,6 +62,7 @@ module internal FSharpProjectSnapshotSerialization = and serializeSnapshot (snapshot: FSharpProjectSnapshot) = let output = JObject() + let snapshot = snapshot.ProjectSnapshot output.Add("ProjectFileName", snapshot.ProjectFileName) output.Add("ProjectId", (snapshot.ProjectId |> Option.defaultValue null |> JToken.FromObject)) From bf7e7de23e6ffb07dbc20b728148334005386d5a Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 27 Dec 2023 14:53:36 +0100 Subject: [PATCH 198/222] update --- src/Compiler/Service/FSharpProjectSnapshot.fs | 8 ++++++-- .../FSharp.Editor/LanguageService/WorkspaceExtensions.fs | 6 ++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index fa14576d6ae..2185a519a21 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -244,7 +244,7 @@ type ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: ProjectCore, member this.Replace(changedSourceFiles: 'T list) = // TODO: validate if changed files are not present in the original list? - + let sourceFiles = sourceFiles |> List.map (fun x -> @@ -435,11 +435,15 @@ and [] internal FSharpReferencedProjectSnapshot = override this.GetHashCode() = this.OutputFile.GetHashCode() - and FSharpProjectSnapshot internal(projectSnapshot) = member internal _.ProjectSnapshot: ProjectSnapshot = projectSnapshot + member _.Replace(changedSourceFiles: FSharpFileSnapshot list) = + projectSnapshot.Replace(changedSourceFiles) |> FSharpProjectSnapshot + + member _.Label = projectSnapshot.Label + static member Create ( projectFileName: string, diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index e34dded1e3b..790df8446f6 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -20,6 +20,8 @@ open Newtonsoft.Json open Newtonsoft.Json.Linq open System.Text.Json.Nodes +#nowarn "57" // Experimental stuff + [] module internal ProjectCache = @@ -195,7 +197,7 @@ module private CheckerExtensions = let changedDocuments = changes.GetChangedDocuments() |> Seq.toList System.Diagnostics.Trace.TraceInformation - $"Incremental update of FSharpProjectSnapshot ({oldSnapshot.ProjectCore.Label}) - {changedDocuments.Length} changed documents" + $"Incremental update of FSharpProjectSnapshot ({oldSnapshot.Label}) - {changedDocuments.Length} changed documents" if changedDocuments.Length = 0 then // this is suspicious @@ -267,7 +269,7 @@ module private CheckerExtensions = let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) - System.Diagnostics.Trace.TraceInformation $"Created new FSharpProjectSnapshot ({snapshot.ProjectCore.Label})" + System.Diagnostics.Trace.TraceInformation $"Created new FSharpProjectSnapshot ({snapshot.Label})" return snapshot } From fc1e45f639a9c429cf8b019b5efe39caa9903c35 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 27 Dec 2023 15:47:15 +0100 Subject: [PATCH 199/222] nowarn --- .../FSharpChecker/FindReferences.fs | 2 ++ .../FSharpChecker/TransparentCompiler.fs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs index d994e0ae1b0..b12edf8cb6d 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/FindReferences.fs @@ -5,6 +5,8 @@ open FSharp.Compiler.CodeAnalysis open FSharp.Test.ProjectGeneration open FSharp.Test.ProjectGeneration.Helpers +#nowarn "57" + type Occurence = Definition | InType | Use let deriveOccurence (su:FSharpSymbolUse) = diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 549bb354b4b..a6b7f6fcc41 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -25,6 +25,8 @@ open OpenTelemetry.Resources open OpenTelemetry.Trace +#nowarn "57" + [] let ``Use Transparent Compiler`` () = From d66b48dc49bbb457e33a806ab6ceec3623d12a49 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 27 Dec 2023 16:56:07 +0100 Subject: [PATCH 200/222] api --- src/Compiler/Service/FSharpProjectSnapshot.fs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 2185a519a21..42f6f13ac1d 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -23,7 +23,7 @@ open FSharp.Compiler.Syntax open FSharp.Compiler.Diagnostics open FSharp.Compiler.DiagnosticsLogger -type IFileSnapshot = +type internal IFileSnapshot = abstract member FileName: string abstract member Version: byte array abstract member IsSignatureFile: bool @@ -59,6 +59,7 @@ module internal Helpers = |> fst, lastFile +[] type FSharpFileSnapshot(FileName: string, Version: string, GetSource: unit -> Task) = static member Create(fileName: string, version: string, getSource: unit -> Task) = @@ -92,7 +93,7 @@ type FSharpFileSnapshot(FileName: string, Version: string, GetSource: unit -> Ta member this.Version = this.Version |> System.Text.Encoding.UTF8.GetBytes member this.IsSignatureFile = this.IsSignatureFile -type FSharpFileSnapshotWithSource +type internal FSharpFileSnapshotWithSource (FileName: string, SourceHash: ImmutableArray, Source: ISourceText, IsLastCompiland: bool, IsExe: bool) = let version = lazy (SourceHash.ToBuilder().ToArray()) @@ -112,7 +113,7 @@ type FSharpFileSnapshotWithSource member this.Version = this.Version member this.IsSignatureFile = this.IsSignatureFile -type FSharpParsedFile +type internal FSharpParsedFile ( FileName: string, SyntaxTreeHash: byte array, @@ -133,12 +134,12 @@ type FSharpParsedFile member this.Version = SyntaxTreeHash member this.IsSignatureFile = this.IsSignatureFile -type ReferenceOnDisk = +type internal ReferenceOnDisk = { Path: string; LastModified: DateTime } -type ProjectSnapshotKey = string * string +type internal ProjectSnapshotKey = string * string -type ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: ProjectCore, sourceFiles: 'T list) = +type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: ProjectCore, sourceFiles: 'T list) = let noFileVersionsHash = lazy @@ -299,10 +300,10 @@ type ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: ProjectCore, member this.FileKey(fileName: string) = this.UpTo(fileName).LastFileKey member this.FileKey(index: FileIndex) = this.UpTo(index).LastFileKey -and ProjectSnapshot = ProjectSnapshotBase -and ProjectSnapshotWithSources = ProjectSnapshotBase +and internal ProjectSnapshot = ProjectSnapshotBase +and internal ProjectSnapshotWithSources = ProjectSnapshotBase -and ProjectCore +and internal ProjectCore ( ProjectFileName: string, ProjectId: string option, @@ -435,7 +436,8 @@ and [] internal FSharpReferencedProjectSnapshot = override this.GetHashCode() = this.OutputFile.GetHashCode() -and FSharpProjectSnapshot internal(projectSnapshot) = +and [] + FSharpProjectSnapshot internal(projectSnapshot) = member internal _.ProjectSnapshot: ProjectSnapshot = projectSnapshot @@ -569,7 +571,7 @@ and FSharpProjectSnapshot internal(projectSnapshot) = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) -let rec snapshotToOptions (projectSnapshot: ProjectSnapshotBase<_>) = +let rec internal snapshotToOptions (projectSnapshot: ProjectSnapshotBase<_>) = { ProjectFileName = projectSnapshot.ProjectFileName ProjectId = projectSnapshot.ProjectId From bedc8dd03e9728189e838d70862da456b4f7ce97 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 27 Dec 2023 19:53:40 +0100 Subject: [PATCH 201/222] add references stamp to hash --- src/Compiler/Facilities/Hashing.fs | 3 +++ src/Compiler/Service/FSharpProjectSnapshot.fs | 1 + 2 files changed, 4 insertions(+) diff --git a/src/Compiler/Facilities/Hashing.fs b/src/Compiler/Facilities/Hashing.fs index 58d82893e20..e4aec9c2c11 100644 --- a/src/Compiler/Facilities/Hashing.fs +++ b/src/Compiler/Facilities/Hashing.fs @@ -76,4 +76,7 @@ module internal Md5Hasher = let addDateTime (dt: System.DateTime) (s: byte array) = dt.Ticks |> BitConverter.GetBytes |> addBytes <| s + let addDateTimes (dts: System.DateTime seq) (s: byte array) = + s |> addSeq dts addDateTime + let toString (bytes: byte array) = bytes |> System.BitConverter.ToString diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index ba1b5f15cc2..684099272d7 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -443,6 +443,7 @@ and ProjectCore lazy (hashForParsing.Value |> Md5Hasher.addStrings (ReferencesOnDisk |> Seq.map (fun r -> r.Path)) + |> Md5Hasher.addDateTimes (ReferencesOnDisk |> Seq.map (fun r -> r.LastModified)) |> Md5Hasher.addBytes' ( ReferencedProjects |> Seq.map (fun (FSharpReference(_name, p)) -> p.SignatureVersion) From 41d4942d9dfa44fa25e7626ac5e395141b893d21 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 27 Dec 2023 19:54:07 +0100 Subject: [PATCH 202/222] f --- src/Compiler/Facilities/Hashing.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Compiler/Facilities/Hashing.fs b/src/Compiler/Facilities/Hashing.fs index e4aec9c2c11..2dfbb38b7ee 100644 --- a/src/Compiler/Facilities/Hashing.fs +++ b/src/Compiler/Facilities/Hashing.fs @@ -76,7 +76,6 @@ module internal Md5Hasher = let addDateTime (dt: System.DateTime) (s: byte array) = dt.Ticks |> BitConverter.GetBytes |> addBytes <| s - let addDateTimes (dts: System.DateTime seq) (s: byte array) = - s |> addSeq dts addDateTime + let addDateTimes (dts: System.DateTime seq) (s: byte array) = s |> addSeq dts addDateTime let toString (bytes: byte array) = bytes |> System.BitConverter.ToString From 9c23b513764aaa1078ba411ad6e42755db63b479 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 27 Dec 2023 19:55:04 +0100 Subject: [PATCH 203/222] invalidate snapshot when references on disk change --- .../LanguageService/WorkspaceExtensions.fs | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 790df8446f6..4db6cec73f8 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -149,6 +149,18 @@ module private CheckerExtensions = |> CancellableTask.whenAll |> CancellableTask.map (Seq.map (fun x -> x.ToString()) >> Set) + let getOnDiskReferences (options: FSharpProjectOptions) = + options.OtherOptions + |> Seq.filter (fun x -> x.StartsWith("-r:")) + |> Seq.map (fun x -> + let path = x.Substring(3) + + { + Path = path + LastModified = System.IO.File.GetLastWriteTimeUtc path + }) + |> Seq.toList + let createProjectSnapshot (snapshotAccumulatorOpt) (project: Project) (options: FSharpProjectOptions option) = cancellableTask { @@ -167,13 +179,20 @@ module private CheckerExtensions = let updatedSnapshot = match latestSnapshots.TryGetValue project.Id with - | true, (_, oldProjectVersion, _, _, oldSnapshot) when projectVersion = oldProjectVersion -> - Some(CancellableTask.singleton oldSnapshot) | true, (_, _, oldReferenceVersions, _, _) when referenceVersions <> oldReferenceVersions -> System.Diagnostics.Trace.TraceWarning "Reference versions changed" None - | true, (oldProject, _, _oldProjectVersion, oldOptions, oldSnapshot: FSharpProjectSnapshot) when + | true, (_, _, _, _, oldSnapshot: FSharpProjectSnapshot) when + oldSnapshot.ProjectSnapshot.ReferencesOnDisk <> (getOnDiskReferences options) + -> + System.Diagnostics.Trace.TraceWarning "References on disk changed" + None + + | true, (_, oldProjectVersion, _, _, oldSnapshot: FSharpProjectSnapshot) when projectVersion = oldProjectVersion -> + Some(CancellableTask.singleton oldSnapshot) + + | true, (oldProject, _oldProjectVersion, _oldReferencesVersion, oldOptions, oldSnapshot: FSharpProjectSnapshot) when FSharpProjectOptions.AreSameForChecking(options, oldOptions) -> @@ -260,14 +279,17 @@ module private CheckerExtensions = // Fall back to file system let version = System.IO.File.GetLastWriteTimeUtc(path) + let getSource () = task { return System.IO.File.ReadAllText(path) |> FSharp.Compiler.Text.SourceTextNew.ofString } + async.Return(version.ToString(), getSource) return FSharpFileSnapshot(FileName = path, Version = version, GetSource = getSource) } - let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) + let! snapshot = + FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, ?snapshotAccumulator = snapshotAccumulatorOpt) System.Diagnostics.Trace.TraceInformation $"Created new FSharpProjectSnapshot ({snapshot.Label})" From 3c3befc4c59ebae65d98463e92dfc05f8ac2b5c8 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 8 Jan 2024 16:58:35 +0100 Subject: [PATCH 204/222] reusable snapshot menu option --- .../LanguageService/WorkspaceExtensions.fs | 10 +- .../FSharp.Editor/Options/EditorOptions.fs | 5 + .../AdvancedOptionsControl.xaml | 6 +- .../FSharp.UIResources/Strings.Designer.cs | 285 +++++++++--------- .../src/FSharp.UIResources/Strings.resx | 3 + .../src/FSharp.UIResources/xlf/Strings.cs.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.de.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.es.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.fr.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.it.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.ja.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.ko.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.pl.xlf | 5 + .../FSharp.UIResources/xlf/Strings.pt-BR.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.ru.xlf | 5 + .../src/FSharp.UIResources/xlf/Strings.tr.xlf | 5 + .../xlf/Strings.zh-Hans.xlf | 5 + .../xlf/Strings.zh-Hant.xlf | 5 + 18 files changed, 230 insertions(+), 144 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 4db6cec73f8..c0682607360 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -178,21 +178,21 @@ module private CheckerExtensions = let! referenceVersions = getReferencedProjectVersions project let updatedSnapshot = - match latestSnapshots.TryGetValue project.Id with - | true, (_, _, oldReferenceVersions, _, _) when referenceVersions <> oldReferenceVersions -> + match project.IsTransparentCompilerSnapshotReuseEnabled, latestSnapshots.TryGetValue project.Id with + | true, (true, (_, _, oldReferenceVersions, _, _)) when referenceVersions <> oldReferenceVersions -> System.Diagnostics.Trace.TraceWarning "Reference versions changed" None - | true, (_, _, _, _, oldSnapshot: FSharpProjectSnapshot) when + | true, (true, (_, _, _, _, oldSnapshot: FSharpProjectSnapshot)) when oldSnapshot.ProjectSnapshot.ReferencesOnDisk <> (getOnDiskReferences options) -> System.Diagnostics.Trace.TraceWarning "References on disk changed" None - | true, (_, oldProjectVersion, _, _, oldSnapshot: FSharpProjectSnapshot) when projectVersion = oldProjectVersion -> + | true, (true, (_, oldProjectVersion, _, _, oldSnapshot: FSharpProjectSnapshot)) when projectVersion = oldProjectVersion -> Some(CancellableTask.singleton oldSnapshot) - | true, (oldProject, _oldProjectVersion, _oldReferencesVersion, oldOptions, oldSnapshot: FSharpProjectSnapshot) when + | true, (true, (oldProject, _oldProjectVersion, _oldReferencesVersion, oldOptions, oldSnapshot: FSharpProjectSnapshot)) when FSharpProjectOptions.AreSameForChecking(options, oldOptions) -> diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index c94df2b2aad..8d7bfc655f2 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -120,6 +120,7 @@ type AdvancedOptions = IsInlineReturnTypeHintsEnabled: bool IsUseLiveBuffersEnabled: bool UseTransparentCompiler: bool + TransparentCompilerSnapshotReuse: bool SendAdditionalTelemetry: bool SolutionBackgroundAnalysis: bool } @@ -132,6 +133,7 @@ type AdvancedOptions = IsInlineParameterNameHintsEnabled = false IsInlineReturnTypeHintsEnabled = false UseTransparentCompiler = false + TransparentCompilerSnapshotReuse = true IsUseLiveBuffersEnabled = true SendAdditionalTelemetry = true SolutionBackgroundAnalysis = false @@ -272,5 +274,8 @@ module EditorOptionsExtensions = member this.UseTransparentCompiler = this.EditorOptions.Advanced.UseTransparentCompiler + member this.IsTransparentCompilerSnapshotReuseEnabled = + this.EditorOptions.Advanced.TransparentCompilerSnapshotReuse + member this.TransparentCompilerCacheFactor = this.EditorOptions.LanguageServicePerformance.TransparentCompilerCacheFactor diff --git a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml index 41d5ce466cf..ce82e0bbd44 100644 --- a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml +++ b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml @@ -14,7 +14,7 @@ - + + + + -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ namespace Microsoft.VisualStudio.FSharp.UIResources { using System; - - + + /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Strings { - + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Strings() { } - + /// - /// 返回此类使用的缓存的 ResourceManager 实例。 + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { @@ -45,10 +45,10 @@ internal Strings() { return resourceMan; } } - + /// - /// 重写当前线程的 CurrentUICulture 属性,对 - /// 使用此强类型资源类的所有资源查找执行重写。 + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { @@ -59,502 +59,502 @@ internal Strings() { resourceCulture = value; } } - + /// - /// 查找类似 Additional performance telemetry (experimental) 的本地化字符串。 + /// Looks up a localized string similar to Additional performance telemetry (experimental). /// public static string AdditionalTelemetry { get { return ResourceManager.GetString("AdditionalTelemetry", resourceCulture); } } - + /// - /// 查找类似 Always place open statements at the top level 的本地化字符串。 + /// Looks up a localized string similar to Always place open statements at the top level. /// public static string Always_place_opens_at_top_level { get { return ResourceManager.GetString("Always_place_opens_at_top_level", resourceCulture); } } - + /// - /// 查找类似 Keep analyzing the entire solution for diagnostics as a low priority background task (requires restart) 的本地化字符串。 + /// Looks up a localized string similar to Keep analyzing the entire solution for diagnostics as a low priority background task (requires restart). /// public static string Analyze_full_solution_on_background { get { return ResourceManager.GetString("Analyze_full_solution_on_background", resourceCulture); } } - + /// - /// 查找类似 Background analysis 的本地化字符串。 + /// Looks up a localized string similar to Background analysis. /// public static string Background_analysis { get { return ResourceManager.GetString("Background_analysis", resourceCulture); } } - + /// - /// 查找类似 Block Structure Guides 的本地化字符串。 + /// Looks up a localized string similar to Block Structure Guides. /// public static string Block_Structure { get { return ResourceManager.GetString("Block_Structure", resourceCulture); } } - + /// - /// 查找类似 Code Fixes 的本地化字符串。 + /// Looks up a localized string similar to Code Fixes. /// public static string Code_Fixes { get { return ResourceManager.GetString("Code_Fixes", resourceCulture); } } - + /// - /// 查找类似 Completion Lists 的本地化字符串。 + /// Looks up a localized string similar to Completion Lists. /// public static string Completion_Lists { get { return ResourceManager.GetString("Completion_Lists", resourceCulture); } } - + /// - /// 查找类似 D_ash underline 的本地化字符串。 + /// Looks up a localized string similar to D_ash underline. /// public static string Dash_underline { get { return ResourceManager.GetString("Dash_underline", resourceCulture); } } - + /// - /// 查找类似 Diagnostics 的本地化字符串。 + /// Looks up a localized string similar to Diagnostics. /// public static string Diagnostics { get { return ResourceManager.GetString("Diagnostics", resourceCulture); } } - + /// - /// 查找类似 D_ot underline 的本地化字符串。 + /// Looks up a localized string similar to D_ot underline. /// public static string Dot_underline { get { return ResourceManager.GetString("Dot_underline", resourceCulture); } } - + /// - /// 查找类似 Keep background symbol keys 的本地化字符串。 + /// Looks up a localized string similar to Keep background symbol keys. /// public static string Enable_Background_ItemKeyStore_And_Semantic_Classification { get { return ResourceManager.GetString("Enable_Background_ItemKeyStore_And_Semantic_Classification", resourceCulture); } } - + /// - /// 查找类似 Enable fast find references & rename (experimental) 的本地化字符串。 + /// Looks up a localized string similar to Enable fast find references & rename (experimental). /// public static string Enable_Fast_Find_References { get { return ResourceManager.GetString("Enable_Fast_Find_References", resourceCulture); } } - + /// - /// 查找类似 _Enable in-memory cross project references 的本地化字符串。 + /// Looks up a localized string similar to _Enable in-memory cross project references. /// public static string Enable_in_memory_cross_project_references { get { return ResourceManager.GetString("Enable_in_memory_cross_project_references", resourceCulture); } } - + /// - /// 查找类似 Use live (unsaved) buffers for analysis (restart required) 的本地化字符串。 + /// Looks up a localized string similar to Use live (unsaved) buffers for analysis (restart required). /// public static string Enable_Live_Buffers { get { return ResourceManager.GetString("Enable_Live_Buffers", resourceCulture); } } - + /// - /// 查找类似 Enable parallel reference resolution 的本地化字符串。 + /// Looks up a localized string similar to Enable parallel reference resolution. /// public static string Enable_Parallel_Reference_Resolution { get { return ResourceManager.GetString("Enable_Parallel_Reference_Resolution", resourceCulture); } } - + /// - /// 查找类似 Enable partial type checking 的本地化字符串。 + /// Looks up a localized string similar to Enable partial type checking. /// public static string Enable_partial_type_checking { get { return ResourceManager.GetString("Enable_partial_type_checking", resourceCulture); } } - + /// - /// 查找类似 Enable stale data for IntelliSense features 的本地化字符串。 + /// Looks up a localized string similar to Enable stale data for IntelliSense features. /// public static string Enable_Stale_IntelliSense_Results { get { return ResourceManager.GetString("Enable_Stale_IntelliSense_Results", resourceCulture); } } - + /// - /// 查找类似 Always add new line on enter 的本地化字符串。 + /// Looks up a localized string similar to Always add new line on enter. /// public static string Enter_key_always { get { return ResourceManager.GetString("Enter_key_always", resourceCulture); } } - + /// - /// 查找类似 Never add new line on enter 的本地化字符串。 + /// Looks up a localized string similar to Never add new line on enter. /// public static string Enter_key_never { get { return ResourceManager.GetString("Enter_key_never", resourceCulture); } } - + /// - /// 查找类似 Only add new line on enter after end of fully typed word 的本地化字符串。 + /// Looks up a localized string similar to Only add new line on enter after end of fully typed word. /// public static string Enter_key_only { get { return ResourceManager.GetString("Enter_key_only", resourceCulture); } } - + /// - /// 查找类似 Enter key behavior 的本地化字符串。 + /// Looks up a localized string similar to Enter key behavior. /// public static string Enter_Key_Rule { get { return ResourceManager.GetString("Enter_Key_Rule", resourceCulture); } } - + /// - /// 查找类似 Find References Performance Options 的本地化字符串。 + /// Looks up a localized string similar to Find References Performance Options. /// public static string Find_References_Performance { get { return ResourceManager.GetString("Find_References_Performance", resourceCulture); } } - + /// - /// 查找类似 Re-format indentation on paste (Experimental) 的本地化字符串。 + /// Looks up a localized string similar to Re-format indentation on paste (Experimental). /// public static string Format_on_paste { get { return ResourceManager.GetString("Format_on_paste", resourceCulture); } } - + /// - /// 查找类似 Formatting 的本地化字符串。 + /// Looks up a localized string similar to Formatting. /// public static string Formatting { get { return ResourceManager.GetString("Formatting", resourceCulture); } } - + /// - /// 查找类似 Inline Hints 的本地化字符串。 + /// Looks up a localized string similar to Inline Hints. /// public static string Inline_Hints { get { return ResourceManager.GetString("Inline_Hints", resourceCulture); } } - + /// - /// 查找类似 IntelliSense Performance Options 的本地化字符串。 + /// Looks up a localized string similar to IntelliSense Performance Options. /// public static string IntelliSense_Performance { get { return ResourceManager.GetString("IntelliSense_Performance", resourceCulture); } } - + /// - /// 查找类似 Keep all background intermediate resolutions (increases memory usage) 的本地化字符串。 + /// Looks up a localized string similar to Keep all background intermediate resolutions (increases memory usage). /// public static string Keep_All_Background_Resolutions { get { return ResourceManager.GetString("Keep_All_Background_Resolutions", resourceCulture); } } - + /// - /// 查找类似 Keep all background symbol uses (increases memory usage) 的本地化字符串。 + /// Looks up a localized string similar to Keep all background symbol uses (increases memory usage). /// public static string Keep_All_Background_Symbol_Uses { get { return ResourceManager.GetString("Keep_All_Background_Symbol_Uses", resourceCulture); } } - + /// - /// 查找类似 Performance 的本地化字符串。 + /// Looks up a localized string similar to Performance. /// public static string Language_Service_Performance { get { return ResourceManager.GetString("Language_Service_Performance", resourceCulture); } } - + /// - /// 查找类似 Language service settings (advanced) 的本地化字符串。 + /// Looks up a localized string similar to Language service settings (advanced). /// public static string Language_Service_Settings { get { return ResourceManager.GetString("Language_Service_Settings", resourceCulture); } } - + /// - /// 查找类似 Live Buffers 的本地化字符串。 + /// Looks up a localized string similar to Live Buffers. /// public static string LiveBuffers { get { return ResourceManager.GetString("LiveBuffers", resourceCulture); } } - + /// - /// 查找类似 Navigation links 的本地化字符串。 + /// Looks up a localized string similar to Navigation links. /// public static string Navigation_links { get { return ResourceManager.GetString("Navigation_links", resourceCulture); } } - + /// - /// 查找类似 Outlining 的本地化字符串。 + /// Looks up a localized string similar to Outlining. /// public static string Outlining { get { return ResourceManager.GetString("Outlining", resourceCulture); } } - + /// - /// 查找类似 Parallelization (requires restart) 的本地化字符串。 + /// Looks up a localized string similar to Parallelization (requires restart). /// public static string Parallelization { get { return ResourceManager.GetString("Parallelization", resourceCulture); } } - + /// - /// 查找类似 Preferred description width in characters 的本地化字符串。 + /// Looks up a localized string similar to Preferred description width in characters. /// public static string Preferred_description_width_in_characters { get { return ResourceManager.GetString("Preferred_description_width_in_characters", resourceCulture); } } - + /// - /// 查找类似 F# Project and Caching Performance Options 的本地化字符串。 + /// Looks up a localized string similar to F# Project and Caching Performance Options. /// public static string Project_Performance { get { return ResourceManager.GetString("Project_Performance", resourceCulture); } } - + /// - /// 查找类似 Remove unnecessary parentheses (experimental, might affect typing performance) 的本地化字符串。 + /// Looks up a localized string similar to Remove unnecessary parentheses (experimental, might affect typing performance). /// public static string Remove_parens_code_fix { get { return ResourceManager.GetString("Remove_parens_code_fix", resourceCulture); } } - + /// - /// 查找类似 Send additional performance telemetry 的本地化字符串。 + /// Looks up a localized string similar to Send additional performance telemetry. /// public static string Send_Additional_Telemetry { get { return ResourceManager.GetString("Send_Additional_Telemetry", resourceCulture); } } - + /// - /// 查找类似 Show s_ymbols in unopened namespaces 的本地化字符串。 + /// Looks up a localized string similar to Show s_ymbols in unopened namespaces. /// public static string Show_all_symbols { get { return ResourceManager.GetString("Show_all_symbols", resourceCulture); } } - + /// - /// 查找类似 Show completion list after a character is _deleted 的本地化字符串。 + /// Looks up a localized string similar to Show completion list after a character is _deleted. /// public static string Show_completion_list_after_a_character_is_deleted { get { return ResourceManager.GetString("Show_completion_list_after_a_character_is_deleted", resourceCulture); } } - + /// - /// 查找类似 _Show completion list after a character is typed 的本地化字符串。 + /// Looks up a localized string similar to _Show completion list after a character is typed. /// public static string Show_completion_list_after_a_character_is_typed { get { return ResourceManager.GetString("Show_completion_list_after_a_character_is_typed", resourceCulture); } } - + /// - /// 查找类似 Show structure guidelines for F# code 的本地化字符串。 + /// Looks up a localized string similar to Show structure guidelines for F# code. /// public static string Show_guides { get { return ResourceManager.GetString("Show_guides", resourceCulture); } } - + /// - /// 查找类似 Display inline parameter name hints (preview) 的本地化字符串。 + /// Looks up a localized string similar to Display inline parameter name hints (preview). /// public static string Show_Inline_Parameter_Name_Hints { get { return ResourceManager.GetString("Show_Inline_Parameter_Name_Hints", resourceCulture); } } - + /// - /// 查找类似 Display inline type hints (preview) 的本地化字符串。 + /// Looks up a localized string similar to Display inline type hints (preview). /// public static string Show_Inline_Type_Hints { get { return ResourceManager.GetString("Show_Inline_Type_Hints", resourceCulture); } } - + /// - /// 查找类似 S_how navigation links as 的本地化字符串。 + /// Looks up a localized string similar to S_how navigation links as. /// public static string Show_navigation_links_as { get { return ResourceManager.GetString("Show_navigation_links_as", resourceCulture); } } - + /// - /// 查找类似 Show outlining and collapsible nodes for F# code 的本地化字符串。 + /// Looks up a localized string similar to Show outlining and collapsible nodes for F# code. /// public static string Show_Outlining { get { return ResourceManager.GetString("Show_Outlining", resourceCulture); } } - + /// - /// 查找类似 Show remarks in Quick Info 的本地化字符串。 + /// Looks up a localized string similar to Show remarks in Quick Info. /// public static string Show_remarks_in_Quick_Info { get { return ResourceManager.GetString("Show_remarks_in_Quick_Info", resourceCulture); } } - + /// - /// 查找类似 Display return type hints (preview) 的本地化字符串。 + /// Looks up a localized string similar to Display return type hints (preview). /// public static string Show_Return_Type_Hints { get { return ResourceManager.GetString("Show_Return_Type_Hints", resourceCulture); } } - + /// - /// 查找类似 Simplify names (remove unnecessary qualifiers) 的本地化字符串。 + /// Looks up a localized string similar to Simplify names (remove unnecessary qualifiers). /// public static string Simplify_name_code_fix { get { return ResourceManager.GetString("Simplify_name_code_fix", resourceCulture); } } - + /// - /// 查找类似 _Solid underline 的本地化字符串。 + /// Looks up a localized string similar to _Solid underline. /// public static string Solid_underline { get { return ResourceManager.GetString("Solid_underline", resourceCulture); } } - + /// - /// 查找类似 Suggest names for unresolved identifiers 的本地化字符串。 + /// Looks up a localized string similar to Suggest names for unresolved identifiers. /// public static string Suggest_names_for_errors_code_fix { get { return ResourceManager.GetString("Suggest_names_for_errors_code_fix", resourceCulture); } } - + /// - /// 查找类似 Text hover 的本地化字符串。 + /// Looks up a localized string similar to Text hover. /// public static string Text_hover { get { return ResourceManager.GetString("Text_hover", resourceCulture); } } - + /// - /// 查找类似 Time until stale results are used (in milliseconds) 的本地化字符串。 + /// Looks up a localized string similar to Time until stale results are used (in milliseconds). /// public static string Time_until_stale_completion { get { return ResourceManager.GetString("Time_until_stale_completion", resourceCulture); } } - + /// - /// 查找类似 In-memory cross-project references store project-level data in memory to allow IDE features to work across projects. 的本地化字符串。 + /// Looks up a localized string similar to In-memory cross-project references store project-level data in memory to allow IDE features to work across projects.. /// public static string Tooltip_in_memory_cross_project_references { get { return ResourceManager.GetString("Tooltip_in_memory_cross_project_references", resourceCulture); } } - + /// - /// 查找类似 Format signature to the given width by adding line breaks conforming with F# syntax rules. 的本地化字符串。 + /// Looks up a localized string similar to Format signature to the given width by adding line breaks conforming with F# syntax rules. . /// public static string Tooltip_preferred_description_width_in_characters { get { return ResourceManager.GetString("Tooltip_preferred_description_width_in_characters", resourceCulture); } } - + /// /// Looks up a localized string similar to Transparent Compiler Cache Factor. /// @@ -563,7 +563,7 @@ public static string Transparent_Compiler_Cache_Factor { return ResourceManager.GetString("Transparent_Compiler_Cache_Factor", resourceCulture); } } - + /// /// Looks up a localized string similar to Higher number means more memory will be used for caching. Changing the value wipes cache.. /// @@ -572,7 +572,16 @@ public static string Transparent_Compiler_Cache_Factor_Tooltip { return ResourceManager.GetString("Transparent_Compiler_Cache_Factor_Tooltip", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Create new project snapshots from existing ones. + /// + public static string Transparent_Compiler_Snapshot_Reuse { + get { + return ResourceManager.GetString("Transparent_Compiler_Snapshot_Reuse", resourceCulture); + } + } + /// /// Looks up a localized string similar to Transparent Compiler (experimental). /// @@ -581,7 +590,7 @@ public static string TransparentCompiler { return ResourceManager.GetString("TransparentCompiler", resourceCulture); } } - + /// /// Looks up a localized string similar to Analyze and suggest fixes for unused values. /// @@ -590,25 +599,25 @@ public static string Unused_declaration_code_fix { return ResourceManager.GetString("Unused_declaration_code_fix", resourceCulture); } } - + /// - /// 查找类似 Remove unused open statements 的本地化字符串。 + /// Looks up a localized string similar to Remove unused open statements. /// public static string Unused_opens_code_fix { get { return ResourceManager.GetString("Unused_opens_code_fix", resourceCulture); } } - + /// - /// 查找类似 Cache parsing results (experimental) 的本地化字符串。 + /// Looks up a localized string similar to Cache parsing results (experimental). /// public static string Use_syntax_tree_cache { get { return ResourceManager.GetString("Use_syntax_tree_cache", resourceCulture); } } - + /// /// Looks up a localized string similar to Use Transparent Compiler (restart required). /// diff --git a/vsintegration/src/FSharp.UIResources/Strings.resx b/vsintegration/src/FSharp.UIResources/Strings.resx index b37d6758877..f139c15f73a 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.resx +++ b/vsintegration/src/FSharp.UIResources/Strings.resx @@ -303,4 +303,7 @@ Show remarks in Quick Info + + Create new project snapshots from existing ones + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf index f194c21b0dd..06ecd8a3cd0 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Odebrat nepoužívané otevřené výkazy diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf index cc38f198e97..c75ef691f6a 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Nicht verwendete "open"-Anweisungen entfernen diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf index 205596a3b5d..c71ee9b4cef 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Quitar instrucciones open no usadas diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf index f96ad926309..77b21b77ddb 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Supprimer les instructions open inutilisées diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf index 09cfbddadd5..df9251bc96b 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Rimuovi istruzioni OPEN inutilizzate diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf index 34b71d8e078..9ff08093a08 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements 未使用の Open ステートメントを削除する diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf index 16e19fe104e..7d567b80971 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements 사용되지 않는 open 문 제거 diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf index bf0eb46d652..dc467970ab4 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Usuń nieużywane otwarte instrukcje diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf index 291df43bb21..b535df1e00c 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Remover instruções abertas não usadas diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf index f24864c4dfd..85cbc3d6697 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Удалить неиспользуемые открытые операторы diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf index 91fbd0e9f45..f1cd83846c4 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements Kullanılmayan açık deyimleri kaldır diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf index 4d6f941f8b0..9947cfd7fec 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements 删除未使用的 open 语句 diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf index c7fcc3ecb0a..fb1d95da8d0 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf @@ -217,6 +217,11 @@ Higher number means more memory will be used for caching. Changing the value wipes cache. + + Create new project snapshots from existing ones + Create new project snapshots from existing ones + + Remove unused open statements 移除未使用的 open 陳述式 From 8f454c1a9b8e2a4d135b00d501c81e9c654b94db Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 8 Jan 2024 17:09:50 +0100 Subject: [PATCH 205/222] api --- src/Compiler/Service/FSharpProjectSnapshot.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 38350b295fa..4a1e06795b1 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -134,7 +134,8 @@ type internal FSharpParsedFile member this.Version = SyntaxTreeHash member this.IsSignatureFile = this.IsSignatureFile -type internal ReferenceOnDisk = +[] +type ReferenceOnDisk = { Path: string; LastModified: DateTime } type internal ProjectSnapshotKey = string * string From b2ac7226bb66a8b7bd137e19fe276a7b42356cd6 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 8 Jan 2024 17:58:26 +0100 Subject: [PATCH 206/222] make FSharpProjectSnapshot public --- src/Compiler/Service/FSharpProjectSnapshot.fs | 9 ++-- ...ervice.SurfaceArea.netstandard20.debug.bsl | 52 +++++++++++++++++++ ...vice.SurfaceArea.netstandard20.release.bsl | 52 +++++++++++++++++++ 3 files changed, 109 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 4a1e06795b1..24b5aff8cdd 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -3,7 +3,7 @@ // Open up the compiler as an incremental service for parsing, // type checking and intellisense-like environment-reporting. -module internal FSharp.Compiler.CodeAnalysis.ProjectSnapshot +module FSharp.Compiler.CodeAnalysis.ProjectSnapshot open System open System.Collections.Generic @@ -402,8 +402,9 @@ and internal ProjectCore member _.CacheKey = cacheKey.Value -and [] internal FSharpReferencedProjectSnapshot = - internal +and [] + [] + FSharpReferencedProjectSnapshot = | FSharpReference of projectOutputFile: string * options: FSharpProjectSnapshot //| PEReference of projectOutputFile: string * getStamp: (unit -> DateTime) * delayedReader: DelayedILModuleReader //| ILModuleReference of @@ -594,7 +595,7 @@ let rec internal snapshotToOptions (projectSnapshot: ProjectSnapshotBase<_>) = [] -type Extensions = +type internal Extensions = [] static member ToOptions(this: ProjectSnapshot) = diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 6e4d980faa4..f79c8eb5913 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2294,6 +2294,58 @@ FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_baggage() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_itemSpec() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String itemSpec FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.String],System.String], System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean IsSignatureFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean get_IsSignatureFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot Create(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot CreateFromFileSystem(System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String FileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String GetFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String Version +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String get_FileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String get_Version() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew] GetSource() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Replace(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot] GetFileSnapshotFromDisk[a](a, System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]]], Microsoft.FSharp.Core.FSharpOption`1[System.Collections.Generic.Dictionary`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, System.String, Int32, FSharp.Compiler.Text.ISourceText) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.String Label +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.String get_Label() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpProjectSnapshot get_options() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpProjectSnapshot options +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpReferencedProjectSnapshot CreateFSharp(System.String, FSharpProjectSnapshot) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpReferencedProjectSnapshot NewFSharpReference(System.String, FSharpProjectSnapshot) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Int32 Tag +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Int32 get_Tag() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String OutputFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String get_OutputFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String get_projectOutputFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String projectOutputFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(ReferenceOnDisk) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 CompareTo(ReferenceOnDisk) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 CompareTo(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.DateTime LastModified +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.DateTime get_LastModified() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String Path +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String get_Path() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Void .ctor(System.String, System.DateTime) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk FSharp.Compiler.CompilerEnvironment: Boolean IsCheckerSupportedSubcategory(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsCompilable(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsScriptFile(System.String) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 6e4d980faa4..f79c8eb5913 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2294,6 +2294,58 @@ FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_baggage() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String get_itemSpec() FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: System.String itemSpec FSharp.Compiler.CodeAnalysis.LegacyResolvedFile: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.String],System.String], System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean IsSignatureFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean get_IsSignatureFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot Create(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot CreateFromFileSystem(System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String FileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String GetFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String Version +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String get_FileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String get_Version() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew] GetSource() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Replace(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot] GetFileSnapshotFromDisk[a](a, System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]]], Microsoft.FSharp.Core.FSharpOption`1[System.Collections.Generic.Dictionary`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, System.String, Int32, FSharp.Compiler.Text.ISourceText) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.String Label +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.String get_Label() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpProjectSnapshot get_options() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpProjectSnapshot options +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpReferencedProjectSnapshot CreateFSharp(System.String, FSharpProjectSnapshot) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharpReferencedProjectSnapshot NewFSharpReference(System.String, FSharpProjectSnapshot) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Int32 Tag +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Int32 get_Tag() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String OutputFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String get_OutputFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String get_projectOutputFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String projectOutputFile +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(ReferenceOnDisk) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 CompareTo(ReferenceOnDisk) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 CompareTo(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.DateTime LastModified +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.DateTime get_LastModified() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String Path +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String get_Path() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Void .ctor(System.String, System.DateTime) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk FSharp.Compiler.CompilerEnvironment: Boolean IsCheckerSupportedSubcategory(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsCompilable(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsScriptFile(System.String) From 338043f7116887e50a5299c996ffef1a4c3b0902 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 8 Jan 2024 18:16:42 +0100 Subject: [PATCH 207/222] f --- src/Compiler/Service/BackgroundCompiler.fs | 3 ++- src/Compiler/Service/FSharpProjectSnapshot.fs | 16 +++++----------- src/Compiler/Service/TransparentCompiler.fs | 10 ++++++++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 3dbcbf24270..b351df0b821 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -1662,7 +1662,8 @@ type internal BackgroundCompiler ) : NodeCode = node { let fileSnapshot = - projectSnapshot.ProjectSnapshot.SourceFiles |> Seq.find (fun f -> f.FileName = fileName) + projectSnapshot.ProjectSnapshot.SourceFiles + |> Seq.find (fun f -> f.FileName = fileName) let! sourceText = fileSnapshot.GetSource() |> NodeCode.AwaitTask let options = projectSnapshot.ToOptions() diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 24b5aff8cdd..3bacc411723 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -402,9 +402,7 @@ and internal ProjectCore member _.CacheKey = cacheKey.Value -and [] - [] - FSharpReferencedProjectSnapshot = +and [] FSharpReferencedProjectSnapshot = | FSharpReference of projectOutputFile: string * options: FSharpProjectSnapshot //| PEReference of projectOutputFile: string * getStamp: (unit -> DateTime) * delayedReader: DelayedILModuleReader //| ILModuleReference of @@ -439,8 +437,7 @@ and [] override this.GetHashCode() = this.OutputFile.GetHashCode() -and [] - FSharpProjectSnapshot internal(projectSnapshot) = +and [] FSharpProjectSnapshot internal (projectSnapshot) = member internal _.ProjectSnapshot: ProjectSnapshot = projectSnapshot @@ -498,8 +495,7 @@ and [] | FSharpReferencedProject.FSharpReference(outputName, options) -> Some( async { - let! snapshot = - FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, snapshotAccumulator) + let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, snapshotAccumulator) return FSharpReferencedProjectSnapshot.FSharpReference(outputName, snapshot) } @@ -593,14 +589,12 @@ let rec internal snapshotToOptions (projectSnapshot: ProjectSnapshotBase<_>) = Stamp = projectSnapshot.Stamp } - [] type internal Extensions = [] - static member ToOptions(this: ProjectSnapshot) = - this |> snapshotToOptions + static member ToOptions(this: ProjectSnapshot) = this |> snapshotToOptions [] static member ToOptions(this: FSharpProjectSnapshot) = - this.ProjectSnapshot |> snapshotToOptions \ No newline at end of file + this.ProjectSnapshot |> snapshotToOptions diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 7e7bc83e820..831c197465f 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -513,7 +513,12 @@ type internal TransparentCompiler node { Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "GetAssemblyData", nm) - return! self.GetAssemblyData(projectSnapshot.ProjectSnapshot, nm, userOpName + ".CheckReferencedProject(" + nm + ")") + return! + self.GetAssemblyData( + projectSnapshot.ProjectSnapshot, + nm, + userOpName + ".CheckReferencedProject(" + nm + ")" + ) } member x.TryGetLogicalTimeStamp(cache) = @@ -2080,7 +2085,8 @@ type internal TransparentCompiler } member this.ParseFile(fileName, projectSnapshot, userOpName) = - this.ParseFile(fileName, projectSnapshot.ProjectSnapshot, userOpName) |> Async.AwaitNodeCode + this.ParseFile(fileName, projectSnapshot.ProjectSnapshot, userOpName) + |> Async.AwaitNodeCode member this.ParseFile ( From 4575389752a32a9d282e94c8655c147ec9242408 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 9 Jan 2024 16:22:09 +0100 Subject: [PATCH 208/222] API, use DLL from disk if fresh --- src/Compiler/Service/FSharpProjectSnapshot.fs | 94 ++++++++++--------- src/Compiler/Service/TransparentCompiler.fs | 45 ++------- src/Compiler/Service/service.fsi | 14 +-- ...ervice.SurfaceArea.netstandard20.debug.bsl | 6 +- ...vice.SurfaceArea.netstandard20.release.bsl | 6 +- 5 files changed, 74 insertions(+), 91 deletions(-) diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 3bacc411723..e5e610fc47c 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -1,8 +1,5 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -// Open up the compiler as an incremental service for parsing, -// type checking and intellisense-like environment-reporting. - module FSharp.Compiler.CodeAnalysis.ProjectSnapshot open System @@ -23,6 +20,7 @@ open FSharp.Compiler.Syntax open FSharp.Compiler.Diagnostics open FSharp.Compiler.DiagnosticsLogger +/// A common interface for an F# source file snapshot that can be used accross all stages (lazy, source loaded, parsed) type internal IFileSnapshot = abstract member FileName: string abstract member Version: byte array @@ -59,6 +57,7 @@ module internal Helpers = |> fst, lastFile +/// A snapshot of an F# source file. [] type FSharpFileSnapshot(FileName: string, Version: string, GetSource: unit -> Task) = @@ -69,7 +68,7 @@ type FSharpFileSnapshot(FileName: string, Version: string, GetSource: unit -> Ta FSharpFileSnapshot( fileName, FileSystem.GetLastWriteTimeShim(fileName).Ticks.ToString(), - fun () -> fileName |> File.ReadAllText |> SourceTextNew.ofString |> Task.FromResult + fun () -> FileSystem.OpenFileForReadShim(fileName).ReadAllText() |> SourceTextNew.ofString |> Task.FromResult ) member public _.FileName = FileName @@ -93,6 +92,7 @@ type FSharpFileSnapshot(FileName: string, Version: string, GetSource: unit -> Ta member this.Version = this.Version |> System.Text.Encoding.UTF8.GetBytes member this.IsSignatureFile = this.IsSignatureFile +/// A source file snapshot with loaded source text. type internal FSharpFileSnapshotWithSource (FileName: string, SourceHash: ImmutableArray, Source: ISourceText, IsLastCompiland: bool, IsExe: bool) = @@ -113,6 +113,7 @@ type internal FSharpFileSnapshotWithSource member this.Version = this.Version member this.IsSignatureFile = this.IsSignatureFile +/// A source file snapshot with parsed syntax tree type internal FSharpParsedFile ( FileName: string, @@ -134,12 +135,12 @@ type internal FSharpParsedFile member this.Version = SyntaxTreeHash member this.IsSignatureFile = this.IsSignatureFile +/// An on-disk reference needed for project compilation. [] type ReferenceOnDisk = { Path: string; LastModified: DateTime } -type internal ProjectSnapshotKey = string * string - +/// A snapshot of an F# project. The source file type can differ based on which stage of compilation the snapshot is used for. type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: ProjectCore, sourceFiles: 'T list) = let noFileVersionsHash = @@ -244,6 +245,7 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj member private _.With(sourceFiles: 'T list) = ProjectSnapshotBase(projectCore, sourceFiles) + /// Create a new snapshot with given source files replacing files in this snapshot with the same name. Other files remain unchanged. member this.Replace(changedSourceFiles: 'T list) = // TODO: validate if changed files are not present in the original list? @@ -254,12 +256,15 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj | Some y -> y | None -> x) - this.With(sourceFiles) + this.With sourceFiles + /// Create a new snapshot with source files only up to the given index (inclusive) member this.UpTo fileIndex = this.With sourceFiles[..fileIndex] + /// Create a new snapshot with source files only up to the given file name (inclusive) member this.UpTo fileName = this.UpTo(this.IndexOf fileName) + /// Create a new snapshot with only source files at the given indexes member this.OnlyWith fileIndexes = this.With( fileIndexes @@ -268,42 +273,52 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj |> List.choose (fun x -> sourceFiles |> List.tryItem x) ) - member this.WithoutSourceFiles = this.With [] - override this.ToString() = Path.GetFileNameWithoutExtension this.ProjectFileName |> sprintf "FSharpProjectSnapshot(%s)" - member this.GetLastModifiedTimeOnDisk() = - // TODO: - DateTime.Now - - member this.FullVersion = fullHash.Value - member this.SignatureVersion = signatureHash.Value |> fst - member this.LastFileVersion = lastFileHash.Value |> fst + /// The newest last modified time of any file in this snapshot including the project file + member _.GetLastModifiedTimeOnDisk() = + seq { + projectCore.ProjectFileName + yield! + sourceFiles + |> Seq.filter (fun x -> not (x.FileName.EndsWith(".AssemblyInfo.fs"))) // TODO: is this safe? any better way of doing this? + |> Seq.filter (fun x -> not (x.FileName.EndsWith(".AssemblyAttributes.fs"))) + |> Seq.map (fun x -> x.FileName) + } |> Seq.map FileSystem.GetLastWriteTimeShim |> Seq.max + + member _.FullVersion = fullHash.Value + member _.SignatureVersion = signatureHash.Value |> fst + member _.LastFileVersion = lastFileHash.Value |> fst /// Version for parsing - doesn't include any references because they don't affect parsing (...right?) - member this.ParsingVersion = projectCore.VersionForParsing |> Md5Hasher.toString + member _.ParsingVersion = projectCore.VersionForParsing |> Md5Hasher.toString /// A key for this snapshot but without file versions. So it will be the same across any in-file changes. - member this.NoFileVersionsKey = noFileVersionsKey.Value + member _.NoFileVersionsKey = noFileVersionsKey.Value /// A full key for this snapshot, any change will cause this to change. - member this.FullKey = fullKey.Value + member _.FullKey = fullKey.Value /// A key including the public surface or signature for this snapshot - member this.SignatureKey = signatureKey.Value + member _.SignatureKey = signatureKey.Value /// A key including the public surface or signature for this snapshot and the last file (even if it's not a signature file) - member this.LastFileKey = lastFileKey.Value + member _.LastFileKey = lastFileKey.Value //TODO: cache it here? member this.FileKey(fileName: string) = this.UpTo(fileName).LastFileKey member this.FileKey(index: FileIndex) = this.UpTo(index).LastFileKey +/// Project snapshot with filenames and versions given as initial input and internal ProjectSnapshot = ProjectSnapshotBase + +/// Project snapshot with file sources loaded and internal ProjectSnapshotWithSources = ProjectSnapshotBase +/// All required information for compiling a project except the source files. It's kept separate so it can be reused +/// for different stages of a project snapshot and also between changes to the source files. and internal ProjectCore ( ProjectFileName: string, @@ -422,9 +437,9 @@ and [ /// The fully qualified path to the output of the referenced project. This should be the same value as the -r reference in the project options for this referenced project. - /// The Project Options for this F# project - static member CreateFSharp(projectOutputFile, options: FSharpProjectSnapshot) = - FSharpReference(projectOutputFile, options) + /// The project snapshot for this F# project + static member CreateFSharp(projectOutputFile, snapshot: FSharpProjectSnapshot) = + FSharpReference(projectOutputFile, snapshot) override this.Equals(o) = match o with @@ -437,10 +452,12 @@ and [] FSharpProjectSnapshot internal (projectSnapshot) = member internal _.ProjectSnapshot: ProjectSnapshot = projectSnapshot + /// Create a new snapshot with given source files replacing files in this snapshot with the same name. Other files remain unchanged. member _.Replace(changedSourceFiles: FSharpFileSnapshot list) = projectSnapshot.Replace(changedSourceFiles) |> FSharpProjectSnapshot @@ -538,19 +555,9 @@ and [] FSha return snapshotAccumulator[options] } - static member GetFileSnapshotFromDisk _ fileName = - async { - let timeStamp = FileSystem.GetLastWriteTimeShim(fileName) - let contents = FileSystem.OpenFileForReadShim(fileName).ReadAllText() - - return - FSharpFileSnapshot.Create( - fileName, - timeStamp.Ticks.ToString(), - (fun () -> Task.FromResult(SourceTextNew.ofString contents)) - ) - } - + static member internal GetFileSnapshotFromDisk _ fileName = + FSharpFileSnapshot.CreateFromFileSystem fileName |> async.Return + static member FromOptions(options: FSharpProjectOptions) = FSharpProjectSnapshot.FromOptions(options, FSharpProjectSnapshot.GetFileSnapshotFromDisk) @@ -558,15 +565,14 @@ and [] FSha let getFileSnapshot _ fName = if fName = fileName then - async.Return( - FSharpFileSnapshot.Create( - fileName, - $"{fileVersion}{sourceText.GetHashCode().ToString()}", - fun () -> Task.FromResult(SourceTextNew.ofISourceText sourceText) - ) + FSharpFileSnapshot.Create( + fileName, + $"{fileVersion}{sourceText.GetHashCode().ToString()}", + fun () -> Task.FromResult(SourceTextNew.ofISourceText sourceText) ) else - FSharpProjectSnapshot.GetFileSnapshotFromDisk () fName + FSharpFileSnapshot.CreateFromFileSystem fName + |> async.Return FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 831c197465f..7d1080e5c89 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -749,42 +749,6 @@ type internal TransparentCompiler tcConfig.GetAvailableLoadedSources() |> List.map (fun (m, fileName) -> m, FSharpFileSnapshot.CreateFromFileSystem(fileName)) - //// Mark up the source files with an indicator flag indicating if they are the last source file in the project - //let sourceFiles = - // let flags, isExe = tcConfig.ComputeCanContainEntryPoint(sourceFiles |> List.map snd) - // ((sourceFiles, flags) ||> List.map2 (fun (m, nm) flag -> (m, nm, (flag, isExe)))) - - //let sourceFiles = - // sourceFiles - // |> List.map (fun (m, fileName, (isLastCompiland, isExe)) -> - // let source = - // fileSnapshots.TryFind fileName - // |> Option.defaultWith (fun () -> - // // TODO: does this commonly happen? - - // // It can happen when source files are inferred from command line options and are not part of FSharpProjectOptions.SourceFiles - which we use to create the Snapshot - - // let snapshotFileSummary = - // match projectSnapshot.SourceFiles with - // | [] -> "The project snapshot has no source files." - // | files -> - // "The project snapshot contains the following files:\n" - // + (files |> Seq.map (fun x -> x.FileName |> shortPath) |> String.concat "\n") - - // failwith - // $"Trying to check a file ({shortPath fileName}) which is not part of the project snapshot. {snapshotFileSummary}" - - // FSharpFileSnapshot.Create( - // fileName, - // (FileSystem.GetLastWriteTimeShim fileName).Ticks.ToString(), - // (fun () -> fileName |> File.ReadAllText |> SourceText.ofString |> Task.FromResult))) - // { - // Range = m // TODO: is this used for anything? - // Source = source - // IsLastCompiland = isLastCompiland - // IsExe = isExe - // }) - return Some { @@ -1210,8 +1174,7 @@ type internal TransparentCompiler // we need to do it differently // |> Set.contains (NodeToTypeCheck.ArtificialImplFile(index - 1)) //then - // TODO: this can actually happen, probably related to adding new files or moving files around - //failwith $"Oops???" + // failwith $"Oops???" let partialResult, tcState = finisher tcInfo.tcState @@ -1593,6 +1556,12 @@ type internal TransparentCompiler else None + // TODO: This kinda works, but the problem is that in order to switch a project to "in-memory" mode + // - some file needs to be edited (this tirggers a re-check, but LastModifiedTimeOnDisk won't change) + // - saved (this will not trigger anything) + // - and then another change has to be made (to any file buffer) - so that recheck is triggered and we get here again + // Until that sequence happens the project will be used from disk (if available). + // To get around it we probably need to detect changes made in the editor and record a timestamp for them. let shouldUseOnDisk = availableOnDiskModifiedTime |> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index 7b4cace12d1..b1253307cb3 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -45,7 +45,7 @@ type public FSharpChecker = /// When set to true we create a set of all identifiers for each parsed file which can be used to speed up finding references. /// Default: FileSystem. You can use Custom source to provide a function that will return the source for a given file path instead of reading it from the file system. Note that with this option the FSharpChecker will also not monitor the file system for file changes. It will expect to be notified of changes via the NotifyFileChanged method. /// Default: true. Indicates whether to keep parsing results in a cache. - /// Default: false. Indicates whether we use a new experimental background compiler. + /// Default: false. Indicates whether we use a new experimental background compiler. This does not yet support all features static member Create: ?projectCacheSize: int * ?keepAssemblyContents: bool * @@ -62,7 +62,7 @@ type public FSharpChecker = DocumentSource * [] ?useSyntaxTreeCache: bool * - [] ?useTransparentCompiler: bool -> + [] ?useTransparentCompiler: bool -> FSharpChecker member internal UsesTransparentCompiler: bool @@ -108,7 +108,7 @@ type public FSharpChecker = Async [] - member internal ParseFile: + member ParseFile: fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async /// @@ -205,7 +205,7 @@ type public FSharpChecker = Async [] - member internal ParseAndCheckFileInProject: + member ParseAndCheckFileInProject: fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async @@ -220,7 +220,7 @@ type public FSharpChecker = member ParseAndCheckProject: options: FSharpProjectOptions * ?userOpName: string -> Async [] - member internal ParseAndCheckProject: + member ParseAndCheckProject: projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async /// @@ -344,7 +344,7 @@ type public FSharpChecker = Async [] - member internal FindBackgroundReferencesInFile: + member FindBackgroundReferencesInFile: fileName: string * projectSnapshot: FSharpProjectSnapshot * symbol: FSharpSymbol * ?userOpName: string -> Async @@ -369,7 +369,7 @@ type public FSharpChecker = /// The project snapshot for which we want to get the semantic classification. /// An optional string used for tracing compiler operations associated with this request. [] - member internal GetBackgroundSemanticClassificationForFile: + member GetBackgroundSemanticClassificationForFile: fileName: string * snapshot: FSharpProjectSnapshot * ?userOpName: string -> Async diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index f79c8eb5913..a20c9248d1c 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2049,14 +2049,19 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualCheckFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualParseFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer] CheckFileInProject(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] GetBackgroundParseResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpParsingOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFileInProject(System.String, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] CheckFileInProjectAllowingStaleCachedResults(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyFileChanged(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyProjectCleaned(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharpProjectSnapshot, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]]] GetProjectOptionsFromScript(System.String, FSharp.Compiler.Text.ISourceText, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.String[]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Int64], Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2309,7 +2314,6 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.Threadin FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Replace(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]) -FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot] GetFileSnapshotFromDisk[a](a, System.String) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]]], Microsoft.FSharp.Core.FSharpOption`1[System.Collections.Generic.Dictionary`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot]]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, System.String, Int32, FSharp.Compiler.Text.ISourceText) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index f79c8eb5913..a20c9248d1c 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2049,14 +2049,19 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualCheckFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Int32 get_ActualParseFileCount() FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer] CheckFileInProject(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckProject(FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] GetBackgroundParseResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpParsingOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFile(System.String, FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults] ParseFileInProject(System.String, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] CheckFileInProjectAllowingStaleCachedResults(FSharp.Compiler.CodeAnalysis.FSharpParseFileResults, System.String, Int32, System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetBackgroundSemanticClassificationForFile(System.String, FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyFileChanged(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] NotifyProjectCleaned(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] FindBackgroundReferencesInFile(System.String, FSharpProjectSnapshot, FSharp.Compiler.Symbols.FSharpSymbol, Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, FSharpProjectSnapshot, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileAnswer]] ParseAndCheckFileInProject(System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetBackgroundCheckResultsForFileInProject(System.String, FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]]] GetProjectOptionsFromScript(System.String, FSharp.Compiler.Text.ISourceText, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.String[]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Int64], Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2309,7 +2314,6 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.Threadin FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Replace(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]) -FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot] GetFileSnapshotFromDisk[a](a, System.String) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]]], Microsoft.FSharp.Core.FSharpOption`1[System.Collections.Generic.Dictionary`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot]]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, System.String, Int32, FSharp.Compiler.Text.ISourceText) From 1c7217243104373864101526314d74570066ec4a Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 10 Jan 2024 13:19:36 +0100 Subject: [PATCH 209/222] f --- src/Compiler/Service/FSharpProjectSnapshot.fs | 18 ++++++++++++------ src/Compiler/Service/TransparentCompiler.fs | 4 ++-- src/Compiler/Service/service.fsi | 3 ++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index e5e610fc47c..83a5b39ff08 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -68,7 +68,10 @@ type FSharpFileSnapshot(FileName: string, Version: string, GetSource: unit -> Ta FSharpFileSnapshot( fileName, FileSystem.GetLastWriteTimeShim(fileName).Ticks.ToString(), - fun () -> FileSystem.OpenFileForReadShim(fileName).ReadAllText() |> SourceTextNew.ofString |> Task.FromResult + fun () -> + FileSystem.OpenFileForReadShim(fileName).ReadAllText() + |> SourceTextNew.ofString + |> Task.FromResult ) member public _.FileName = FileName @@ -281,12 +284,15 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj member _.GetLastModifiedTimeOnDisk() = seq { projectCore.ProjectFileName - yield! - sourceFiles + + yield! + sourceFiles |> Seq.filter (fun x -> not (x.FileName.EndsWith(".AssemblyInfo.fs"))) // TODO: is this safe? any better way of doing this? - |> Seq.filter (fun x -> not (x.FileName.EndsWith(".AssemblyAttributes.fs"))) + |> Seq.filter (fun x -> not (x.FileName.EndsWith(".AssemblyAttributes.fs"))) |> Seq.map (fun x -> x.FileName) - } |> Seq.map FileSystem.GetLastWriteTimeShim |> Seq.max + } + |> Seq.map FileSystem.GetLastWriteTimeShim + |> Seq.max member _.FullVersion = fullHash.Value member _.SignatureVersion = signatureHash.Value |> fst @@ -557,7 +563,7 @@ and [] FSha static member internal GetFileSnapshotFromDisk _ fileName = FSharpFileSnapshot.CreateFromFileSystem fileName |> async.Return - + static member FromOptions(options: FSharpProjectOptions) = FSharpProjectSnapshot.FromOptions(options, FSharpProjectSnapshot.GetFileSnapshotFromDisk) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 7d1080e5c89..f58f791e812 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1556,10 +1556,10 @@ type internal TransparentCompiler else None - // TODO: This kinda works, but the problem is that in order to switch a project to "in-memory" mode + // TODO: This kinda works, but the problem is that in order to switch a project to "in-memory" mode // - some file needs to be edited (this tirggers a re-check, but LastModifiedTimeOnDisk won't change) // - saved (this will not trigger anything) - // - and then another change has to be made (to any file buffer) - so that recheck is triggered and we get here again + // - and then another change has to be made (to any file buffer) - so that recheck is triggered and we get here again // Until that sequence happens the project will be used from disk (if available). // To get around it we probably need to detect changes made in the editor and record a timestamp for them. let shouldUseOnDisk = diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index b1253307cb3..40f0ddff858 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -62,7 +62,8 @@ type public FSharpChecker = DocumentSource * [] ?useSyntaxTreeCache: bool * - [] ?useTransparentCompiler: bool -> + [] ?useTransparentCompiler: + bool -> FSharpChecker member internal UsesTransparentCompiler: bool From 3c97e898a832e8e47eea86523064d7112feacd8f Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 10 Jan 2024 13:31:12 +0100 Subject: [PATCH 210/222] Removed TaskAgent --- src/Compiler/FSharp.Compiler.Service.fsproj | 1 - src/Compiler/Facilities/TaskAgent.fs | 92 ------------------- .../CompilerService/TaskAgent.fs | 81 ---------------- .../FSharp.Compiler.ComponentTests.fsproj | 70 +++++--------- 4 files changed, 23 insertions(+), 221 deletions(-) delete mode 100644 src/Compiler/Facilities/TaskAgent.fs delete mode 100644 tests/FSharp.Compiler.ComponentTests/CompilerService/TaskAgent.fs diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 1ab3319eb63..328ddc5ed6b 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -161,7 +161,6 @@ - diff --git a/src/Compiler/Facilities/TaskAgent.fs b/src/Compiler/Facilities/TaskAgent.fs deleted file mode 100644 index 0f978343af8..00000000000 --- a/src/Compiler/Facilities/TaskAgent.fs +++ /dev/null @@ -1,92 +0,0 @@ -module internal Internal.Utilities.TaskAgent - -open System.Threading -open System.Threading.Tasks - -open System.Collections.Concurrent -open System - -type AgentMessage<'Message, 'MessageNoReply, 'Reply> = - | ExpectsReply of 'Message * TaskCompletionSource<'Reply> - | DoNotReply of 'MessageNoReply - -[] -type TaskInbox<'Msg, 'MsgNoReply, 'Reply>() = - - let queue = ConcurrentQueue>() - - let messageNotifications = new SemaphoreSlim(0) - - member _.PostAndAwaitReply(msg) = - let replySource = TaskCompletionSource<'Reply>() - - queue.Enqueue(ExpectsReply(msg, replySource)) - - messageNotifications.Release() |> ignore - - replySource.Task - - member _.Post(msg) = - queue.Enqueue(DoNotReply msg) - messageNotifications.Release() |> ignore - - member _.Receive() = - task { - do! messageNotifications.WaitAsync() - - return - match queue.TryDequeue() with - | true, msg -> msg - | false, _ -> failwith "Message notifications broken" - } - - interface IDisposable with - member _.Dispose() = messageNotifications.Dispose() - -[] -type TaskAgent<'Msg, 'MsgNoReply, 'Reply> - (processMessage: ('MsgNoReply -> unit) -> 'Msg -> 'Reply, processMessageNoReply: ('MsgNoReply -> unit) -> 'MsgNoReply -> unit) = - let inbox = new TaskInbox<'Msg, 'MsgNoReply, 'Reply>() - - let exceptionEvent = new Event<_>() - - let mutable running = true - - let _loop = - backgroundTask { - while running do - match! inbox.Receive() with - | ExpectsReply(msg, replySource) -> - try - let reply = processMessage inbox.Post msg - replySource.SetResult reply - with ex -> - replySource.SetException ex - - | DoNotReply msg -> - try - processMessageNoReply inbox.Post msg - with ex -> - exceptionEvent.Trigger(msg, ex) - } - - member _.NoReplyExceptions = exceptionEvent.Publish - - member _.Status = _loop.Status - - member _.PostAndAwaitReply(msg) = - if not running then - failwith "Agent has been disposed and is no longer processing messages" - - inbox.PostAndAwaitReply(msg) - - member _.Post(msg) = - if not running then - failwith "Agent has been disposed and is no longer processing messages" - - inbox.Post(msg) - - interface IDisposable with - member _.Dispose() = - running <- false - (inbox :> IDisposable).Dispose() diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/TaskAgent.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/TaskAgent.fs deleted file mode 100644 index 8952e6dce1d..00000000000 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/TaskAgent.fs +++ /dev/null @@ -1,81 +0,0 @@ -module CompilerService.TaskAgent -open Internal.Utilities.TaskAgent - -open Xunit -open System -open System.Threading.Tasks -open System.Runtime.CompilerServices -open System.Threading - - -[] -let ``TaskAgent should process messages that expect a reply`` () = - // Arrange - let agent = new TaskAgent<_,_,_>( - processMessage = (fun _ msg -> msg + 1), - processMessageNoReply = (fun _ _ -> ())) - - // Act - let replyTask = agent.PostAndAwaitReply(1) - - // Assert - Assert.Equal(2, replyTask.Result) - -[] -let ``TaskAgent should process messages that do not expect a reply`` () = - // Arrange - let mutable messageProcessed = false - - let agent = new TaskAgent<_,_,_>( - processMessage = (fun _ msg -> msg + 1), - processMessageNoReply = (fun _ _ -> messageProcessed <- true)) - - // Act - agent.Post(1) - agent.PostAndAwaitReply(1).Wait() - - // Assert - Assert.True(messageProcessed) - -[] -let ``TaskAgent should publish exceptions that occur while processing messages that do not expect a reply`` () = - // Arrange - let mutable exceptionPublished = false - - let agent = new TaskAgent<_,_,_>( - processMessage = (fun _ msg -> msg + 1), - processMessageNoReply = (fun _ _ -> failwith "Test exception")) - - use _ = agent.NoReplyExceptions.Subscribe(fun _ -> exceptionPublished <- true) - - // Act - agent.Post(1) - agent.PostAndAwaitReply(1).Wait() - - // Assert - Assert.True(exceptionPublished) - -[] -let ``TaskAgent should not deadlock under heavy use`` () = - // Arrange - use messagesProcessed = new ThreadLocal<_>((fun () -> ResizeArray()), true) - let agent = new TaskAgent<_,_,_>( - processMessage = (fun post msg -> - if msg > 0 then - post (msg + 1) - msg + 1), - processMessageNoReply = (fun _ msg -> - messagesProcessed.Value.Add(msg))) - let numMessages = 100000 - - // Act - let replyTasks = - [ for i in 1..numMessages do - yield agent.PostAndAwaitReply(i) ] - let replies = (replyTasks |> Task.WhenAll).Result - - // Assert - Assert.Equal(numMessages, replies.Length) - Assert.True(replies |> Seq.forall (fun r -> r > 0)) - agent.PostAndAwaitReply(0).Wait() - Assert.Equal(numMessages, messagesProcessed.Values |> Seq.sumBy (fun x -> x.Count)) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 60021b39b1e..a9dd5382893 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -32,75 +32,52 @@ FsUnit.fs - - - - - + + + + + - - + + - + - + - + - - + + - - - - - - + + + + + + - + - + - - + + - + @@ -292,7 +269,6 @@ - From 0e4643f6c2d611bc3ccfd9e47ca081cbb5ec7aae Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 10 Jan 2024 15:14:38 +0100 Subject: [PATCH 211/222] cleanup --- src/Compiler/Driver/GraphChecking/Graph.fs | 1 + src/Compiler/Facilities/prim-lexing.fsi | 1 + src/Compiler/Service/service.fs | 4 ++-- .../FSharp.Compiler.Benchmarks.fsproj | 1 - .../ProjectSnapshotBenchmarks.fs | 13 ------------- .../CompilerServiceBenchmarks/SourceText.fs | 1 - .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 2 +- .../Diagnostics/UnusedOpensDiagnosticAnalyzer.fs | 2 +- .../FSharp.UIResources/AdvancedOptionsControl.xaml | 2 +- 9 files changed, 7 insertions(+), 20 deletions(-) delete mode 100644 tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ProjectSnapshotBenchmarks.fs diff --git a/src/Compiler/Driver/GraphChecking/Graph.fs b/src/Compiler/Driver/GraphChecking/Graph.fs index bd0a3ee824a..dbe4c6b6cc7 100644 --- a/src/Compiler/Driver/GraphChecking/Graph.fs +++ b/src/Compiler/Driver/GraphChecking/Graph.fs @@ -50,6 +50,7 @@ module internal Graph = |> Array.Parallel.map (fun node -> node, graph |> transitiveDeps node) |> readOnlyDict + // TODO: optimize /// Get subgraph of the given graph that contains only nodes that are reachable from the given node. let subGraphFor node graph = let allDeps = graph |> transitiveDeps node diff --git a/src/Compiler/Facilities/prim-lexing.fsi b/src/Compiler/Facilities/prim-lexing.fsi index 1686c858d21..ff13f96c9e1 100644 --- a/src/Compiler/Facilities/prim-lexing.fsi +++ b/src/Compiler/Facilities/prim-lexing.fsi @@ -39,6 +39,7 @@ type ISourceText = /// Throws an exception when the input range is outside the file boundaries. abstract GetSubTextFromRange: range: range -> string +/// Just like ISourceText, but with a checksum. Added as a separate type to avoid breaking changes. type ISourceTextNew = inherit ISourceText diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 62feae7a72a..5bfbdd02ca5 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -284,8 +284,8 @@ type FSharpChecker member _.MatchBraces(fileName, sourceText: ISourceText, options: FSharpParsingOptions, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" - //use _ = - // Activity.start "FSharpChecker.MatchBraces" [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, userOpName |] + use _ = + Activity.start "FSharpChecker.MatchBraces" [| Activity.Tags.fileName, fileName; Activity.Tags.userOpName, userOpName |] let hash = sourceText.GetHashCode() |> int64 diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj index 57ae0a14cb2..19c7f7a7e96 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/FSharp.Compiler.Benchmarks.fsproj @@ -14,7 +14,6 @@ - diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ProjectSnapshotBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ProjectSnapshotBenchmarks.fs deleted file mode 100644 index df9f936d60c..00000000000 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/ProjectSnapshotBenchmarks.fs +++ /dev/null @@ -1,13 +0,0 @@ -module FSharp.Benchmarks.ProjectSnapshotBenchmarks - -open System.IO -open BenchmarkDotNet.Attributes -open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.Text -open FSharp.Compiler.Diagnostics -open FSharp.Test.ProjectGeneration -open BenchmarkDotNet.Engines - - -[] -let FSharpCategory = "fsharp" diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs index 817e95fd044..89d911d9b71 100644 --- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs +++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/SourceText.fs @@ -98,7 +98,6 @@ module internal SourceText = let lastLine = this.GetLineString(range.EndLine - 1) sb.Append(lastLine.Substring(0, range.EndColumn)).ToString() - } sourceText diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index ccaf60daa19..f8de2140da1 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -27,7 +27,7 @@ type internal UnusedDeclarationsAnalyzer [] () = cancellableTask { - // do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) + do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof (UnusedDeclarationsAnalyzer)) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index 61a9d9d28b0..eca26429312 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -42,7 +42,7 @@ type internal UnusedOpensDiagnosticAnalyzer [] () = Tasks.Task.FromResult(ImmutableArray.Empty) else cancellableTask { - // do Trace.TraceInformation("{0:n3} (start) UnusedOpensAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) + do Trace.TraceInformation("{0:n3} (start) UnusedOpensAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) let! sourceText = document.GetTextAsync() let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges document diff --git a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml index ce82e0bbd44..f2b95204c80 100644 --- a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml +++ b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml @@ -14,7 +14,7 @@ - + Date: Wed, 10 Jan 2024 16:05:59 +0100 Subject: [PATCH 212/222] doc --- src/Compiler/Facilities/AsyncMemoize.fs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 25450de46b9..1855f75a709 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -141,6 +141,16 @@ type internal CachingDiagnosticsLogger(originalLogger: DiagnosticsLogger option) member _.CapturedDiagnostics = capturedDiagnostics |> Seq.toList +/// +/// A cache/memoization for computations that makes sure that the same computation wil only be computed once even if it's needed +/// at multiple places/times. +/// +/// Strongly holds at most one result per key. +/// +/// Maximum number of strongly held results to keep in the cache +/// Maximum number of weakly held results to keep in the cache +/// Name of the cache - used in tracing messages +/// If true, when a job is started, all other jobs with the same key will be canceled. [] type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> (?keepStrongly, ?keepWeakly, ?name: string, ?cancelDuplicateRunningJobs: bool) = @@ -298,7 +308,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let ex = exn (message) failures.Add(key, ex) Interlocked.Increment &errors |> ignore - // raise ex -- Suppose there's no need to raise here - where does it even go? + // raise ex -- Suppose there's no need to raise here - where does it even go? let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = task { @@ -586,6 +596,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T $"{locked}{running}{cache.DebuggerDisplay} {stats}{avgDuration}" +/// A drop-in replacement for AsyncMemoize that disables caching and just runs the computation every time. [] type internal AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> (?keepStrongly, ?keepWeakly, ?name: string, ?cancelDuplicateRunningJobs: bool) = From ba717599f61408d7e99e77810a1490ce3827aaf4 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 10 Jan 2024 17:05:50 +0100 Subject: [PATCH 213/222] signatures --- src/Compiler/FSharp.Compiler.Service.fsproj | 6 + src/Compiler/Facilities/AsyncMemoize.fs | 12 +- src/Compiler/Facilities/AsyncMemoize.fsi | 78 ++++++ src/Compiler/Facilities/Hashing.fsi | 46 ++++ src/Compiler/Service/BackgroundCompiler.fsi | 236 +++++++++++++++++++ src/Compiler/Service/TransparentCompiler.fs | 1 - src/Compiler/Service/TransparentCompiler.fsi | 175 ++++++++++++++ src/Compiler/Utilities/LruCache.fsi | 48 ++++ 8 files changed, 590 insertions(+), 12 deletions(-) create mode 100644 src/Compiler/Facilities/AsyncMemoize.fsi create mode 100644 src/Compiler/Facilities/Hashing.fsi create mode 100644 src/Compiler/Service/BackgroundCompiler.fsi create mode 100644 src/Compiler/Service/TransparentCompiler.fsi create mode 100644 src/Compiler/Utilities/LruCache.fsi diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 328ddc5ed6b..86389d684b1 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -21,6 +21,7 @@ $(OtherFlags) --warnon:3218 $(OtherFlags) --warnon:3390 + true $(IntermediateOutputPath)$(TargetFramework)\ $(IntermediateOutputPath)$(TargetFramework)\ @@ -126,6 +127,7 @@ + @@ -147,6 +149,7 @@ + @@ -160,6 +163,7 @@ + @@ -499,7 +503,9 @@ + + diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 1855f75a709..825d23c3857 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -141,16 +141,6 @@ type internal CachingDiagnosticsLogger(originalLogger: DiagnosticsLogger option) member _.CapturedDiagnostics = capturedDiagnostics |> Seq.toList -/// -/// A cache/memoization for computations that makes sure that the same computation wil only be computed once even if it's needed -/// at multiple places/times. -/// -/// Strongly holds at most one result per key. -/// -/// Maximum number of strongly held results to keep in the cache -/// Maximum number of weakly held results to keep in the cache -/// Name of the cache - used in tracing messages -/// If true, when a job is started, all other jobs with the same key will be canceled. [] type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> (?keepStrongly, ?keepWeakly, ?name: string, ?cancelDuplicateRunningJobs: bool) = @@ -308,7 +298,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let ex = exn (message) failures.Add(key, ex) Interlocked.Increment &errors |> ignore - // raise ex -- Suppose there's no need to raise here - where does it even go? + // raise ex -- Suppose there's no need to raise here - where does it even go? let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = task { diff --git a/src/Compiler/Facilities/AsyncMemoize.fsi b/src/Compiler/Facilities/AsyncMemoize.fsi new file mode 100644 index 00000000000..bd27a35ac5b --- /dev/null +++ b/src/Compiler/Facilities/AsyncMemoize.fsi @@ -0,0 +1,78 @@ +namespace Internal.Utilities.Collections + +open System.Threading.Tasks +open FSharp.Compiler.BuildGraph + +[] +module internal Utils = + + /// Return file name with one directory above it + val shortPath: path: string -> string + + val (|TaskCancelled|_|): ex: exn -> TaskCanceledException option + +type internal JobEvent = + | Started + | Finished + | Canceled + | Evicted + | Collected + | Weakened + | Strengthened + | Failed + +type internal ICacheKey<'TKey, 'TVersion> = + + abstract GetKey: unit -> 'TKey + + abstract GetLabel: unit -> string + + abstract GetVersion: unit -> 'TVersion + +[] +type Extensions = + + [] + static member internal WithExtraVersion: cacheKey: ICacheKey<'a, 'b> * extraVersion: 'c -> ICacheKey<'a, ('b * 'c)> + +type internal AsyncLock = + interface System.IDisposable + + new: unit -> AsyncLock + + member Do: f: (unit -> #Task<'b>) -> Task<'b> + +/// +/// A cache/memoization for computations that makes sure that the same computation wil only be computed once even if it's needed +/// at multiple places/times. +/// +/// Strongly holds at most one result per key. +/// +type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> = + + /// Maximum number of strongly held results to keep in the cache + /// Maximum number of weakly held results to keep in the cache + /// Name of the cache - used in tracing messages + /// If true, when a job is started, all other jobs with the same key will be canceled. + new: + ?keepStrongly: int * ?keepWeakly: int * ?name: string * ?cancelDuplicateRunningJobs: bool -> + AsyncMemoize<'TKey, 'TVersion, 'TValue> + + member Clear: unit -> unit + + member Get: key: ICacheKey<'TKey, 'TVersion> * computation: NodeCode<'TValue> -> NodeCode<'TValue> + + member Get': key: 'TKey * computation: NodeCode<'TValue> -> NodeCode<'TValue> + + member Event: IEvent + + member OnEvent: ((JobEvent * (string * 'TKey * 'TVersion) -> unit) -> unit) + +/// A drop-in replacement for AsyncMemoize that disables caching and just runs the computation every time. +type internal AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality> = + + new: + ?keepStrongly: obj * ?keepWeakly: obj * ?name: string * ?cancelDuplicateRunningJobs: bool -> + AsyncMemoizeDisabled<'TKey, 'TVersion, 'TValue> + + member Get: _key: ICacheKey<'a, 'b> * computation: 'c -> 'c diff --git a/src/Compiler/Facilities/Hashing.fsi b/src/Compiler/Facilities/Hashing.fsi new file mode 100644 index 00000000000..121afb29eb2 --- /dev/null +++ b/src/Compiler/Facilities/Hashing.fsi @@ -0,0 +1,46 @@ +namespace Internal.Utilities.Hashing + +/// Tools for hashing things with MD5 into a string that can be used as a cache key. +module internal Md5StringHasher = + + val hashString: s: string -> byte array + + val empty: string + + val addBytes: bytes: byte array -> s: string -> string + + val addString: s: string -> s2: string -> string + + val addSeq: items: 'item seq -> addItem: ('item -> string -> string) -> s: string -> string + + val addStrings: strings: string seq -> (string -> string) + + val addBool: b: bool -> s: string -> string + + val addDateTime: dt: System.DateTime -> s: string -> string + +module internal Md5Hasher = + + val computeHash: bytes: byte array -> byte array + + val empty: 'a array + + val hashString: s: string -> byte array + + val addBytes: bytes: byte array -> s: byte array -> byte array + + val addString: s: string -> s2: byte array -> byte array + + val addSeq: items: 'item seq -> addItem: ('item -> byte array -> byte array) -> s: byte array -> byte array + + val addStrings: strings: string seq -> (byte array -> byte array) + + val addBytes': bytes: byte array seq -> (byte array -> byte array) + + val addBool: b: bool -> s: byte array -> byte array + + val addDateTime: dt: System.DateTime -> s: byte array -> byte array + + val addDateTimes: dts: System.DateTime seq -> s: byte array -> byte array + + val toString: bytes: byte array -> string diff --git a/src/Compiler/Service/BackgroundCompiler.fsi b/src/Compiler/Service/BackgroundCompiler.fsi new file mode 100644 index 00000000000..d2d6053ead6 --- /dev/null +++ b/src/Compiler/Service/BackgroundCompiler.fsi @@ -0,0 +1,236 @@ +namespace FSharp.Compiler.CodeAnalysis + +open FSharp.Compiler.Text +open FSharp.Compiler.BuildGraph + +open System.Reflection +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CompilerConfig +open FSharp.Compiler.Diagnostics + +type SourceTextHash = int64 + +type CacheStamp = int64 + +type FileName = string + +type FilePath = string + +type ProjectPath = string + +type FileVersion = int + +type FSharpProjectSnapshot = ProjectSnapshot.FSharpProjectSnapshot + +type internal IBackgroundCompiler = + + /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. + abstract CheckFileInProject: + parseResults: FSharpParseFileResults * + fileName: string * + fileVersion: int * + sourceText: ISourceText * + options: FSharpProjectOptions * + userOpName: string -> + NodeCode + + /// Type-check the result obtained by parsing, but only if the antecedent type checking context is available. + abstract CheckFileInProjectAllowingStaleCachedResults: + parseResults: FSharpParseFileResults * + fileName: string * + fileVersion: int * + sourceText: ISourceText * + options: FSharpProjectOptions * + userOpName: string -> + NodeCode + + abstract ClearCache: options: FSharpProjectOptions seq * userOpName: string -> unit + + abstract ClearCaches: unit -> unit + + abstract DownsizeCaches: unit -> unit + + abstract FindReferencesInFile: + fileName: string * + projectSnapshot: FSharpProjectSnapshot * + symbol: FSharp.Compiler.Symbols.FSharpSymbol * + userOpName: string -> + NodeCode + + abstract FindReferencesInFile: + fileName: string * + options: FSharpProjectOptions * + symbol: FSharp.Compiler.Symbols.FSharpSymbol * + canInvalidateProject: bool * + userOpName: string -> + NodeCode + + abstract GetAssemblyData: + projectSnapshot: FSharpProjectSnapshot * outputFileName: string * userOpName: string -> + NodeCode + + abstract GetAssemblyData: + options: FSharpProjectOptions * outputFileName: string * userOpName: string -> + NodeCode + + /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) + abstract GetBackgroundCheckResultsForFileInProject: + fileName: string * options: FSharpProjectOptions * userOpName: string -> + NodeCode + + /// Fetch the parse information from the background compiler (which checks w.r.t. the FileSystem API) + abstract GetBackgroundParseResultsForFileInProject: + fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode + + abstract GetCachedCheckFileResult: + builder: IncrementalBuilder * fileName: string * sourceText: ISourceText * options: FSharpProjectOptions -> + NodeCode<(FSharpParseFileResults * FSharpCheckFileResults) option> + + abstract GetProjectOptionsFromScript: + fileName: string * + sourceText: ISourceText * + previewEnabled: bool option * + loadedTimeStamp: System.DateTime option * + otherFlags: string array option * + useFsiAuxLib: bool option * + useSdkRefs: bool option * + sdkDirOverride: string option * + assumeDotNetFramework: bool option * + optionsStamp: int64 option * + userOpName: string -> + Async + + abstract GetSemanticClassificationForFile: + fileName: string * snapshot: FSharpProjectSnapshot * userOpName: string -> + NodeCode + + abstract GetSemanticClassificationForFile: + fileName: string * options: FSharpProjectOptions * userOpName: string -> + NodeCode + + abstract InvalidateConfiguration: options: FSharpProjectOptions * userOpName: string -> unit + + abstract NotifyFileChanged: fileName: string * options: FSharpProjectOptions * userOpName: string -> NodeCode + + abstract NotifyProjectCleaned: options: FSharpProjectOptions * userOpName: string -> Async + + abstract ParseAndCheckFileInProject: + fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> + NodeCode + + /// Parses and checks the source file and returns untyped AST and check results. + abstract ParseAndCheckFileInProject: + fileName: string * + fileVersion: int * + sourceText: ISourceText * + options: FSharpProjectOptions * + userOpName: string -> + NodeCode + + abstract ParseAndCheckProject: + projectSnapshot: FSharpProjectSnapshot * userOpName: string -> NodeCode + + /// Parse and typecheck the whole project. + abstract ParseAndCheckProject: + options: FSharpProjectOptions * userOpName: string -> NodeCode + + abstract ParseFile: + fileName: string * projectSnapshot: FSharpProjectSnapshot * userOpName: string -> Async + + abstract ParseFile: + fileName: string * + sourceText: ISourceText * + options: FSharpParsingOptions * + cache: bool * + flatErrors: bool * + userOpName: string -> + Async + + /// Try to get recent approximate type check results for a file. + abstract TryGetRecentCheckResultsForFile: + fileName: string * options: FSharpProjectOptions * sourceText: ISourceText option * userOpName: string -> + (FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash) option + + abstract BeforeBackgroundFileCheck: IEvent + + abstract FileChecked: IEvent + + abstract FileParsed: IEvent + + abstract FrameworkImportsCache: FrameworkImportsCache + + abstract ProjectChecked: IEvent + +type internal ParseCacheLockToken = + interface Internal.Utilities.Library.LockToken + + new: unit -> ParseCacheLockToken + +type internal ScriptClosureCacheToken = + interface Internal.Utilities.Library.LockToken + + new: unit -> ScriptClosureCacheToken + +type CheckFileCacheKey = FileName * SourceTextHash * FSharpProjectOptions + +type CheckFileCacheValue = FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash * System.DateTime + +[] +module internal EnvMisc = + + val braceMatchCacheSize: int + + val parseFileCacheSize: int + + val checkFileInProjectCacheSize: int + + val projectCacheSizeDefault: int + + val frameworkTcImportsCacheStrongSize: int + +[] +module internal Helpers = + + /// Determine whether two (fileName,options) keys are identical w.r.t. affect on checking + val AreSameForChecking2: (string * FSharpProjectOptions) * (string * FSharpProjectOptions) -> bool + + /// Determine whether two (fileName,options) keys should be identical w.r.t. resource usage + val AreSubsumable2: (string * FSharpProjectOptions) * (string * FSharpProjectOptions) -> bool + + /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. parsing + val AreSameForParsing: (string * int64 * 'a) * (string * int64 * 'a) -> bool when 'a: equality + + val AreSimilarForParsing: ('a * 'b * 'c) * ('a * 'd * 'e) -> bool when 'a: equality + + /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. checking + val AreSameForChecking3: (string * int64 * FSharpProjectOptions) * (string * int64 * FSharpProjectOptions) -> bool + + /// Determine whether two (fileName,sourceText,options) keys should be identical w.r.t. resource usage + val AreSubsumable3: (string * 'a * FSharpProjectOptions) * (string * 'b * FSharpProjectOptions) -> bool + + /// If a symbol is an attribute check if given set of names contains its name without the Attribute suffix + val NamesContainAttribute: symbol: FSharp.Compiler.Symbols.FSharpSymbol -> names: Set -> bool + +type internal BackgroundCompiler = + interface IBackgroundCompiler + + new: + legacyReferenceResolver: LegacyReferenceResolver * + projectCacheSize: int * + keepAssemblyContents: bool * + keepAllBackgroundResolutions: bool * + tryGetMetadataSnapshot: FSharp.Compiler.AbstractIL.ILBinaryReader.ILReaderTryGetMetadataSnapshot * + suggestNamesForErrors: bool * + keepAllBackgroundSymbolUses: bool * + enableBackgroundItemKeyStoreAndSemanticClassification: bool * + enablePartialTypeChecking: bool * + parallelReferenceResolution: ParallelReferenceResolution * + captureIdentifiersWhenParsing: bool * + getSource: (string -> Async) option * + useChangeNotifications: bool * + useSyntaxTreeCache: bool -> + BackgroundCompiler + + static member ActualCheckFileCount: int + + static member ActualParseFileCount: int diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index f58f791e812..dfaa36be2c9 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -45,7 +45,6 @@ open System.Threading open Internal.Utilities.Hashing open FSharp.Compiler.CodeAnalysis.ProjectSnapshot -open System.Runtime.Serialization.Formatters.Binary /// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. [] diff --git a/src/Compiler/Service/TransparentCompiler.fsi b/src/Compiler/Service/TransparentCompiler.fsi new file mode 100644 index 00000000000..00167ccc67f --- /dev/null +++ b/src/Compiler/Service/TransparentCompiler.fsi @@ -0,0 +1,175 @@ +namespace FSharp.Compiler.CodeAnalysis.TransparentCompiler + +open Internal.Utilities.Collections + +open FSharp.Compiler.AbstractIL.ILBinaryReader +open FSharp.Compiler.BuildGraph +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CompilerConfig +open FSharp.Compiler.CompilerImports +open FSharp.Compiler.CheckBasics +open FSharp.Compiler.Diagnostics +open FSharp.Compiler.DiagnosticsLogger +open FSharp.Compiler.ScriptClosure +open FSharp.Compiler.Symbols +open FSharp.Compiler.TcGlobals +open FSharp.Compiler.Text +open FSharp.Compiler.ParseAndCheckInputs +open FSharp.Compiler.GraphChecking +open FSharp.Compiler.Syntax +open FSharp.Compiler.NameResolution +open FSharp.Compiler.TypedTree +open FSharp.Compiler.CheckDeclarations +open FSharp.Compiler.EditorServices + +/// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. +[] +type internal TcInfo = + { + tcState: TcState + tcEnvAtEndOfFile: TcEnv + + /// Disambiguation table for module names + moduleNamesDict: ModuleNamesDict + + topAttribs: TopAttribs option + + latestCcuSigForFile: ModuleOrNamespaceType option + + /// Accumulated diagnostics, last file first + tcDiagnosticsRev: (PhasedDiagnostic * FSharpDiagnosticSeverity)[] list + + tcDependencyFiles: string list + + sigNameOpt: (string * QualifiedNameOfFile) option + + graphNode: NodeToTypeCheck option + + stateContainsNodes: Set + + sink: TcResultsSinkImpl list + } + +[] +type internal TcIntermediate = + { + finisher: Finisher + + /// Disambiguation table for module names + moduleNamesDict: ModuleNamesDict + + /// Accumulated diagnostics, last file first + tcDiagnosticsRev: (PhasedDiagnostic * FSharpDiagnosticSeverity) array list + tcDependencyFiles: string list + sink: TcResultsSinkImpl + } + +/// Things we need to start parsing and checking files for a given project snapshot +type internal BootstrapInfo = + { Id: int + AssemblyName: string + OutFile: string + TcConfig: TcConfig + TcImports: TcImports + TcGlobals: TcGlobals + InitialTcInfo: TcInfo + LoadedSources: (range * ProjectSnapshot.FSharpFileSnapshot) list + LoadClosure: LoadClosure option + LastFileName: string } + +type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string + +[] +type internal DependencyGraphType = + + /// A dependency graph for a single file - it will be missing files which this file does not depend on + | File + + /// A dependency graph for a project - it will contain all files in the project + | Project + +[] +type internal Extensions = + + [] + static member Key: + fileSnapshots: #ProjectSnapshot.IFileSnapshot list * ?extraKeyFlag: DependencyGraphType -> + ICacheKey<(DependencyGraphType option * byte array), string> + +type internal CompilerCaches = + + new: sizeFactor: int -> CompilerCaches + + member AssemblyData: AsyncMemoize<(string * string), (string * string), ProjectAssemblyDataResult> + + member BootstrapInfo: AsyncMemoize<(string * string), string, (BootstrapInfo option * FSharpDiagnostic array)> + + member BootstrapInfoStatic: + AsyncMemoize<(string * string), (string * string), (int * TcImports * TcGlobals * TcInfo * Event)> + + member DependencyGraph: + AsyncMemoize<(DependencyGraphType option * byte array), string, (Graph * Graph)> + + member FrameworkImports: AsyncMemoize + + member ItemKeyStore: AsyncMemoize<(string * (string * string)), string, ItemKeyStore option> + + member ParseAndCheckAllFilesInProject: AsyncMemoizeDisabled + + member ParseAndCheckFileInProject: + AsyncMemoize<(string * (string * string)), string, (FSharpParseFileResults * FSharpCheckFileAnswer)> + + member ParseAndCheckProject: AsyncMemoize<(string * string), string, FSharpCheckProjectResults> + + member ParseFile: + AsyncMemoize<((string * string) * string), (string * string * bool), ProjectSnapshot.FSharpParsedFile> + + member ProjectExtras: AsyncMemoizeDisabled + + member SemanticClassification: AsyncMemoize<(string * (string * string)), string, SemanticClassificationView option> + + member SizeFactor: int + + member TcIntermediate: AsyncMemoize<(string * (string * string)), (string * int), TcIntermediate> + + member TcLastFile: AsyncMemoizeDisabled + +type internal TransparentCompiler = + interface IBackgroundCompiler + + new: + legacyReferenceResolver: LegacyReferenceResolver * + projectCacheSize: int * + keepAssemblyContents: bool * + keepAllBackgroundResolutions: bool * + tryGetMetadataSnapshot: ILReaderTryGetMetadataSnapshot * + suggestNamesForErrors: bool * + keepAllBackgroundSymbolUses: bool * + enableBackgroundItemKeyStoreAndSemanticClassification: bool * + enablePartialTypeChecking: bool * + parallelReferenceResolution: ParallelReferenceResolution * + captureIdentifiersWhenParsing: bool * + getSource: (string -> Async) option * + useChangeNotifications: bool * + useSyntaxTreeCache: bool -> + TransparentCompiler + + member FindReferencesInFile: + fileName: string * projectSnapshot: ProjectSnapshot.ProjectSnapshot * symbol: FSharpSymbol * userOpName: string -> + NodeCode + + member GetAssemblyData: + projectSnapshot: ProjectSnapshot.ProjectSnapshot * fileName: string * _userOpName: string -> + NodeCode + + member ParseAndCheckFileInProject: + fileName: string * projectSnapshot: ProjectSnapshot.ProjectSnapshot * userOpName: string -> + NodeCode + + member ParseFile: + fileName: string * projectSnapshot: ProjectSnapshot.ProjectSnapshot * _userOpName: 'a -> + NodeCode + + member SetCacheSizeFactor: sizeFactor: int -> unit + + member Caches: CompilerCaches diff --git a/src/Compiler/Utilities/LruCache.fsi b/src/Compiler/Utilities/LruCache.fsi new file mode 100644 index 00000000000..e204e682788 --- /dev/null +++ b/src/Compiler/Utilities/LruCache.fsi @@ -0,0 +1,48 @@ +namespace Internal.Utilities.Collections + +[] +type internal CacheEvent = + | Evicted + | Collected + | Weakened + | Strengthened + +/// A cache where least recently used items are removed when the cache is full. +/// +/// It's also versioned, meaning each key can have multiple versions and only the latest one is kept strongly. +/// Older versions are kept weakly and can be collected by GC. +type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVersion: equality and 'TValue: not struct> = + + /// Maximum number of strongly held results to keep in the cache + /// Maximum number of weakly held results to keep in the cache + /// A predicate that determines if a value should be kept strongly (no matter what) + /// An event that is called when an item is evicted, collected, weakened or strengthened + new: + keepStrongly: int * + ?keepWeakly: int * + ?requiredToKeep: ('TValue -> bool) * + ?event: (CacheEvent -> string * 'TKey * 'TVersion -> unit) -> + LruCache<'TKey, 'TVersion, 'TValue> + + member Clear: unit -> unit + + /// Returns an option of a value for given key and version, and also a list of all other versions for given key + member GetAll: key: 'TKey * version: 'TVersion -> 'TValue option * ('TVersion * 'TValue) list + + member GetValues: unit -> (string * 'TVersion * 'TValue) seq + + member Remove: key: 'TKey -> unit + + member Remove: key: 'TKey * version: 'TVersion -> unit + + member Set: key: 'TKey * value: 'TValue -> unit + + member Set: key: 'TKey * version: 'TVersion * value: 'TValue -> unit + + member Set: key: 'TKey * version: 'TVersion * label: string * value: 'TValue -> unit + + member TryGet: key: 'TKey -> 'TValue option + + member TryGet: key: 'TKey * version: 'TVersion -> 'TValue option + + member DebuggerDisplay: string From 3e9d961d7fc7b46d12cd574b6a4d636b8b194274 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 10 Jan 2024 17:21:36 +0100 Subject: [PATCH 214/222] cleanup --- src/Compiler/Service/BackgroundCompiler.fs | 3 --- src/Compiler/Service/BackgroundCompiler.fsi | 14 -------------- 2 files changed, 17 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index b351df0b821..52f4c061864 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -174,9 +174,6 @@ type internal IBackgroundCompiler = type internal ParseCacheLockToken() = interface LockToken -type internal ScriptClosureCacheToken() = - interface LockToken - type CheckFileCacheKey = FileName * SourceTextHash * FSharpProjectOptions type CheckFileCacheValue = FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash * DateTime diff --git a/src/Compiler/Service/BackgroundCompiler.fsi b/src/Compiler/Service/BackgroundCompiler.fsi index d2d6053ead6..c052c5174ba 100644 --- a/src/Compiler/Service/BackgroundCompiler.fsi +++ b/src/Compiler/Service/BackgroundCompiler.fsi @@ -161,20 +161,6 @@ type internal IBackgroundCompiler = abstract ProjectChecked: IEvent -type internal ParseCacheLockToken = - interface Internal.Utilities.Library.LockToken - - new: unit -> ParseCacheLockToken - -type internal ScriptClosureCacheToken = - interface Internal.Utilities.Library.LockToken - - new: unit -> ScriptClosureCacheToken - -type CheckFileCacheKey = FileName * SourceTextHash * FSharpProjectOptions - -type CheckFileCacheValue = FSharpParseFileResults * FSharpCheckFileResults * SourceTextHash * System.DateTime - [] module internal EnvMisc = From 183f1d051a25eca531405f092c9c7259d7fec6c3 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 10 Jan 2024 18:01:01 +0100 Subject: [PATCH 215/222] settings disclaimer --- .../AdvancedOptionsControl.xaml | 2 ++ .../src/FSharp.UIResources/Strings.Designer.cs | 18 ++++++++++++++++++ .../src/FSharp.UIResources/Strings.resx | 6 ++++++ .../src/FSharp.UIResources/xlf/Strings.cs.xlf | 10 ++++++++++ .../src/FSharp.UIResources/xlf/Strings.de.xlf | 10 ++++++++++ .../src/FSharp.UIResources/xlf/Strings.es.xlf | 10 ++++++++++ .../src/FSharp.UIResources/xlf/Strings.fr.xlf | 10 ++++++++++ .../src/FSharp.UIResources/xlf/Strings.it.xlf | 10 ++++++++++ .../src/FSharp.UIResources/xlf/Strings.ja.xlf | 10 ++++++++++ .../src/FSharp.UIResources/xlf/Strings.ko.xlf | 10 ++++++++++ .../src/FSharp.UIResources/xlf/Strings.pl.xlf | 10 ++++++++++ .../FSharp.UIResources/xlf/Strings.pt-BR.xlf | 10 ++++++++++ .../src/FSharp.UIResources/xlf/Strings.ru.xlf | 10 ++++++++++ .../src/FSharp.UIResources/xlf/Strings.tr.xlf | 10 ++++++++++ .../FSharp.UIResources/xlf/Strings.zh-Hans.xlf | 10 ++++++++++ .../FSharp.UIResources/xlf/Strings.zh-Hant.xlf | 10 ++++++++++ 16 files changed, 156 insertions(+) diff --git a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml index f2b95204c80..6ff62014086 100644 --- a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml +++ b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml @@ -40,6 +40,8 @@ + + public static string TransparentCompiler_Discalimer1 { + get { + return ResourceManager.GetString("TransparentCompiler_Discalimer1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use at your own risk!. + /// + public static string TransparentCompiler_Discalimer2 { + get { + return ResourceManager.GetString("TransparentCompiler_Discalimer2", resourceCulture); + } + } + /// /// Looks up a localized string similar to Analyze and suggest fixes for unused values. /// diff --git a/vsintegration/src/FSharp.UIResources/Strings.resx b/vsintegration/src/FSharp.UIResources/Strings.resx index f139c15f73a..5557306c7e4 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.resx +++ b/vsintegration/src/FSharp.UIResources/Strings.resx @@ -306,4 +306,10 @@ Create new project snapshots from existing ones + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + Use at your own risk! + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf index 06ecd8a3cd0..adbaf55ebd1 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf index c75ef691f6a..dbfed423e3f 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf index c71ee9b4cef..68cecbe5732 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf index 77b21b77ddb..9b87037bbe1 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf index df9251bc96b..672c52b0a56 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf index 9ff08093a08..f1b2fc79855 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf index 7d567b80971..b9ec816324a 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf index dc467970ab4..d3d0dc7211f 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf index b535df1e00c..243d1792621 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf index 85cbc3d6697..f88b89e4efb 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf index f1cd83846c4..11ab3840e59 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf index 9947cfd7fec..cb38678625a 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf index fb1d95da8d0..fbe68a02e10 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf @@ -207,6 +207,16 @@ Transparent Compiler (experimental) + + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + + + + Use at your own risk! + Use at your own risk! + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor From 71ec4e04da67206c0679cd6bacd81d81e19ba7d8 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Thu, 11 Jan 2024 15:10:12 +0100 Subject: [PATCH 216/222] auto opt in for telemetry --- .../src/FSharp.Editor/Telemetry/TelemetryReporter.fs | 4 +++- .../FSharp.UIResources/AdvancedOptionsControl.xaml | 1 + .../src/FSharp.UIResources/Strings.Designer.cs | 11 ++++++++++- vsintegration/src/FSharp.UIResources/Strings.resx | 5 ++++- .../src/FSharp.UIResources/xlf/Strings.cs.xlf | 5 +++++ .../src/FSharp.UIResources/xlf/Strings.de.xlf | 5 +++++ .../src/FSharp.UIResources/xlf/Strings.es.xlf | 5 +++++ .../src/FSharp.UIResources/xlf/Strings.fr.xlf | 5 +++++ .../src/FSharp.UIResources/xlf/Strings.it.xlf | 5 +++++ .../src/FSharp.UIResources/xlf/Strings.ja.xlf | 5 +++++ .../src/FSharp.UIResources/xlf/Strings.ko.xlf | 5 +++++ .../src/FSharp.UIResources/xlf/Strings.pl.xlf | 5 +++++ .../src/FSharp.UIResources/xlf/Strings.pt-BR.xlf | 5 +++++ .../src/FSharp.UIResources/xlf/Strings.ru.xlf | 5 +++++ .../src/FSharp.UIResources/xlf/Strings.tr.xlf | 5 +++++ .../src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf | 5 +++++ .../src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf | 5 +++++ 17 files changed, 83 insertions(+), 3 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs index 2a520d72066..b841b7e8cd6 100644 --- a/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs +++ b/vsintegration/src/FSharp.Editor/Telemetry/TelemetryReporter.fs @@ -105,9 +105,11 @@ type TelemetryReporter private (name: string, props: (string * obj) array, stopw TelemetryService.DefaultSession.IsUserMicrosoftInternal else let workspace = componentModel.GetService() + let options = workspace.Services.GetService() TelemetryService.DefaultSession.IsUserMicrosoftInternal - || workspace.Services.GetService().Advanced.SendAdditionalTelemetry) + || options.Advanced.SendAdditionalTelemetry + || options.Advanced.UseTransparentCompiler) static member ReportFault(name, ?severity: FaultSeverity, ?e: exn) = if TelemetryReporter.SendAdditionalTelemetry.Value then diff --git a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml index 6ff62014086..15397d068ec 100644 --- a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml +++ b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml @@ -42,6 +42,7 @@ public static string TransparentCompiler_Discalimer1 { get { @@ -609,6 +609,15 @@ public static string TransparentCompiler_Discalimer2 { } } + /// + /// Looks up a localized string similar to By checking this you also opt-in for additional performance telemetry. + /// + public static string TransparentCompiler_Discalimer3 { + get { + return ResourceManager.GetString("TransparentCompiler_Discalimer3", resourceCulture); + } + } + /// /// Looks up a localized string similar to Analyze and suggest fixes for unused values. /// diff --git a/vsintegration/src/FSharp.UIResources/Strings.resx b/vsintegration/src/FSharp.UIResources/Strings.resx index 5557306c7e4..58821d8b8be 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.resx +++ b/vsintegration/src/FSharp.UIResources/Strings.resx @@ -307,9 +307,12 @@ Create new project snapshots from existing ones - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf index adbaf55ebd1..fa6273bf1d8 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf index dbfed423e3f..e372ab69dc9 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf index 68cecbe5732..07f10c25a33 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf index 9b87037bbe1..56d28edb642 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf index 672c52b0a56..a30c43f40cb 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf index f1b2fc79855..e16f9b25022 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf index b9ec816324a..9d5918b03d5 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf index d3d0dc7211f..d7be168642d 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf index 243d1792621..ca5d37235d0 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf index f88b89e4efb..c2eebea058c 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf index 11ab3840e59..ade67804291 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf index cb38678625a..2f9009d075b 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf index fbe68a02e10..f0d69e9ff75 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf @@ -217,6 +217,11 @@ Use at your own risk! + + By checking this you also opt-in for additional performance telemetry + By checking this you also opt-in for additional performance telemetry + + Transparent Compiler Cache Factor Transparent Compiler Cache Factor From 090c9f8195df74ae177fc0fb1c30699fd1000448 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 12 Jan 2024 13:53:31 +0100 Subject: [PATCH 217/222] option to clear cache for a given project --- src/Compiler/Facilities/AsyncMemoize.fs | 11 +++- src/Compiler/Facilities/AsyncMemoize.fsi | 3 + src/Compiler/Service/BackgroundCompiler.fs | 6 ++ src/Compiler/Service/BackgroundCompiler.fsi | 2 + src/Compiler/Service/FSharpProjectSnapshot.fs | 47 ++++++++++----- src/Compiler/Service/TransparentCompiler.fs | 26 ++++++++- src/Compiler/Service/service.fs | 4 ++ src/Compiler/Service/service.fsi | 2 + src/Compiler/Utilities/LruCache.fs | 32 ++++++++-- src/Compiler/Utilities/LruCache.fsi | 4 ++ .../CompilerService/LruCache.fs | 58 +++++++++++++++++++ ...ervice.SurfaceArea.netstandard20.debug.bsl | 20 +++++++ ...vice.SurfaceArea.netstandard20.release.bsl | 20 +++++++ .../src/FSharp.UIResources/xlf/Strings.cs.xlf | 4 +- .../src/FSharp.UIResources/xlf/Strings.de.xlf | 4 +- .../src/FSharp.UIResources/xlf/Strings.es.xlf | 4 +- .../src/FSharp.UIResources/xlf/Strings.fr.xlf | 4 +- .../src/FSharp.UIResources/xlf/Strings.it.xlf | 4 +- .../src/FSharp.UIResources/xlf/Strings.ja.xlf | 4 +- .../src/FSharp.UIResources/xlf/Strings.ko.xlf | 4 +- .../src/FSharp.UIResources/xlf/Strings.pl.xlf | 4 +- .../FSharp.UIResources/xlf/Strings.pt-BR.xlf | 4 +- .../src/FSharp.UIResources/xlf/Strings.ru.xlf | 4 +- .../src/FSharp.UIResources/xlf/Strings.tr.xlf | 4 +- .../xlf/Strings.zh-Hans.xlf | 4 +- .../xlf/Strings.zh-Hant.xlf | 4 +- 26 files changed, 239 insertions(+), 48 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 825d23c3857..ad798140cb9 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -82,6 +82,7 @@ type internal JobEvent = | Weakened | Strengthened | Failed + | Cleared type internal ICacheKey<'TKey, 'TVersion> = abstract member GetKey: unit -> 'TKey @@ -160,6 +161,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let mutable evicted = 0 let mutable collected = 0 let mutable strengthened = 0 + let mutable cleared = 0 let mutable cancel_ct_registration_original = 0 let mutable cancel_exception_original = 0 @@ -195,7 +197,11 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T | CacheEvent.Strengthened -> (fun k -> Interlocked.Increment &strengthened |> ignore - event.Trigger(JobEvent.Strengthened, k))) + event.Trigger(JobEvent.Strengthened, k)) + | CacheEvent.Cleared -> + (fun k -> + Interlocked.Increment &cleared |> ignore + event.Trigger(JobEvent.Cleared, k))) ) let requestCounts = Dictionary, int>() @@ -526,6 +532,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T member _.Clear() = cache.Clear() + member _.Clear predicate = cache.Clear predicate + member val Event = event.Publish member this.OnEvent = this.Event.Add @@ -577,6 +585,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T if restarted > 0 then $"| restarted: {restarted} " else "" if evicted > 0 then $"| evicted: {evicted} " else "" if collected > 0 then $"| collected: {collected} " else "" + if cleared > 0 then $"| cleared: {cleared} " else "" if strengthened > 0 then $"| strengthened: {strengthened} " else diff --git a/src/Compiler/Facilities/AsyncMemoize.fsi b/src/Compiler/Facilities/AsyncMemoize.fsi index bd27a35ac5b..a142605ac22 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fsi +++ b/src/Compiler/Facilities/AsyncMemoize.fsi @@ -20,6 +20,7 @@ type internal JobEvent = | Weakened | Strengthened | Failed + | Cleared type internal ICacheKey<'TKey, 'TVersion> = @@ -60,6 +61,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T member Clear: unit -> unit + member Clear: predicate: ('TKey -> bool) -> unit + member Get: key: ICacheKey<'TKey, 'TVersion> * computation: NodeCode<'TValue> -> NodeCode<'TValue> member Get': key: 'TKey * computation: NodeCode<'TValue> -> NodeCode<'TValue> diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 52f4c061864..d742a52d783 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -71,6 +71,8 @@ type internal IBackgroundCompiler = abstract member ClearCache: options: seq * userOpName: string -> unit + abstract member ClearCache: projects: ProjectSnapshot.FSharpProjectIdentifier seq * userOpName: string -> unit + abstract member ClearCaches: unit -> unit abstract member DownsizeCaches: unit -> unit @@ -1527,6 +1529,10 @@ type internal BackgroundCompiler self.CheckFileInProjectAllowingStaleCachedResults(parseResults, fileName, fileVersion, sourceText, options, userOpName) member _.ClearCache(options: seq, userOpName: string) : unit = self.ClearCache(options, userOpName) + + member _.ClearCache(projects: ProjectSnapshot.FSharpProjectIdentifier seq, userOpName: string) = + ignore (projects, userOpName) + member _.ClearCaches() : unit = self.ClearCaches() member _.DownsizeCaches() : unit = self.DownsizeCaches() member _.FileChecked: IEvent = self.FileChecked diff --git a/src/Compiler/Service/BackgroundCompiler.fsi b/src/Compiler/Service/BackgroundCompiler.fsi index c052c5174ba..f3bf3c96ccc 100644 --- a/src/Compiler/Service/BackgroundCompiler.fsi +++ b/src/Compiler/Service/BackgroundCompiler.fsi @@ -46,6 +46,8 @@ type internal IBackgroundCompiler = abstract ClearCache: options: FSharpProjectOptions seq * userOpName: string -> unit + abstract ClearCache: projects: ProjectSnapshot.FSharpProjectIdentifier seq * userOpName: string -> unit + abstract ClearCaches: unit -> unit abstract DownsizeCaches: unit -> unit diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 83a5b39ff08..eb62ca7f131 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -20,6 +20,10 @@ open FSharp.Compiler.Syntax open FSharp.Compiler.Diagnostics open FSharp.Compiler.DiagnosticsLogger + +type internal ProjectIdentifier = string * string + + /// A common interface for an F# source file snapshot that can be used accross all stages (lazy, source loaded, parsed) type internal IFileSnapshot = abstract member FileName: string @@ -57,6 +61,11 @@ module internal Helpers = |> fst, lastFile + let findOutputFileName options = + options + |> Seq.tryFind (fun (x: string) -> x.StartsWith("-o:")) + |> Option.map (fun x -> x.Substring(3)) + /// A snapshot of an F# source file. [] type FSharpFileSnapshot(FileName: string, Version: string, GetSource: unit -> Task) = @@ -154,11 +163,12 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj let noFileVersionsKey = lazy ({ new ICacheKey<_, _> with - member this.GetLabel() = projectCore.Label - member this.GetKey() = projectCore.Key + member _.GetLabel() = projectCore.Label + member _.GetKey() = projectCore.Identifier - member this.GetVersion() = + member _.GetVersion() = noFileVersionsHash.Value |> Md5Hasher.toString + }) let fullHash = @@ -176,9 +186,9 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj let fullKey = lazy ({ new ICacheKey<_, _> with - member this.GetLabel() = projectCore.Label - member this.GetKey() = projectCore.Key - member this.GetVersion() = fullHash.Value |> Md5Hasher.toString + member _.GetLabel() = projectCore.Label + member _.GetKey() = projectCore.Identifier + member _.GetVersion() = fullHash.Value |> Md5Hasher.toString }) let addHash (file: 'T) hash = @@ -207,7 +217,7 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj { new ICacheKey<_, _> with member _.GetLabel() = $"{f.FileName} ({projectCore.Label})" - member _.GetKey() = f.FileName, projectCore.Key + member _.GetKey() = f.FileName, projectCore.Identifier member _.GetVersion() = hash |> Md5Hasher.toString }) @@ -215,6 +225,7 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj member _.ProjectFileName = projectCore.ProjectFileName member _.ProjectId = projectCore.ProjectId + member _.Identifier = projectCore.Identifier member _.ReferencesOnDisk = projectCore.ReferencesOnDisk member _.OtherOptions = projectCore.OtherOptions member _.ReferencedProjects = projectCore.ReferencedProjects @@ -371,10 +382,7 @@ and internal ProjectCore |> Seq.toList) let outputFileName = - lazy - (OtherOptions - |> List.tryFind (fun x -> x.StartsWith("-o:")) - |> Option.map (fun x -> x.Substring(3))) + lazy (OtherOptions |> findOutputFileName) let key = lazy (ProjectFileName, outputFileName.Value |> Option.defaultValue "") @@ -382,13 +390,13 @@ and internal ProjectCore lazy ({ new ICacheKey<_, _> with member _.GetLabel() = self.Label - member _.GetKey() = self.Key + member _.GetKey() = self.Identifier member _.GetVersion() = fullHashString.Value }) member val ProjectDirectory = Path.GetDirectoryName(ProjectFileName) member _.OutputFileName = outputFileName.Value - member _.Key = key.Value + member _.Identifier: ProjectIdentifier = key.Value member _.Version = fullHash.Value member _.Label = ProjectFileName |> shortPath member _.VersionForParsing = hashForParsing.Value @@ -410,14 +418,14 @@ and internal ProjectCore member _.CacheKeyWith(label, version) = { new ICacheKey<_, _> with member _.GetLabel() = $"{label} ({self.Label})" - member _.GetKey() = self.Key + member _.GetKey() = self.Identifier member _.GetVersion() = fullHashString.Value, version } member _.CacheKeyWith(label, key, version) = { new ICacheKey<_, _> with member _.GetLabel() = $"{label} ({self.Label})" - member _.GetKey() = key, self.Key + member _.GetKey() = key, self.Identifier member _.GetVersion() = fullHashString.Value, version } @@ -458,6 +466,10 @@ and [] + FSharpProjectIdentifier = FSharpProjectIdentifier of projectFileName: string * outputFileName: string + /// A snapshot of an F# project. This type contains all the necessary information for type checking a project. and [] FSharpProjectSnapshot internal (projectSnapshot) = @@ -468,6 +480,7 @@ and [] FSha projectSnapshot.Replace(changedSourceFiles) |> FSharpProjectSnapshot member _.Label = projectSnapshot.Label + member _.Identifier = FSharpProjectIdentifier projectSnapshot.ProjectCore.Identifier static member Create ( @@ -610,3 +623,7 @@ type internal Extensions = [] static member ToOptions(this: FSharpProjectSnapshot) = this.ProjectSnapshot |> snapshotToOptions + + [] + static member GetProjectIdentifier(this: FSharpProjectOptions): ProjectIdentifier = + this.ProjectFileName, this.OtherOptions |> findOutputFileName |> Option.defaultValue "" \ No newline at end of file diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index dfaa36be2c9..998fdd4bfc1 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -119,6 +119,7 @@ type internal BootstrapInfo = type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string + [] type internal DependencyGraphType = /// A dependency graph for a single file - it will be missing files which this file does not depend on @@ -291,6 +292,19 @@ type internal CompilerCaches(sizeFactor: int) = member val ItemKeyStore = AsyncMemoize(sf, 2 * sf, name = "ItemKeyStore") + member this.Clear(projects: Set) = + let shouldClear project = projects |> Set.contains project + + this.ParseFile.Clear(fst >> shouldClear) + this.ParseAndCheckFileInProject.Clear(snd >> shouldClear) + this.ParseAndCheckProject.Clear(shouldClear) + this.BootstrapInfoStatic.Clear(shouldClear) + this.BootstrapInfo.Clear(shouldClear) + this.TcIntermediate.Clear(snd >> shouldClear) + this.AssemblyData.Clear(shouldClear) + this.SemanticClassification.Clear(snd >> shouldClear) + this.ItemKeyStore.Clear(snd >> shouldClear) + type internal TransparentCompiler ( legacyReferenceResolver, @@ -844,7 +858,7 @@ type internal TransparentCompiler member _.GetLabel() = file.FileName |> shortPath member _.GetKey() = - projectSnapshot.ProjectCore.Key, file.FileName + projectSnapshot.ProjectCore.Identifier, file.FileName member _.GetVersion() = projectSnapshot.ParsingVersion, @@ -1843,8 +1857,16 @@ type internal TransparentCompiler return Some result } - member _.ClearCache(options: seq, userOpName: string) : unit = + member this.ClearCache(projects: FSharpProjectIdentifier seq, userOpName: string): unit = + use _ = + Activity.start "TransparentCompiler.ClearCache" [| Activity.Tags.userOpName, userOpName |] + this.Caches.Clear (projects |> Seq.map (function FSharpProjectIdentifier (x, y) -> (x, y)) |> Set) + + member this.ClearCache(options: seq, userOpName: string) : unit = + use _ = + Activity.start "TransparentCompiler.ClearCache" [| Activity.Tags.userOpName, userOpName |] backgroundCompiler.ClearCache(options, userOpName) + this.Caches.Clear(options |> Seq.map (fun o -> o.GetProjectIdentifier()) |> Set) member _.ClearCaches() : unit = backgroundCompiler.ClearCaches() diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 5bfbdd02ca5..b79254d7935 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -385,6 +385,10 @@ type FSharpChecker let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.ClearCache(options, userOpName) + member _.ClearCache(projects: ProjectSnapshot.FSharpProjectIdentifier seq, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.ClearCache(projects, userOpName) + /// This function is called when a project has been cleaned, and thus type providers should be refreshed. member _.NotifyProjectCleaned(options: FSharpProjectOptions, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" diff --git a/src/Compiler/Service/service.fsi b/src/Compiler/Service/service.fsi index 40f0ddff858..14124fbda6b 100644 --- a/src/Compiler/Service/service.fsi +++ b/src/Compiler/Service/service.fsi @@ -415,6 +415,8 @@ type public FSharpChecker = /// An optional string used for tracing compiler operations associated with this request. member ClearCache: options: FSharpProjectOptions seq * ?userOpName: string -> unit + member ClearCache: projects: ProjectSnapshot.FSharpProjectIdentifier seq * ?userOpName: string -> unit + /// Report a statistic for testability static member ActualParseFileCount: int diff --git a/src/Compiler/Utilities/LruCache.fs b/src/Compiler/Utilities/LruCache.fs index b0308b951b8..0ca2b7edb17 100644 --- a/src/Compiler/Utilities/LruCache.fs +++ b/src/Compiler/Utilities/LruCache.fs @@ -14,6 +14,7 @@ type internal CacheEvent = | Collected | Weakened | Strengthened + | Cleared [] type internal ValueLink<'T when 'T: not struct> = @@ -77,6 +78,8 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers let cutStrongListIfTooLong () = let mutable node = strongList.Last + let mutable anythingWeakened = false + while strongList.Count > keepStrongly && node <> null do let previous = node.Previous @@ -87,11 +90,13 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers node.Value <- key, version, label, Weak(WeakReference<_> v) weakList.AddFirst node event CacheEvent.Weakened (label, key, version) + anythingWeakened <- true | _key, _version, _label, _ -> failwith "Invalid state, weak reference in strong list" node <- previous - cutWeakListIfTooLong () + if anythingWeakened then + cutWeakListIfTooLong () let pushNodeToTop (node: LinkedListNode<_>) = match node.Value with @@ -101,9 +106,7 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers | _, _, _, Weak _ -> failwith "Invalid operation, pushing weak reference to strong list" let pushValueToTop key version label value = - let node = strongList.AddFirst(value = (key, version, label, Strong value)) - cutStrongListIfTooLong () - node + strongList.AddFirst(value = (key, version, label, Strong value)) member _.DebuggerDisplay = $"Cache(S:{strongList.Count} W:{weakList.Count})" @@ -148,9 +151,12 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers if anythingWeakened then cutWeakListIfTooLong () + else + cutStrongListIfTooLong () | false, _ -> let node = pushValueToTop key version label value + cutStrongListIfTooLong () dictionary[key] <- Dictionary() dictionary[key][version] <- node @@ -177,6 +183,7 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers weakList.Remove node let node = pushValueToTop key version label value event CacheEvent.Strengthened (label, key, version) + cutStrongListIfTooLong() versionDict[version] <- node Some value | _ -> @@ -237,6 +244,23 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers strongList.Clear() weakList.Clear() + member _.Clear(predicate) = + let keysToRemove = dictionary.Keys |> Seq.filter predicate |> Seq.toList + + for key in keysToRemove do + match dictionary.TryGetValue key with + | true, versionDict -> + versionDict.Values + |> Seq.iter (fun node -> + match node.Value with + | _, _, _, Strong _ -> strongList.Remove node + | _, _, _, Weak _ -> weakList.Remove node + match node.Value with + | key, version, label, _ -> event CacheEvent.Cleared (label, key, version)) + + dictionary.Remove key |> ignore + | _ -> () + member _.GetValues() = strongList |> Seq.append weakList diff --git a/src/Compiler/Utilities/LruCache.fsi b/src/Compiler/Utilities/LruCache.fsi index e204e682788..d9aefd2a240 100644 --- a/src/Compiler/Utilities/LruCache.fsi +++ b/src/Compiler/Utilities/LruCache.fsi @@ -6,6 +6,7 @@ type internal CacheEvent = | Collected | Weakened | Strengthened + | Cleared /// A cache where least recently used items are removed when the cache is full. /// @@ -26,6 +27,9 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers member Clear: unit -> unit + /// Clear any keys that match the given predicate + member Clear: predicate: ('TKey -> bool) -> unit + /// Returns an option of a value for given key and version, and also a list of all other versions for given key member GetAll: key: 'TKey * version: 'TVersion -> 'TValue option * ('TVersion * 'TValue) list diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs index 9fef3a32606..a477f7e6f7c 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/LruCache.fs @@ -137,3 +137,61 @@ let ``Looking up a weakened item will strengthen it`` () = ] Assert.Equal>(expected, eventLog |> Seq.toList) + + +[] +let ``New version doesn't push other keys out of strong list``() = + + let eventLog = ResizeArray() + + let cache = new LruCache<_, int, _>(keepStrongly = 2, keepWeakly = 2, event = (fun e v -> eventLog.Add(e, v))) + + cache.Set(1, 1, "one1") + cache.Set(1, 2, "one2") + cache.Set(1, 3, "one3") + cache.Set(1, 4, "one4") + cache.Set(2, 1, "two1") + cache.Set(2, 2, "two2") + + let expected = [ + CacheEvent.Weakened, ("[no label]", 1, 1) + CacheEvent.Weakened, ("[no label]", 1, 2) + CacheEvent.Weakened, ("[no label]", 1, 3) + CacheEvent.Evicted, ("[no label]", 1, 1) + CacheEvent.Weakened, ("[no label]", 2, 1) + CacheEvent.Evicted, ("[no label]", 1, 2) + ] + + Assert.Equal>(expected, eventLog |> Seq.toList) + +[] +let ``We can clear specific keys based on a predicate``() = + + let eventLog = ResizeArray() + + let cache = new LruCache<_, int, _>(keepStrongly = 2, keepWeakly = 2, event = (fun e v -> eventLog.Add(e, v))) + + cache.Set(1, 1, "one1") + cache.Set(1, 2, "one2") + cache.Set(1, 3, "one3") + cache.Set(1, 4, "one4") + cache.Set(2, 1, "two1") + cache.Set(2, 2, "two2") + + cache.Clear((=) 1) + + let result = cache.TryGet(1, 2) + Assert.True(result.IsNone) + + let expected = [ + CacheEvent.Weakened, ("[no label]", 1, 1) + CacheEvent.Weakened, ("[no label]", 1, 2) + CacheEvent.Weakened, ("[no label]", 1, 3) + CacheEvent.Evicted, ("[no label]", 1, 1) + CacheEvent.Weakened, ("[no label]", 2, 1) + CacheEvent.Evicted, ("[no label]", 1, 2) + CacheEvent.Cleared, ("[no label]", 1, 3) + CacheEvent.Cleared, ("[no label]", 1, 4) + ] + + Assert.Equal>(expected, eventLog |> Seq.toList) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 05d9ace6e4d..fdee6f56559 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2082,6 +2082,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.CodeA FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParsingOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]] GetParsingOptionsFromProjectOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.Tokenization.FSharpTokenInfo[],FSharp.Compiler.Tokenization.FSharpTokenizerLexState] TokenizeLine(System.String, FSharp.Compiler.Tokenization.FSharpTokenizerLexState) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearCache(System.Collections.Generic.IEnumerable`1[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearCache(System.Collections.Generic.IEnumerable`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateAll() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateConfiguration(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2312,6 +2313,24 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String g FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String get_Version() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew] GetSource() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Boolean Equals(FSharpProjectIdentifier) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: FSharpProjectIdentifier NewFSharpProjectIdentifier(System.String, System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 CompareTo(FSharpProjectIdentifier) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 CompareTo(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 Tag +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 get_Tag() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_outputFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_projectFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String outputFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String projectFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier Identifier +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier get_Identifier() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Replace(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) @@ -2347,6 +2366,7 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String ToSt FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String get_Path() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Void .ctor(System.String, System.DateTime) FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 05d9ace6e4d..fdee6f56559 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2082,6 +2082,7 @@ FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.CodeA FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.CodeAnalysis.FSharpParsingOptions,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Diagnostics.FSharpDiagnostic]] GetParsingOptionsFromProjectOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) FSharp.Compiler.CodeAnalysis.FSharpChecker: System.Tuple`2[FSharp.Compiler.Tokenization.FSharpTokenInfo[],FSharp.Compiler.Tokenization.FSharpTokenizerLexState] TokenizeLine(System.String, FSharp.Compiler.Tokenization.FSharpTokenizerLexState) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearCache(System.Collections.Generic.IEnumerable`1[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions], Microsoft.FSharp.Core.FSharpOption`1[System.String]) +FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearCache(System.Collections.Generic.IEnumerable`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.FSharpChecker: Void ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateAll() FSharp.Compiler.CodeAnalysis.FSharpChecker: Void InvalidateConfiguration(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpOption`1[System.String]) @@ -2312,6 +2313,24 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String g FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String get_Version() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew] GetSource() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Void .ctor(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Boolean Equals(FSharpProjectIdentifier) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Boolean Equals(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Boolean Equals(System.Object, System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: FSharpProjectIdentifier NewFSharpProjectIdentifier(System.String, System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 CompareTo(FSharpProjectIdentifier) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 CompareTo(System.Object) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 CompareTo(System.Object, System.Collections.IComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 GetHashCode() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 GetHashCode(System.Collections.IEqualityComparer) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 Tag +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 get_Tag() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_outputFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_projectFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String outputFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String projectFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier Identifier +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier get_Identifier() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Replace(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) @@ -2347,6 +2366,7 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String ToSt FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: System.String get_Path() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Void .ctor(System.String, System.DateTime) FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf index fa6273bf1d8..b19c8c778ec 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf index e372ab69dc9..ec717552d33 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf index 07f10c25a33..fe5a5d80c80 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf index 56d28edb642..ffb25527da0 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf index a30c43f40cb..f97a4c85ee2 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf index e16f9b25022..cda6f1881bf 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf index 9d5918b03d5..c98f936cad6 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf index d7be168642d..1fef6b0d5f9 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf index ca5d37235d0..0a713d7df79 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf index c2eebea058c..baa8b8f4a66 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf index ade67804291..c9d50660caf 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf index 2f9009d075b..64295cbe8e3 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf index f0d69e9ff75..205e692ad93 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf @@ -208,8 +208,8 @@ - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. - WARNING! Transparent does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. + WARNING! Transparent Compiler does not yet support all features and can cause crashes or give incorrect results. From 59992f9ed76dee630e886772d9ef04fbe9efbd5b Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 12 Jan 2024 13:53:59 +0100 Subject: [PATCH 218/222] f --- src/Compiler/Service/BackgroundCompiler.fs | 3 +-- src/Compiler/Service/FSharpProjectSnapshot.fs | 13 +++++-------- src/Compiler/Service/TransparentCompiler.fs | 12 +++++++++--- src/Compiler/Utilities/LruCache.fs | 3 ++- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index d742a52d783..514bb8e45c5 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -1530,8 +1530,7 @@ type internal BackgroundCompiler member _.ClearCache(options: seq, userOpName: string) : unit = self.ClearCache(options, userOpName) - member _.ClearCache(projects: ProjectSnapshot.FSharpProjectIdentifier seq, userOpName: string) = - ignore (projects, userOpName) + member _.ClearCache(projects: ProjectSnapshot.FSharpProjectIdentifier seq, userOpName: string) = ignore (projects, userOpName) member _.ClearCaches() : unit = self.ClearCaches() member _.DownsizeCaches() : unit = self.DownsizeCaches() diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index eb62ca7f131..259948dc706 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -20,10 +20,8 @@ open FSharp.Compiler.Syntax open FSharp.Compiler.Diagnostics open FSharp.Compiler.DiagnosticsLogger - type internal ProjectIdentifier = string * string - /// A common interface for an F# source file snapshot that can be used accross all stages (lazy, source loaded, parsed) type internal IFileSnapshot = abstract member FileName: string @@ -381,8 +379,7 @@ and internal ProjectCore } |> Seq.toList) - let outputFileName = - lazy (OtherOptions |> findOutputFileName) + let outputFileName = lazy (OtherOptions |> findOutputFileName) let key = lazy (ProjectFileName, outputFileName.Value |> Option.defaultValue "") @@ -467,8 +464,8 @@ and [] - FSharpProjectIdentifier = FSharpProjectIdentifier of projectFileName: string * outputFileName: string +and [] FSharpProjectIdentifier = + | FSharpProjectIdentifier of projectFileName: string * outputFileName: string /// A snapshot of an F# project. This type contains all the necessary information for type checking a project. and [] FSharpProjectSnapshot internal (projectSnapshot) = @@ -625,5 +622,5 @@ type internal Extensions = this.ProjectSnapshot |> snapshotToOptions [] - static member GetProjectIdentifier(this: FSharpProjectOptions): ProjectIdentifier = - this.ProjectFileName, this.OtherOptions |> findOutputFileName |> Option.defaultValue "" \ No newline at end of file + static member GetProjectIdentifier(this: FSharpProjectOptions) : ProjectIdentifier = + this.ProjectFileName, this.OtherOptions |> findOutputFileName |> Option.defaultValue "" diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 998fdd4bfc1..fe82627483f 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -119,7 +119,6 @@ type internal BootstrapInfo = type internal TcIntermediateResult = TcInfo * TcResultsSinkImpl * CheckedImplFile option * string - [] type internal DependencyGraphType = /// A dependency graph for a single file - it will be missing files which this file does not depend on @@ -1857,14 +1856,21 @@ type internal TransparentCompiler return Some result } - member this.ClearCache(projects: FSharpProjectIdentifier seq, userOpName: string): unit = + member this.ClearCache(projects: FSharpProjectIdentifier seq, userOpName: string) : unit = use _ = Activity.start "TransparentCompiler.ClearCache" [| Activity.Tags.userOpName, userOpName |] - this.Caches.Clear (projects |> Seq.map (function FSharpProjectIdentifier (x, y) -> (x, y)) |> Set) + + this.Caches.Clear( + projects + |> Seq.map (function + | FSharpProjectIdentifier(x, y) -> (x, y)) + |> Set + ) member this.ClearCache(options: seq, userOpName: string) : unit = use _ = Activity.start "TransparentCompiler.ClearCache" [| Activity.Tags.userOpName, userOpName |] + backgroundCompiler.ClearCache(options, userOpName) this.Caches.Clear(options |> Seq.map (fun o -> o.GetProjectIdentifier()) |> Set) diff --git a/src/Compiler/Utilities/LruCache.fs b/src/Compiler/Utilities/LruCache.fs index 0ca2b7edb17..c75ed1d88cf 100644 --- a/src/Compiler/Utilities/LruCache.fs +++ b/src/Compiler/Utilities/LruCache.fs @@ -183,7 +183,7 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers weakList.Remove node let node = pushValueToTop key version label value event CacheEvent.Strengthened (label, key, version) - cutStrongListIfTooLong() + cutStrongListIfTooLong () versionDict[version] <- node Some value | _ -> @@ -255,6 +255,7 @@ type internal LruCache<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'TVers match node.Value with | _, _, _, Strong _ -> strongList.Remove node | _, _, _, Weak _ -> weakList.Remove node + match node.Value with | key, version, label, _ -> event CacheEvent.Cleared (label, key, version)) From ce270e48950ca06b72307089287def7cfa8482d2 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Fri, 12 Jan 2024 14:14:33 +0100 Subject: [PATCH 219/222] disable snapshot reuse by default --- vsintegration/src/FSharp.Editor/Options/EditorOptions.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 8d7bfc655f2..e9b7f60252a 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -133,7 +133,7 @@ type AdvancedOptions = IsInlineParameterNameHintsEnabled = false IsInlineReturnTypeHintsEnabled = false UseTransparentCompiler = false - TransparentCompilerSnapshotReuse = true + TransparentCompilerSnapshotReuse = false IsUseLiveBuffersEnabled = true SendAdditionalTelemetry = true SolutionBackgroundAnalysis = false From ad0fa6af57cc4a8a44383d008e8b38587301223e Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 15 Jan 2024 13:53:49 +0100 Subject: [PATCH 220/222] revert pipeline --- azure-pipelines.yml | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 059334fe460..8ee494eeed1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -86,11 +86,11 @@ stages: # Signed build # #-------------------------------------------------------------------------------------------------------------------# - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/release/dev17.9') }}: + - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/release/dev17.8') }}: - template: /eng/common/templates/job/onelocbuild.yml parameters: MirrorRepo: fsharp - MirrorBranch: release/dev17.9 + MirrorBranch: release/dev17.8 LclSource: lclFilesfromPackage LclPackageId: 'LCL-JUNO-PROD-FSHARP' - template: /eng/common/templates/jobs/jobs.yml @@ -439,10 +439,6 @@ stages: vs_release: _configuration: Release _testKind: testVs - # experimental_features: - # _configuration: Release - # _testKind: testCoreclr - ${{ if eq(variables['Build.Reason'], 'Flaky, disabled, was PullRequest') }}: inttests_release: _configuration: Release @@ -460,15 +456,7 @@ stages: env: NativeToolsOnMachine: true displayName: Build / Test - condition: and( ne(variables['_testKind'], 'testIntegration'), ne(variables['System.JobName'], 'experimental_features') ) - - - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) - env: - FSHARP_EXPERIMENTAL_FEATURES: 1 - NativeToolsOnMachine: true - displayName: Build / Test Experimental Features - condition: and( eq(variables['System.JobName'], 'experimental_features'), ne(variables['_testKind'], 'testIntegration') ) - + condition: ne(variables['_testKind'], 'testIntegration') - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind) env: NativeToolsOnMachine: true @@ -818,8 +806,8 @@ stages: - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - template: eng/release/insert-into-vs.yml parameters: - componentBranchName: refs/heads/release/dev17.9 - insertTargetBranch: rel/d17.9 + componentBranchName: refs/heads/release/dev17.8 + insertTargetBranch: main insertTeamEmail: fsharpteam@microsoft.com insertTeamName: 'F#' completeInsertion: 'auto' From 6b86bb470d1a357813378c3993f64cb6dcbf9b56 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 15 Jan 2024 13:54:48 +0100 Subject: [PATCH 221/222] revert pipeline --- azure-pipelines.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8ee494eeed1..94bba597e8a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -86,11 +86,11 @@ stages: # Signed build # #-------------------------------------------------------------------------------------------------------------------# - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/release/dev17.8') }}: + - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/release/dev17.9') }}: - template: /eng/common/templates/job/onelocbuild.yml parameters: MirrorRepo: fsharp - MirrorBranch: release/dev17.8 + MirrorBranch: release/dev17.9 LclSource: lclFilesfromPackage LclPackageId: 'LCL-JUNO-PROD-FSHARP' - template: /eng/common/templates/jobs/jobs.yml @@ -806,8 +806,8 @@ stages: - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - template: eng/release/insert-into-vs.yml parameters: - componentBranchName: refs/heads/release/dev17.8 - insertTargetBranch: main + componentBranchName: refs/heads/release/dev17.9 + insertTargetBranch: rel/d17.9 insertTeamEmail: fsharpteam@microsoft.com insertTeamName: 'F#' completeInsertion: 'auto' From 4b099a151d4c22b56bfb7a363172523154844c9c Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 15 Jan 2024 15:23:16 +0100 Subject: [PATCH 222/222] Try to fix flakiness in async tests --- .../CompilerService/AsyncMemoize.fs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index d64645e8028..817a3b8c70a 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -101,6 +101,8 @@ let ``We can cancel a job`` () = Assert.Equal<(JobEvent * int) array>([| Started, key |], eventLog |> Seq.toArray ) + do! Task.Delay 100 + cts1.Cancel() cts2.Cancel() @@ -143,6 +145,8 @@ let ``Job is restarted if first requestor cancels`` () = let _task2 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts2.Token) let _task3 = NodeCode.StartAsTask_ForTesting( memoize.Get'(key, computation key), ct = cts3.Token) + do! Task.Delay 100 + cts1.Cancel() do! Task.Delay 100