From a88665c4ec3a5aeec77508fe9e27fdf3a0ba46e1 Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Sat, 9 Nov 2024 15:26:08 +0100 Subject: [PATCH] Re-enable TransparentCompiler tests (#17966) --- ...y_FSharp.Compiler.Service_Debug_net9.0.bsl | 12 +- ....Compiler.Service_Debug_netstandard2.0.bsl | 12 +- src/Compiler/Facilities/AsyncMemoize.fs | 298 ++++++------- src/Compiler/Facilities/AsyncMemoize.fsi | 2 + .../CompilerService/AsyncMemoize.fs | 180 +++----- .../FSharpChecker/TransparentCompiler.fs | 415 ++++++++---------- .../ExprTests.fs | 89 ++-- .../MultiProjectAnalysisTests.fs | 233 +++++----- .../ProjectAnalysisTests.fs | 64 +-- 9 files changed, 576 insertions(+), 729 deletions(-) diff --git a/eng/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl b/eng/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl index eac41ef3110..427a2ec919e 100644 --- a/eng/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl +++ b/eng/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl @@ -21,14 +21,14 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3508-780::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3508-779::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. [IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@110::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+dataTipOfReferences@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-492::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-492::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-492::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-492::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-492::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.StaticLinking+TypeForwarding::followTypeForwardForILTypeRef([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILTypeRef)][offset 0x00000010][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::getCompilerOption([FSharp.Compiler.Service]FSharp.Compiler.CompilerOptions+CompilerOption, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1)][offset 0x000000E6][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::AddPathMapping([FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, string)][offset 0x0000000B][found Char] Unexpected type on the stack. diff --git a/eng/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl b/eng/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl index 35af722664f..f5141d0aed8 100644 --- a/eng/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl +++ b/eng/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl @@ -28,18 +28,18 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000039][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3508-780::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3508-779::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiInteractionProcessor::CompletionsForPartialLID([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState, string)][offset 0x0000001B][found Char] Unexpected type on the stack. [IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@110::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+dataTipOfReferences@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseMemberFunctionAndValues@176::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue)][offset 0x00000059][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseEntity@218::GenerateNext([S.P.CoreLib]System.Collections.Generic.IEnumerable`1&)][offset 0x000000DA][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.ParsedInput+visitor@1423-6::VisitExpr([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Compiler.Service]FSharp.Compiler.Syntax.SynExpr)][offset 0x00000605][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-492::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-492::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-492::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-492::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-492::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-491::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$Symbols+fullName@2490-1::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000015][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CreateILModule+MainModuleBuilder::ConvertProductVersionToILVersionInfo(string)][offset 0x00000011][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.StaticLinking+TypeForwarding::followTypeForwardForILTypeRef([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILTypeRef)][offset 0x00000010][found Char] Unexpected type on the stack. diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index d22093a2b4f..af1a52e5e5d 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -172,6 +172,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T let mutable strengthened = 0 let mutable cleared = 0 + let mutable updates_in_flight = 0 + let mutable cancel_ct_registration_original = 0 let mutable cancel_exception_original = 0 let mutable cancel_original_processed = 0 @@ -325,154 +327,154 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T // raise ex -- Suppose there's no need to raise here - where does it even go? let processStateUpdate post (key: KeyData<_, _>, action: StateUpdate<_>) = - task { - do! Task.Yield() - - do! - lock.Do(fun () -> - task { - - let cached = cache.TryGet(key.Key, key.Version) - - match action, cached with - - | OriginatorCanceled, Some(Running(tcs, cts, computation, _, _)) -> - - Interlocked.Increment &cancel_original_processed |> ignore - - decrRequestCount key - - if requestCounts[key] < 1 then - cancelRegistration key - cts.Cancel() - tcs.TrySetCanceled() |> ignore - // Remember the job in case it completes after cancellation - cache.Set(key.Key, key.Version, key.Label, Job.Canceled DateTime.Now) - requestCounts.Remove key |> ignore - log (Canceled, key) - Interlocked.Increment &canceled |> ignore - use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] - () - - else - // We need to restart the computation - Task.Run(fun () -> - Async.StartAsTask( - async { - - let cachingLogger = new CachingDiagnosticsLogger(None) - - try - // TODO: Should unify starting and restarting - log (Restarted, key) - Interlocked.Increment &restarted |> ignore - System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}" - let currentLogger = DiagnosticsThreadStatics.DiagnosticsLogger - DiagnosticsThreadStatics.DiagnosticsLogger <- cachingLogger - - try - let! result = computation - post (key, (JobCompleted(result, cachingLogger.CapturedDiagnostics))) - return () - finally - DiagnosticsThreadStatics.DiagnosticsLogger <- currentLogger - with - | TaskCancelled _ -> - Interlocked.Increment &cancel_exception_subsequent |> ignore - post (key, CancelRequest) - () - | ex -> post (key, (JobFailed(ex, cachingLogger.CapturedDiagnostics))) - } - ), - cts.Token) - |> ignore - - | CancelRequest, Some(Running(tcs, cts, _c, _, _)) -> - - Interlocked.Increment &cancel_subsequent_processed |> ignore - - decrRequestCount key - - if requestCounts[key] < 1 then - cancelRegistration key - cts.Cancel() - tcs.TrySetCanceled() |> ignore - // Remember the job in case it completes after cancellation - cache.Set(key.Key, key.Version, key.Label, Job.Canceled DateTime.Now) - requestCounts.Remove key |> ignore - log (Canceled, key) - Interlocked.Increment &canceled |> ignore - use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] - () - - // Probably in some cases cancellation can be fired off even after we just unregistered it - | CancelRequest, None - | CancelRequest, Some(Completed _) - | CancelRequest, Some(Job.Canceled _) - | CancelRequest, Some(Job.Failed _) - | OriginatorCanceled, None - | OriginatorCanceled, Some(Completed _) - | OriginatorCanceled, Some(Job.Canceled _) - | OriginatorCanceled, Some(Job.Failed _) -> () - - | JobFailed(ex, diags), Some(Running(tcs, _cts, _c, _ts, loggers)) -> - cancelRegistration key - cache.Set(key.Key, key.Version, key.Label, Job.Failed(DateTime.Now, ex)) - requestCounts.Remove key |> ignore - log (Failed, key) - Interlocked.Increment &failed |> ignore - failures.Add(key.Label, ex) - - for logger in loggers do - diags |> replayDiagnostics logger - - tcs.TrySetException ex |> ignore - - | JobCompleted(result, diags), Some(Running(tcs, _cts, _c, started, loggers)) -> - cancelRegistration key - cache.Set(key.Key, key.Version, key.Label, (Completed(result, diags))) - requestCounts.Remove key |> ignore - log (Finished, key) - Interlocked.Increment &completed |> ignore - let duration = float (DateTime.Now - started).Milliseconds - - avgDurationMs <- - if completed < 2 then - duration - else - avgDurationMs + (duration - avgDurationMs) / float completed - - for logger in loggers do - diags |> replayDiagnostics logger - - if tcs.TrySetResult result = false then - internalError key.Label "Invalid state: Completed job already completed" - - // Sometimes job can be canceled but it still manages to complete (or fail) - | JobFailed _, Some(Job.Canceled _) - | JobCompleted _, Some(Job.Canceled _) -> () - - // Job can't be evicted from cache while it's running because then subsequent requesters would be waiting forever - | JobFailed _, None -> internalError key.Label "Invalid state: Running job missing in cache (failed)" - - | JobCompleted _, None -> internalError key.Label "Invalid state: Running job missing in cache (completed)" - - | JobFailed(ex, _diags), Some(Completed(_job, _diags2)) -> - internalError key.Label $"Invalid state: Failed Completed job \n%A{ex}" - - | JobCompleted(_result, _diags), Some(Completed(_job, _diags2)) -> - internalError key.Label "Invalid state: Double-Completed job" - - | JobFailed(ex, _diags), Some(Job.Failed(_, ex2)) -> - internalError key.Label $"Invalid state: Double-Failed job \n%A{ex} \n%A{ex2}" - - | JobCompleted(_result, _diags), Some(Job.Failed(_, ex2)) -> - internalError key.Label $"Invalid state: Completed Failed job \n%A{ex2}" - }) - } + lock.Do(fun () -> + task { + + let cached = cache.TryGet(key.Key, key.Version) + + match action, cached with + + | OriginatorCanceled, Some(Running(tcs, cts, computation, _, _)) -> + + Interlocked.Increment &cancel_original_processed |> ignore + + decrRequestCount key + + if requestCounts[key] < 1 then + cancelRegistration key + cts.Cancel() + tcs.TrySetCanceled() |> ignore + // Remember the job in case it completes after cancellation + cache.Set(key.Key, key.Version, key.Label, Job.Canceled DateTime.Now) + requestCounts.Remove key |> ignore + log (Canceled, key) + Interlocked.Increment &canceled |> ignore + use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] + () + + else + // We need to restart the computation + Task.Run(fun () -> + Async.StartAsTask( + async { + + let cachingLogger = new CachingDiagnosticsLogger(None) + + try + // TODO: Should unify starting and restarting + log (Restarted, key) + Interlocked.Increment &restarted |> ignore + System.Diagnostics.Trace.TraceInformation $"{name} Restarted {key.Label}" + let currentLogger = DiagnosticsThreadStatics.DiagnosticsLogger + DiagnosticsThreadStatics.DiagnosticsLogger <- cachingLogger + + try + let! result = computation + post (key, (JobCompleted(result, cachingLogger.CapturedDiagnostics))) + return () + finally + DiagnosticsThreadStatics.DiagnosticsLogger <- currentLogger + with + | TaskCancelled _ -> + Interlocked.Increment &cancel_exception_subsequent |> ignore + post (key, CancelRequest) + () + | ex -> post (key, (JobFailed(ex, cachingLogger.CapturedDiagnostics))) + } + ), + cts.Token) + |> ignore + + | CancelRequest, Some(Running(tcs, cts, _c, _, _)) -> + + Interlocked.Increment &cancel_subsequent_processed |> ignore + + decrRequestCount key + + if requestCounts[key] < 1 then + cancelRegistration key + cts.Cancel() + tcs.TrySetCanceled() |> ignore + // Remember the job in case it completes after cancellation + cache.Set(key.Key, key.Version, key.Label, Job.Canceled DateTime.Now) + requestCounts.Remove key |> ignore + log (Canceled, key) + Interlocked.Increment &canceled |> ignore + use _ = Activity.start $"{name}: Canceled job" [| "key", key.Label |] + () + + // Probably in some cases cancellation can be fired off even after we just unregistered it + | CancelRequest, None + | CancelRequest, Some(Completed _) + | CancelRequest, Some(Job.Canceled _) + | CancelRequest, Some(Job.Failed _) + | OriginatorCanceled, None + | OriginatorCanceled, Some(Completed _) + | OriginatorCanceled, Some(Job.Canceled _) + | OriginatorCanceled, Some(Job.Failed _) -> () + + | JobFailed(ex, diags), Some(Running(tcs, _cts, _c, _ts, loggers)) -> + cancelRegistration key + cache.Set(key.Key, key.Version, key.Label, Job.Failed(DateTime.Now, ex)) + requestCounts.Remove key |> ignore + log (Failed, key) + Interlocked.Increment &failed |> ignore + failures.Add(key.Label, ex) + + for logger in loggers do + diags |> replayDiagnostics logger + + tcs.TrySetException ex |> ignore + + | JobCompleted(result, diags), Some(Running(tcs, _cts, _c, started, loggers)) -> + cancelRegistration key + cache.Set(key.Key, key.Version, key.Label, (Completed(result, diags))) + requestCounts.Remove key |> ignore + log (Finished, key) + Interlocked.Increment &completed |> ignore + let duration = float (DateTime.Now - started).Milliseconds + + avgDurationMs <- + if completed < 2 then + duration + else + avgDurationMs + (duration - avgDurationMs) / float completed + + for logger in loggers do + diags |> replayDiagnostics logger + + if tcs.TrySetResult result = false then + internalError key.Label "Invalid state: Completed job already completed" + + // Sometimes job can be canceled but it still manages to complete (or fail) + | JobFailed _, Some(Job.Canceled _) + | JobCompleted _, Some(Job.Canceled _) -> () + + // Job can't be evicted from cache while it's running because then subsequent requesters would be waiting forever + | JobFailed _, None -> internalError key.Label "Invalid state: Running job missing in cache (failed)" + + | JobCompleted _, None -> internalError key.Label "Invalid state: Running job missing in cache (completed)" + + | JobFailed(ex, _diags), Some(Completed(_job, _diags2)) -> + internalError key.Label $"Invalid state: Failed Completed job \n%A{ex}" + + | JobCompleted(_result, _diags), Some(Completed(_job, _diags2)) -> + internalError key.Label "Invalid state: Double-Completed job" + + | JobFailed(ex, _diags), Some(Job.Failed(_, ex2)) -> + internalError key.Label $"Invalid state: Double-Failed job \n%A{ex} \n%A{ex2}" + + | JobCompleted(_result, _diags), Some(Job.Failed(_, ex2)) -> + internalError key.Label $"Invalid state: Completed Failed job \n%A{ex2}" + }) let rec post msg = - Task.Run(fun () -> processStateUpdate post msg :> Task) |> ignore + Interlocked.Increment &updates_in_flight |> ignore + backgroundTask { + do! processStateUpdate post msg + Interlocked.Decrement &updates_in_flight |> ignore + } + |> ignore member this.Get'(key, computation) = @@ -564,7 +566,9 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T member this.OnEvent = this.Event.Add - member this.Count = cache.Count + member _.Count = lock.Do(fun () -> Task.FromResult cache.Count).Result + + member _.Updating = updates_in_flight > 0 member _.Locked = lock.Semaphore.CurrentCount < 1 diff --git a/src/Compiler/Facilities/AsyncMemoize.fsi b/src/Compiler/Facilities/AsyncMemoize.fsi index 049a06914a8..d86352d9987 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fsi +++ b/src/Compiler/Facilities/AsyncMemoize.fsi @@ -83,6 +83,8 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T member Count: int + member Updating: bool + /// 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> = diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 7b65ba798fe..e031aa9cbb9 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -11,83 +11,72 @@ open FSharp.Compiler.Diagnostics open Xunit -[] -module internal JobEvents = +let tap f x = f x; x - let publishEvent (cache: AsyncMemoize<_, _, _>) = - let wrapper = Event<_>() - cache.OnEvent (fun e -> lock wrapper <| fun () -> wrapper.Trigger e) - wrapper.Publish |> Event.map (fun (jobEvent, (_,k,_)) -> jobEvent, k) +let internal record (cache: AsyncMemoize<_,_,_>) = - let collectEvents cache = - cache |> publishEvent |> Event.scan (fun es e -> e :: es) [] |> Event.map List.rev + let events = ResizeArray() - /// Exposes a live view of the list of JobEvents generated by AsyncMemoize. - let observe cache = - let updateAvailable = new AutoResetEvent(false) - let mutable recorded = [] + let waitForIdle() = SpinWait.SpinUntil(fun () -> not cache.Updating) - let update next = - Debug.WriteLine $"%A{next}" - recorded <- next - updateAvailable.Set() |> ignore + waitForIdle() + cache.Event + |> Event.map (fun (e, (_, k, _)) -> e, k) + |> Event.add events.Add - collectEvents cache |> Event.add update + let getEvents () = + waitForIdle() + events |> List.ofSeq |> tap (printfn "events: %A") - let waitForUpdate = updateAvailable |> Async.AwaitWaitHandle |> Async.Ignore + getEvents - async { - Debug.WriteLine $"current: %A{recorded}" - return recorded, waitForUpdate - } +let check getEvents assertFunction = + let actual = getEvents() + assertFunction actual - let countOf value count events = events |> Seq.filter (fst >> (=) value) |> Seq.length |> (=) count +let waitUntil getEvents condition = + while getEvents() |> condition |> not do () - let received value events = events |> Seq.exists (fst >> (=) value) +let recorded (expected: 't list) (actual: 't list) = + Assert.Equal<'t>(expected, actual) - let waitUntil observedCache condition = - let rec loop() = async { - let! current, waitForUpdate = observedCache - if current |> condition |> not then - do! waitForUpdate - return! loop() - } - loop() +let countOf value count events = + events |> Seq.filter (fst >> (=) value) |> Seq.length |> (=) count + +let received value events = + events |> List.tryLast |> Option.map (fst >> (=) value) |> Option.defaultValue false [] let ``Basics``() = - task { - let computation key = async { - do! Async.Sleep 1 - return key * 2 - } + let computation key = async { + do! Async.Sleep 1 + return key * 2 + } - let memoize = AsyncMemoize() - let events = observe memoize - - 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 memoize = AsyncMemoize() + let events = record memoize - let expected = [| 10; 10; 4; 10; 6; 4|] + 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 - Assert.Equal(expected, result) + let expected = [| 10; 10; 4; 10; 6; 4|] - do! waitUntil events (countOf Finished 3) - let! current, _ = events - let groups = current |> Seq.groupBy snd |> Seq.toList + Assert.Equal(expected, result) + + check events <| fun events -> + let groups = events |> Seq.groupBy snd |> Seq.toList Assert.Equal(3, groups.Length) for key, events in groups do Assert.Equal>(Set [ Requested, key; Started, key; Finished, key ], Set events) - } [] let ``We can cancel a job`` () = @@ -106,7 +95,7 @@ let ``We can cancel a job`` () = } let memoize = AsyncMemoize<_, int, _>() - let events = observe memoize + let events = record memoize let key = 1 @@ -116,22 +105,14 @@ let ``We can cancel a job`` () = cts.Cancel() ctsCancelled.Set() - do! waitUntil events (received Canceled) - let! current, _ = events - - Assert.Equal<_ list>( - [ - Requested, key - Started, key - Canceled, key - ], - current - ) + check events recorded + [ Requested, key + Started, key + Canceled, key ] } [] let ``Job is restarted if first requestor cancels`` () = - task { let jobStarted = new SemaphoreSlim(0) let jobCanComplete = new ManualResetEventSlim(false) @@ -144,47 +125,39 @@ let ``Job is restarted if first requestor cancels`` () = } let memoize = AsyncMemoize<_, int, _>() - let events = observe memoize + let events = record memoize 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) - do! jobStarted.WaitAsync() - 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.Wait() + let task2 = Async.StartAsTask( memoize.Get'(key, computation key)) + let task3 = Async.StartAsTask( memoize.Get'(key, computation key)) - do! waitUntil events (countOf Requested 3) + waitUntil events (countOf Requested 3) cts1.Cancel() jobCanComplete.Set() |> ignore - do! jobStarted.WaitAsync() - - let! result = _task2 - Assert.Equal(2, result) + jobStarted.Wait() - let! current, _ = events + Assert.Equal(2, task2.Result) + Assert.Equal(2, task3.Result) - Assert.Equal<_ list>( - [ Requested, key - Started, key - Requested, key - Requested, key - Restarted, key - Finished, key ], - current - ) - } + check events recorded + [ Requested, key + Started, key + Requested, key + Requested, key + Restarted, key + Finished, key ] [] let ``Job is restarted if first requestor cancels but keeps running if second requestor cancels`` () = - task { let jobStarted = new ManualResetEventSlim(false) let jobCanComplete = new ManualResetEventSlim(false) @@ -196,11 +169,10 @@ let ``Job is restarted if first requestor cancels but keeps running if second re } let memoize = AsyncMemoize<_, int, _>() - let events = observe memoize + let events = record memoize use cts1 = new CancellationTokenSource() use cts2 = new CancellationTokenSource() - use cts3 = new CancellationTokenSource() let key = 1 @@ -210,9 +182,9 @@ let ``Job is restarted if first requestor cancels but keeps running if second re 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 task3 = Async.StartAsTask( memoize.Get'(key, computation key)) - do! waitUntil events (countOf Requested 3) + waitUntil events (countOf Requested 3) cts1.Cancel() @@ -222,27 +194,21 @@ let ``Job is restarted if first requestor cancels but keeps running if second re jobCanComplete.Set() |> ignore - let! result = _task3 - Assert.Equal(2, result) - - let! current, _ = events + Assert.Equal(2, task3.Result) - Assert.Equal<_ list>( + check events recorded [ Requested, key Started, key Requested, key Requested, key Restarted, key - Finished, key ], - current - ) - } + Finished, key ] type ExpectedException() = inherit Exception() -[] +[] let ``Stress test`` () = let seed = System.Random().Next() diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 0395a421895..b41963df66d 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -13,6 +13,7 @@ open FSharp.Compiler.Diagnostics open Xunit +open FSharp.Test open FSharp.Test.ProjectGeneration open FSharp.Test.ProjectGeneration.Helpers open System.IO @@ -26,10 +27,46 @@ open OpenTelemetry open OpenTelemetry.Resources open OpenTelemetry.Trace +let fileName fileId = $"File%s{fileId}.fs" + +let internal recordAllEvents groupBy = + let mutable cache : AsyncMemoize<_,_,_> option = None + let events = ResizeArray() + + let waitForIdle() = SpinWait.SpinUntil(fun () -> not cache.Value.Updating) + + let observe (getCache: CompilerCaches -> AsyncMemoize<_,_,_>) (checker: FSharpChecker) = + cache <- Some (getCache checker.Caches) + waitForIdle() + cache.Value.Event + |> Event.map (fun (e, k) -> groupBy k, e) + |> Event.add events.Add + + let getEvents () = + waitForIdle() + events |> List.ofSeq + + observe, getEvents + +let getFileNameKey (_l, (f: string, _p), _) = Path.GetFileName f + + // TODO: currently the label for DependecyGraph cache is $"%d{fileSnapshots.Length} files ending with {lastFile}" +let getDependecyGraphKey (_l, _, _) = failwith "not implemented" + +let internal recordEvents groupBy = + let observe, getEvents = recordAllEvents groupBy + + let check key expected = + let events = getEvents() + let actual = events |> Seq.filter (fun e -> fst e = key) |> Seq.map snd |> Seq.toList + printfn $"{key}: %A{actual}" + Assert.Equal(expected, actual) + + observe, check #nowarn "57" -[] +[] let ``Use Transparent Compiler`` () = let size = 20 @@ -59,7 +96,7 @@ let ``Use Transparent Compiler`` () = checkFile last expectSignatureChanged } -[] +[] let ``Parallel processing`` () = let project = SyntheticProject.Create( @@ -77,7 +114,7 @@ let ``Parallel processing`` () = checkFile "E" expectSignatureChanged } -[] +[] let ``Parallel processing with signatures`` () = let project = SyntheticProject.Create( @@ -112,7 +149,7 @@ let makeTestProject () = let testWorkflow () = ProjectWorkflowBuilder(makeTestProject(), useTransparentCompiler = true) -[] +[] let ``Edit file, check it, then check dependent file`` () = testWorkflow() { updateFile "First" breakDependentFiles @@ -120,21 +157,21 @@ let ``Edit file, check it, then check dependent file`` () = 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) @@ -143,7 +180,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>") } -[] +[] let ``Files depend on signature file if present`` () = let project = makeTestProject() |> updateFile "First" addSignatureFile @@ -153,7 +190,7 @@ let ``Files depend on signature file if present`` () = checkFile "Second" expectNoChanges } -[] +[] let ``Project with signatures`` () = let project = SyntheticProject.Create( @@ -168,7 +205,7 @@ let ``Project with signatures`` () = checkFile "Second" expectOk } -[] +[] let ``Signature update`` () = let project = SyntheticProject.Create( @@ -184,7 +221,7 @@ let ``Signature update`` () = checkFile "Second" expectSignatureChanged } -[] +[] let ``Adding a file`` () = testWorkflow() { addFileAbove "Second" (sourceFile "New" []) @@ -192,14 +229,14 @@ let ``Adding a file`` () = checkFile "Last" (expectSignatureContains "val f: x: 'a -> (ModuleFirst.TFirstV_1<'a> * ModuleNew.TNewV_1<'a> * ModuleSecond.TSecondV_1<'a>) * (ModuleFirst.TFirstV_1<'a> * ModuleThird.TThirdV_1<'a>) * TLastV_1<'a>") } -[] +[] let ``Removing a file`` () = testWorkflow() { removeFile "Second" checkFile "Last" expectErrors } -[] +[] let ``Changes in a referenced project`` () = let library = SyntheticProject.Create("library", sourceFile "Library" []) @@ -218,55 +255,35 @@ let ``Changes in a referenced project`` () = } -[] +[] let ``File is not checked twice`` () = - let cacheEvents = ConcurrentQueue() + let observe, check = recordEvents getFileNameKey testWorkflow() { - withChecker (fun checker -> - async { - do! Async.Sleep 50 // wait for events from initial project check - checker.Caches.TcIntermediate.OnEvent cacheEvents.Enqueue - }) + withChecker (observe _.TcIntermediate) updateFile "First" updatePublicSurface checkFile "Third" expectOk } |> ignore - let intermediateTypeChecks = - cacheEvents - |> Seq.groupBy (fun (_e, (_l, (f, _p), _)) -> f |> Path.GetFileName) - |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) - |> Map + check (fileName "First") [Weakened; Requested; Started; Finished] + check (fileName "Third") [Weakened; Requested; Started; Finished] - Assert.Equal([Weakened; Requested; Started; Finished], intermediateTypeChecks["FileFirst.fs"]) - Assert.Equal([Weakened; Requested; Started; Finished], intermediateTypeChecks["FileThird.fs"]) -[] +[] let ``If a file is checked as a dependency it's not re-checked later`` () = - let cacheEvents = ConcurrentQueue() + let observe, check = recordEvents getFileNameKey testWorkflow() { - withChecker (fun checker -> - async { - do! Async.Sleep 50 // wait for events from initial project check - checker.Caches.TcIntermediate.OnEvent cacheEvents.Enqueue - }) + withChecker (observe _.TcIntermediate) 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 + check (fileName "Third") [Weakened; Requested; Started; Finished; Requested] - Assert.Equal([Weakened; Requested; Started; Finished; Requested], intermediateTypeChecks["FileThird.fs"]) - - -// [] TODO: differentiate complete and minimal checking requests +[] // TODO: differentiate complete and minimal checking requests let ``We don't check files that are not depended on`` () = let project = SyntheticProject.Create( sourceFile "First" [], @@ -274,29 +291,19 @@ let ``We don't check files that are not depended on`` () = sourceFile "Third" ["First"], sourceFile "Last" ["Third"]) - let cacheEvents = ConcurrentQueue() + let observe, check = recordEvents getFileNameKey - ProjectWorkflowBuilder(project, useTransparentCompiler = true) { - withChecker (fun checker -> - async { - do! Async.Sleep 50 // wait for events from initial project check - checker.Caches.TcIntermediate.OnEvent cacheEvents.Enqueue - }) + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + withChecker (observe _.TcIntermediate) updateFile "First" updatePublicSurface checkFile "Last" expectOk } |> ignore - let intermediateTypeChecks = - cacheEvents - |> Seq.groupBy (fun (_e, (_l, (f, _p), _)) -> Path.GetFileName f) - |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) - |> Map + check "FileFirst.fs" [Weakened; Requested; Started; Finished] + check "FileThird.fs" [Weakened; Requested; Started; Finished] + // check "FileSecond.fs" [] // TODO: assert does not hold. - Assert.Equal([Started; Finished], intermediateTypeChecks["FileFirst.fs"]) - Assert.Equal([Started; Finished], intermediateTypeChecks["FileThird.fs"]) - Assert.False (intermediateTypeChecks.ContainsKey "FileSecond.fs") - -// [] TODO: differentiate complete and minimal checking requests +[] // TODO: differentiate complete and minimal checking requests let ``Files that are not depended on don't invalidate cache`` () = let project = SyntheticProject.Create( sourceFile "First" [], @@ -304,40 +311,22 @@ let ``Files that are not depended on don't invalidate cache`` () = sourceFile "Third" ["First"], sourceFile "Last" ["Third"]) - let cacheTcIntermediateEvents = ConcurrentQueue() - let cacheGraphConstructionEvents = ConcurrentQueue() + let observeTcIntermediateEvents, _getTcIntermediateEvents = recordAllEvents getFileNameKey + // let observeGraphConstructionEvents, checkGraphConstructionEvents = record getDependecyGraphKey ProjectWorkflowBuilder(project, useTransparentCompiler = true) { updateFile "First" updatePublicSurface checkFile "Last" expectOk - withChecker (fun checker -> - async { - do! Async.Sleep 50 // wait for events from initial project check - checker.Caches.TcIntermediate.OnEvent cacheTcIntermediateEvents.Enqueue - checker.Caches.DependencyGraph.OnEvent cacheGraphConstructionEvents.Enqueue - - }) + withChecker (observeTcIntermediateEvents _.TcIntermediate) + // withChecker (observeGraphConstructionEvents _.DependencyGraph) updateFile "Second" updatePublicSurface checkFile "Last" expectOk } |> ignore - let intermediateTypeChecks = - cacheTcIntermediateEvents - |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) - |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) - |> Map - - let graphConstructions = - cacheGraphConstructionEvents - |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) - |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) - |> Map + // Assert.Empty(getTcIntermediateEvents()) TODO: assert does not hold + // checkGraphConstructionEvents "FileLast.fs" [Started; Finished] - Assert.Equal([Started; Finished], graphConstructions["FileLast.fs"]) - - Assert.Equal([], intermediateTypeChecks |> Map.toList) - -// [] TODO: differentiate complete and minimal checking requests +[] // 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" [], @@ -346,67 +335,41 @@ let ``Files that are not depended on don't invalidate cache part 2`` () = sourceFile "D" ["B"; "C"], sourceFile "E" ["C"]) - let cacheTcIntermediateEvents = ConcurrentQueue() - let cacheGraphConstructionEvents = ConcurrentQueue() + let observeTcIntermediateEvents, checkTcIntermediateEvents = recordEvents getFileNameKey + // let observeGraphConstructionEvents, checkGraphConstructionEvents = record getDependecyGraphKey ProjectWorkflowBuilder(project, useTransparentCompiler = true) { updateFile "A" updatePublicSurface checkFile "D" expectOk - withChecker (fun checker -> - async { - do! Async.Sleep 50 // wait for events from initial project check - checker.Caches.TcIntermediate.OnEvent cacheTcIntermediateEvents.Enqueue - checker.Caches.DependencyGraph.OnEvent cacheGraphConstructionEvents.Enqueue - }) + withChecker (observeTcIntermediateEvents _.TcIntermediate) + // withChecker (observeGraphConstructionEvents _.DependencyGraph) updateFile "B" updatePublicSurface checkFile "E" expectOk } |> ignore - let intermediateTypeChecks = - cacheTcIntermediateEvents - |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) - |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) - |> Seq.toList - - let graphConstructions = - cacheGraphConstructionEvents - |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) - |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) - |> Seq.toList + checkTcIntermediateEvents "FileE.fs" [Weakened; Requested; Started; Finished] + // checkGraphConstructionEvents "FileE.fs" [Weakened; Requested; Started; Finished] - Assert.Equal(["FileE.fs", [Started; Finished]], graphConstructions) - Assert.Equal(["FileE.fs", [Started; Finished]], intermediateTypeChecks) - -[] +[] let ``Changing impl files doesn't invalidate cache when they have signatures`` () = let project = SyntheticProject.Create( { sourceFile "A" [] with SignatureFile = AutoGenerated }, { sourceFile "B" ["A"] with SignatureFile = AutoGenerated }, { sourceFile "C" ["B"] with SignatureFile = AutoGenerated }) - let cacheEvents = ConcurrentQueue() + let observe, getEvents = recordAllEvents getFileNameKey ProjectWorkflowBuilder(project, useTransparentCompiler = true) { updateFile "A" updatePublicSurface checkFile "C" expectOk - withChecker (fun checker -> - async { - do! Async.Sleep 50 // wait for events from initial project check - checker.Caches.TcIntermediate.OnEvent cacheEvents.Enqueue - }) + withChecker ( observe _.TcIntermediate) updateFile "A" updateInternal checkFile "C" expectOk } |> ignore - let intermediateTypeChecks = - cacheEvents - |> Seq.groupBy (fun (_e, (l, _k, _)) -> l) - |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) - |> Seq.toList - - Assert.Equal([], intermediateTypeChecks) + Assert.Empty(getEvents()) -[] +[] let ``Changing impl file doesn't invalidate an in-memory referenced project`` () = let library = SyntheticProject.Create("library", { sourceFile "A" [] with SignatureFile = AutoGenerated }) @@ -414,27 +377,19 @@ let ``Changing impl file doesn't invalidate an in-memory referenced project`` () SyntheticProject.Create("project", sourceFile "B" ["A"] ) with DependsOn = [library] } - let cacheEvents = ConcurrentQueue() + let mutable count = 0 ProjectWorkflowBuilder(project, useTransparentCompiler = true) { checkFile "B" expectOk withChecker (fun checker -> async { - do! Async.Sleep 50 // wait for events from initial project check - checker.Caches.TcIntermediate.OnEvent cacheEvents.Enqueue + checker.Caches.TcIntermediate.OnEvent (fun _ -> Interlocked.Increment &count |> ignore) }) 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) - + Assert.Equal(0, count) [] [] @@ -645,13 +600,10 @@ let fuzzingTest seed (project: SyntheticProject) = task { builder.DeleteProjectDir() } - -(* This gets in the way of insertions too often now, uncomment when stable. [] [] [] [] -*) let Fuzzing signatureFiles = let seed = System.Random().Next() @@ -789,7 +741,7 @@ module Stuff = let fileName, snapshot, checker = singleFileChecker source checker.ParseFile(fileName, snapshot) |> Async.RunSynchronously - //[] + //[] let ``Hash stays the same when whitespace changes`` () = //let parseResult = getParseResult source @@ -845,61 +797,41 @@ let ``TypeCheck last file in project with transparent compiler`` useTransparentC checkFile lastFile expectOk } -[] +[] let ``LoadClosure for script is computed once`` () = - let project = SyntheticProject.CreateForScript( - sourceFile "First" []) + let project = SyntheticProject.CreateForScript( + sourceFile "First" []) - let cacheEvents = ConcurrentQueue() + let observe, getEvents = recordAllEvents getFileNameKey - ProjectWorkflowBuilder(project, useTransparentCompiler = true) { - withChecker (fun checker -> - async { - do! Async.Sleep 50 // wait for events from initial project check - checker.Caches.ScriptClosure.OnEvent cacheEvents.Enqueue - }) + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { + withChecker (observe _.ScriptClosure) + checkFile "First" expectOk + } + |> ignore - checkFile "First" expectOk - } |> ignore - - let closureComputations = - cacheEvents - |> Seq.groupBy (fun (_e, (_l, (f, _p), _)) -> Path.GetFileName f) - |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) - |> Map - - Assert.Empty(closureComputations) + Assert.Empty(getEvents()) -[] +[] let ``LoadClosure for script is recomputed after changes`` () = + let project = SyntheticProject.CreateForScript( sourceFile "First" []) - let cacheEvents = ConcurrentQueue() - + let observe, check = recordEvents getFileNameKey + ProjectWorkflowBuilder(project, useTransparentCompiler = true) { - withChecker (fun checker -> - async { - do! Async.Sleep 50 // wait for events from initial project check - checker.Caches.ScriptClosure.OnEvent cacheEvents.Enqueue - }) - + withChecker (observe _.ScriptClosure) checkFile "First" expectOk updateFile "First" updateInternal checkFile "First" expectOk updateFile "First" updatePublicSurface checkFile "First" expectOk } |> ignore - - let closureComputations = - cacheEvents - |> Seq.groupBy (fun (_e, (_l, (f, _p), _)) -> Path.GetFileName f) - |> Seq.map (fun (k, g) -> k, g |> Seq.map fst |> Seq.toList) - |> Map - Assert.Equal([Weakened; Requested; Started; Finished; Weakened; Requested; Started; Finished], closureComputations["FileFirst.fs"]) + check (fileName "First") [Weakened; Requested; Started; Finished; Weakened; Requested; Started; Finished] -[] +[] let ``TryGetRecentCheckResultsForFile returns None before first call to ParseAndCheckFileInProject`` () = let project = SyntheticProject.Create( sourceFile "First" []) @@ -909,7 +841,7 @@ let ``TryGetRecentCheckResultsForFile returns None before first call to ParseAnd tryGetRecentCheckResults "First" expectNone } |> ignore -[] +[] let ``TryGetRecentCheckResultsForFile returns result after first call to ParseAndCheckFileInProject`` () = let project = SyntheticProject.Create( sourceFile "First" [] ) @@ -918,7 +850,7 @@ let ``TryGetRecentCheckResultsForFile returns result after first call to ParseAn tryGetRecentCheckResults "First" expectSome } |> ignore -[] +[] let ``TryGetRecentCheckResultsForFile returns no result after edit`` () = let project = SyntheticProject.Create( sourceFile "First" []) @@ -931,7 +863,7 @@ let ``TryGetRecentCheckResultsForFile returns no result after edit`` () = tryGetRecentCheckResults "First" expectSome } |> ignore -[] +[] let ``TryGetRecentCheckResultsForFile returns result after edit of other file`` () = let project = SyntheticProject.Create( sourceFile "First" [], @@ -945,9 +877,9 @@ let ``TryGetRecentCheckResultsForFile returns result after edit of other file`` tryGetRecentCheckResults "Second" expectSome // file didn't change so we still want to get the recent result } |> ignore -[] +[] let ``Background compiler and Transparent compiler return the same options`` () = - async { + task { let backgroundChecker = FSharpChecker.Create(useTransparentCompiler = false) let transparentChecker = FSharpChecker.Create(useTransparentCompiler = true) let scriptName = Path.Combine(__SOURCE_DIRECTORY__, "script.fsx") @@ -1012,15 +944,14 @@ printfn "Hello from F#" checkFile "As 01" expectTwoWarnings } -[] +[] let ``Transparent Compiler ScriptClosure cache is populated after GetProjectOptionsFromScript`` () = - async { + task { let transparentChecker = FSharpChecker.Create(useTransparentCompiler = true) let scriptName = Path.Combine(__SOURCE_DIRECTORY__, "script.fsx") let content = SourceTextNew.ofString "" let! _ = transparentChecker.GetProjectOptionsFromScript(scriptName, content) Assert.Equal(1, transparentChecker.Caches.ScriptClosure.Count) - } type private LoadClosureTestShim(currentFileSystem: IFileSystem) = @@ -1064,67 +995,69 @@ type private LoadClosureTestShim(currentFileSystem: IFileSystem) = ?shouldShadowCopy = shouldShadowCopy ) -[] -[] -[] -let ``The script load closure should always be evaluated`` useTransparentCompiler = - async { - // The LoadScriptClosure uses the file system shim so we need to reset that. - let currentFileSystem = FileSystemAutoOpens.FileSystem - let assumeDotNetFramework = - // The old BackgroundCompiler uses assumeDotNetFramework = true - // This is not always correctly loading when this test runs on non-Windows. - if System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework") then - None - else - Some false +module TestsMutatingFileSystem = + + [] + [] + [] + let ``The script load closure should always be evaluated`` useTransparentCompiler = + async { + // The LoadScriptClosure uses the file system shim so we need to reset that. + let currentFileSystem = FileSystemAutoOpens.FileSystem + let assumeDotNetFramework = + // The old BackgroundCompiler uses assumeDotNetFramework = true + // This is not always correctly loading when this test runs on non-Windows. + if System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework") then + None + else + Some false - try - let checker = FSharpChecker.Create(useTransparentCompiler = useTransparentCompiler) - let fileSystemShim = LoadClosureTestShim(currentFileSystem) - // Override the file system shim for loading b.fsx - FileSystem <- fileSystemShim - - let! initialSnapshot, _ = - checker.GetProjectSnapshotFromScript( - "a.fsx", - SourceTextNew.ofString fileSystemShim.aFsx, - documentSource = DocumentSource.Custom fileSystemShim.DocumentSource, - ?assumeDotNetFramework = assumeDotNetFramework - ) - - // File b.fsx should also be included in the snapshot. - Assert.Equal(2, initialSnapshot.SourceFiles.Length) - - let! checkResults = checker.ParseAndCheckFileInProject("a.fsx", initialSnapshot) - - match snd checkResults with - | FSharpCheckFileAnswer.Aborted -> failwith "Did not expected FSharpCheckFileAnswer.Aborted" - | FSharpCheckFileAnswer.Succeeded checkFileResults -> Assert.Equal(0, checkFileResults.Diagnostics.Length) + try + let checker = FSharpChecker.Create(useTransparentCompiler = useTransparentCompiler) + let fileSystemShim = LoadClosureTestShim(currentFileSystem) + // Override the file system shim for loading b.fsx + FileSystem <- fileSystemShim + + let! initialSnapshot, _ = + checker.GetProjectSnapshotFromScript( + "a.fsx", + SourceTextNew.ofString fileSystemShim.aFsx, + documentSource = DocumentSource.Custom fileSystemShim.DocumentSource, + ?assumeDotNetFramework = assumeDotNetFramework + ) + + // File b.fsx should also be included in the snapshot. + Assert.Equal(2, initialSnapshot.SourceFiles.Length) + + let! checkResults = checker.ParseAndCheckFileInProject("a.fsx", initialSnapshot) + + match snd checkResults with + | FSharpCheckFileAnswer.Aborted -> failwith "Did not expected FSharpCheckFileAnswer.Aborted" + | FSharpCheckFileAnswer.Succeeded checkFileResults -> Assert.Equal(0, checkFileResults.Diagnostics.Length) - // Update b.fsx, it should now load c.fsx - fileSystemShim.UpdateB() - - // The constructed key for the load closure will the exactly the same as the first GetProjectSnapshotFromScript call. - // However, a none cached version will be computed first in GetProjectSnapshotFromScript and update the cache afterwards. - let! secondSnapshot, _ = - checker.GetProjectSnapshotFromScript( - "a.fsx", - SourceTextNew.ofString fileSystemShim.aFsx, - documentSource = DocumentSource.Custom fileSystemShim.DocumentSource, - ?assumeDotNetFramework = assumeDotNetFramework - ) - - Assert.Equal(3, secondSnapshot.SourceFiles.Length) - - let! checkResults = checker.ParseAndCheckFileInProject("a.fsx", secondSnapshot) - - match snd checkResults with - | FSharpCheckFileAnswer.Aborted -> failwith "Did not expected FSharpCheckFileAnswer.Aborted" - | FSharpCheckFileAnswer.Succeeded checkFileResults -> Assert.Equal(0, checkFileResults.Diagnostics.Length) - finally - FileSystemAutoOpens.FileSystem <- currentFileSystem - } + // Update b.fsx, it should now load c.fsx + fileSystemShim.UpdateB() + + // The constructed key for the load closure will the exactly the same as the first GetProjectSnapshotFromScript call. + // However, a none cached version will be computed first in GetProjectSnapshotFromScript and update the cache afterwards. + let! secondSnapshot, _ = + checker.GetProjectSnapshotFromScript( + "a.fsx", + SourceTextNew.ofString fileSystemShim.aFsx, + documentSource = DocumentSource.Custom fileSystemShim.DocumentSource, + ?assumeDotNetFramework = assumeDotNetFramework + ) + + Assert.Equal(3, secondSnapshot.SourceFiles.Length) + + let! checkResults = checker.ParseAndCheckFileInProject("a.fsx", secondSnapshot) + + match snd checkResults with + | FSharpCheckFileAnswer.Aborted -> failwith "Did not expected FSharpCheckFileAnswer.Aborted" + | FSharpCheckFileAnswer.Succeeded checkFileResults -> Assert.Equal(0, checkFileResults.Diagnostics.Length) + finally + FileSystemAutoOpens.FileSystem <- currentFileSystem + } [] let ``Parsing without cache and without project snapshot`` () = diff --git a/tests/FSharp.Compiler.Service.Tests/ExprTests.fs b/tests/FSharp.Compiler.Service.Tests/ExprTests.fs index 4d74b131ed5..251e1fc3a91 100644 --- a/tests/FSharp.Compiler.Service.Tests/ExprTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ExprTests.fs @@ -1,6 +1,7 @@ module FSharp.Compiler.Service.Tests.ExprTests open Xunit +open FSharp.Test open FSharp.Test.Assert open System open System.Text @@ -656,13 +657,11 @@ let test{0}ToStringOperator (e1:{1}) = string e1 """ /// This test is run in unison with its optimized counterpart below -[] -[] -[] -let ``Test Unoptimized Declarations Project1`` useTransparentCompiler = +[] +let ``Test Unoptimized Declarations Project1`` () = let cleanup, options = Project1.createOptionsWithArgs [ "--langversion:preview" ] use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -797,13 +796,11 @@ let ``Test Unoptimized Declarations Project1`` useTransparentCompiler = () -[] -[] -[] -let ``Test Optimized Declarations Project1`` useTransparentCompiler = +[] +let ``Test Optimized Declarations Project1`` () = let cleanup, options = Project1.createOptionsWithArgs [ "--langversion:preview" ] use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3136,14 +3133,12 @@ let BigSequenceExpression(outFileOpt,docFileOpt,baseAddressOpt) = #if !NETFRAMEWORK && DEBUG [] #else -[] -[] -[] +[] #endif -let ``Test expressions of declarations stress big expressions`` useTransparentCompiler = +let ``Test expressions of declarations stress big expressions`` () = let cleanup, options = ProjectStressBigExpressions.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate wholeProjectResults.Diagnostics.Length |> shouldEqual 0 @@ -3157,14 +3152,12 @@ let ``Test expressions of declarations stress big expressions`` useTransparentCo #if !NETFRAMEWORK && DEBUG [] #else -[] -[] -[] +[] #endif -let ``Test expressions of optimized declarations stress big expressions`` useTransparentCompiler = +let ``Test expressions of optimized declarations stress big expressions`` () = let cleanup, options = ProjectStressBigExpressions.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate wholeProjectResults.Diagnostics.Length |> shouldEqual 0 @@ -3220,13 +3213,11 @@ let f8() = callXY (D()) (C()) let createOptions() = createProjectOptions dirName [fileSource1] ["--langversion:7.0"] -[] -[] -[] -let ``Test ProjectForWitnesses1`` useTransparentCompiler = +[] +let ``Test ProjectForWitnesses1`` () = let cleanup, options = ProjectForWitnesses1.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3266,13 +3257,11 @@ let ``Test ProjectForWitnesses1`` useTransparentCompiler = |> shouldEqual expected -[] -[] -[] -let ``Test ProjectForWitnesses1 GetWitnessPassingInfo`` useTransparentCompiler = +[] +let ``Test ProjectForWitnesses1 GetWitnessPassingInfo`` () = let cleanup, options = ProjectForWitnesses1.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3348,13 +3337,11 @@ type MyNumberWrapper = let createOptions() = createProjectOptions dirName [fileSource1] ["--langversion:7.0"] -[] -[] -[] -let ``Test ProjectForWitnesses2`` useTransparentCompiler = +[] +let ``Test ProjectForWitnesses2`` () = let cleanup, options = ProjectForWitnesses2.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3406,13 +3393,11 @@ let s2 = sign p1 let createOptions() = createProjectOptions dirName [fileSource1] ["--langversion:7.0"] -[] -[] -[] -let ``Test ProjectForWitnesses3`` useTransparentCompiler = +[] +let ``Test ProjectForWitnesses3`` () = let cleanup, options = createProjectOptions dirName [ ProjectForWitnesses3.fileSource1 ] ["--langversion:7.0"] use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3439,13 +3424,11 @@ let ``Test ProjectForWitnesses3`` useTransparentCompiler = actual |> shouldEqual expected -[] -[] -[] -let ``Test ProjectForWitnesses3 GetWitnessPassingInfo`` useTransparentCompiler = +[] +let ``Test ProjectForWitnesses3 GetWitnessPassingInfo`` () = let cleanup, options = ProjectForWitnesses3.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3504,13 +3487,11 @@ let isNullQuoted (ts : 't[]) = let createOptions() = createProjectOptions dirName [fileSource1] ["--langversion:7.0"] -[] -[] -[] -let ``Test ProjectForWitnesses4 GetWitnessPassingInfo`` useTransparentCompiler = +[] +let ``Test ProjectForWitnesses4 GetWitnessPassingInfo`` () = let cleanup, options = ProjectForWitnesses4.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -3544,13 +3525,11 @@ let rec f = new System.EventHandler(fun _ _ -> f.Invoke(null,null)) let createOptions() = createProjectOptions dirName [fileSource1] [] -[] -[] -[] -let ``Test NoWarn HashDirective`` useTransparentCompiler = +[] +let ``Test NoWarn HashDirective`` () = let cleanup, options = ProjectForNoWarnHashDirective.createOptions() use _holder = cleanup - let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let exprChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do diff --git a/tests/FSharp.Compiler.Service.Tests/MultiProjectAnalysisTests.fs b/tests/FSharp.Compiler.Service.Tests/MultiProjectAnalysisTests.fs index 230d90f2527..ee6b23794b1 100644 --- a/tests/FSharp.Compiler.Service.Tests/MultiProjectAnalysisTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/MultiProjectAnalysisTests.fs @@ -1,6 +1,7 @@ module FSharp.Compiler.Service.Tests.MultiProjectAnalysisTests open Xunit +open FSharp.Test open FSharp.Test.Assert open System.IO open System.Collections.Generic @@ -14,8 +15,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) +let internal checker = FSharpChecker.Create(projectCacheSize=numProjectsForStressTest + 10, useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) /// Extract range info let internal tups (m:range) = (m.StartLine, m.StartColumn), (m.EndLine, m.EndColumn) @@ -124,12 +124,8 @@ let u = Case1 3 FSharpReferencedProject.FSharpReference(Project1B.dllName, Project1B.options); |] } let cleanFileName a = if a = fileName1 then "file1" else "??" -[] -[] -[] -let ``Test multi project 1 basic`` useTransparentCompiler = - - let checker = if useTransparentCompiler then transparentCompilerChecker else checker +[] +let ``Test multi project 1 basic`` () = let wholeProjectResults = checker.ParseAndCheckProject(MultiProject1.options) |> Async.RunImmediate @@ -141,12 +137,8 @@ let ``Test multi project 1 basic`` useTransparentCompiler = [ for x in wholeProjectResults.AssemblySignature.Entities[0].MembersFunctionsAndValues -> x.DisplayName ] |> shouldEqual ["p"; "c"; "u"] -[] -[] -[] -let ``Test multi project 1 all symbols`` useTransparentCompiler = - - let checker = if useTransparentCompiler then transparentCompilerChecker else checker +[] +let ``Test multi project 1 all symbols`` () = let p1A = checker.ParseAndCheckProject(Project1A.options) |> Async.RunImmediate let p1B = checker.ParseAndCheckProject(Project1B.options) |> Async.RunImmediate @@ -183,12 +175,8 @@ let ``Test multi project 1 all symbols`` useTransparentCompiler = usesOfx1FromProject1AInMultiProject1 |> shouldEqual usesOfx1FromMultiProject1InMultiProject1 -[] -[] -[] -let ``Test multi project 1 xmldoc`` useTransparentCompiler = - - let checker = if useTransparentCompiler then transparentCompilerChecker else checker +[] +let ``Test multi project 1 xmldoc`` () = let p1A = checker.ParseAndCheckProject(Project1A.options) |> Async.RunImmediate let p1B = checker.ParseAndCheckProject(Project1B.options) |> Async.RunImmediate @@ -263,12 +251,12 @@ let ``Test multi project 1 xmldoc`` useTransparentCompiler = //------------------------------------------------------------------------------------ +type private Project = { ModuleName: string; FileName: string; Options: FSharpProjectOptions; DllName: string } // A project referencing many sub-projects -module internal ManyProjectsStressTest = +type private ManyProjectsStressTest() = let numProjectsForStressTest = 100 - type Project = { ModuleName: string; FileName: string; Options: FSharpProjectOptions; DllName: string } let projects = [ for i in 1 .. numProjectsForStressTest do let fileName1 = Path.ChangeExtension(getTemporaryFileName (), ".fs") @@ -325,18 +313,23 @@ let p = (""" |> function Some x -> x | None -> if a = jointProject.FileName then "fileN" else "??" - let makeCheckerForStressTest ensureBigEnough useTransparentCompiler = + member _.JointProject = jointProject + member _.Projects = projects + member _.CleanFileName a = cleanFileName a + static member MakeCheckerForStressTest ensureBigEnough = let size = (if ensureBigEnough then numProjectsForStressTest + 10 else numProjectsForStressTest / 2 ) - FSharpChecker.Create(projectCacheSize=size, useTransparentCompiler=useTransparentCompiler) + FSharpChecker.Create(projectCacheSize=size, useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) + + -[] -[] -[] -let ``Test ManyProjectsStressTest basic`` useTransparentCompiler = +[] +let ``Test ManyProjectsStressTest basic`` () = - let checker = ManyProjectsStressTest.makeCheckerForStressTest true useTransparentCompiler + let manyProjectsStressTest = ManyProjectsStressTest() - let wholeProjectResults = checker.ParseAndCheckProject(ManyProjectsStressTest.jointProject.Options) |> Async.RunImmediate + let checker = ManyProjectsStressTest.MakeCheckerForStressTest true + + let wholeProjectResults = checker.ParseAndCheckProject(manyProjectsStressTest.JointProject.Options) |> Async.RunImmediate [ for x in wholeProjectResults.AssemblySignature.Entities -> x.DisplayName ] |> shouldEqual ["JointProject"] @@ -345,14 +338,14 @@ let ``Test ManyProjectsStressTest basic`` useTransparentCompiler = [ for x in wholeProjectResults.AssemblySignature.Entities[0].MembersFunctionsAndValues -> x.DisplayName ] |> shouldEqual ["p"] -[] -[] -[] -let ``Test ManyProjectsStressTest cache too small`` useTransparentCompiler = +[] +let ``Test ManyProjectsStressTest cache too small`` () = + + let manyProjectsStressTest = ManyProjectsStressTest() - let checker = ManyProjectsStressTest.makeCheckerForStressTest false useTransparentCompiler + let checker = ManyProjectsStressTest.MakeCheckerForStressTest false - let wholeProjectResults = checker.ParseAndCheckProject(ManyProjectsStressTest.jointProject.Options) |> Async.RunImmediate + let wholeProjectResults = checker.ParseAndCheckProject(manyProjectsStressTest.JointProject.Options) |> Async.RunImmediate [ for x in wholeProjectResults.AssemblySignature.Entities -> x.DisplayName ] |> shouldEqual ["JointProject"] @@ -361,16 +354,17 @@ let ``Test ManyProjectsStressTest cache too small`` useTransparentCompiler = [ for x in wholeProjectResults.AssemblySignature.Entities[0].MembersFunctionsAndValues -> x.DisplayName ] |> shouldEqual ["p"] -[] -[] -[] -let ``Test ManyProjectsStressTest all symbols`` useTransparentCompiler = +[] +let ``Test ManyProjectsStressTest all symbols`` () = + + let manyProjectsStressTest = ManyProjectsStressTest() - let checker = ManyProjectsStressTest.makeCheckerForStressTest true useTransparentCompiler + + let checker = ManyProjectsStressTest.MakeCheckerForStressTest true 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 ] - let jointProjectResults = checker.ParseAndCheckProject(ManyProjectsStressTest.jointProject.Options) |> Async.RunImmediate + let projectsResults = [ for p in manyProjectsStressTest.Projects -> p, checker.ParseAndCheckProject(p.Options) |> Async.RunImmediate ] + let jointProjectResults = checker.ParseAndCheckProject(manyProjectsStressTest.JointProject.Options) |> Async.RunImmediate let vsFromJointProject = [ for s in jointProjectResults.GetAllUsesOfAllSymbols() do @@ -388,13 +382,13 @@ let ``Test ManyProjectsStressTest all symbols`` useTransparentCompiler = let usesFromJointProject = jointProjectResults.GetUsesOfSymbol(vFromProject) - |> Array.map (fun s -> s.Symbol.DisplayName, ManyProjectsStressTest.cleanFileName s.FileName, tups s.Symbol.DeclarationLocation.Value) + |> Array.map (fun s -> s.Symbol.DisplayName, manyProjectsStressTest.CleanFileName s.FileName, tups s.Symbol.DeclarationLocation.Value) usesFromJointProject.Length |> shouldEqual 1 //----------------------------------------------------------------------------------------- -module internal MultiProjectDirty1 = +type internal MultiProjectDirty1(checker: FSharpChecker) = let fileName1 = Path.ChangeExtension(getTemporaryFileName (), ".fs") let baseName = getTemporaryFileName() @@ -405,18 +399,20 @@ module internal MultiProjectDirty1 = let x = "F#" """ - FileSystem.OpenFileForWriteShim(fileName1).Write(content) + do FileSystem.OpenFileForWriteShim(fileName1).Write(content) - let cleanFileName a = if a = fileName1 then "Project1" else "??" let fileNames = [|fileName1|] - let getOptions() = + member _.Content = content + member _.CleanFileName a = if a = fileName1 then "Project1" else "??" + member _.DllName = dllName + member _.FileName1 = fileName1 + member _.GetOptions() = let args = mkProjectCommandLineArgs (dllName, fileNames) { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } -module internal MultiProjectDirty2 = - +type internal MultiProjectDirty2(checker: FSharpChecker, multiProjectDirty1: MultiProjectDirty1) = let fileName1 = Path.ChangeExtension(getTemporaryFileName (), ".fs") let baseName = getTemporaryFileName () @@ -430,43 +426,49 @@ open Project1 let y = x let z = Project1.x """ - FileSystem.OpenFileForWriteShim(fileName1).Write(content) + do FileSystem.OpenFileForWriteShim(fileName1).Write(content) let cleanFileName a = if a = fileName1 then "Project2" else "??" let fileNames = [|fileName1|] - let getOptions() = + member _.CleanFileName a = cleanFileName a + member _.DllName = dllName + member _.FileName1 = fileName1 + member _.GetOptions() = let args = mkProjectCommandLineArgs (dllName, fileNames) 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()) |] } + OtherOptions = Array.append options.OtherOptions [| ("-r:" + multiProjectDirty1.DllName) |] + ReferencedProjects = [| FSharpReferencedProject.FSharpReference(multiProjectDirty1.DllName, multiProjectDirty1.GetOptions()) |] } -[] -// [] -[] -let ``Test multi project symbols should pick up changes in dependent projects`` useTransparentCompiler = +[] +let ``Test multi project symbols should pick up changes in dependent projects`` () = - let checker = if useTransparentCompiler then transparentCompilerChecker else checker + // A private checker because we subscribe to FileChecked. + let checker = FSharpChecker.Create(useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) + + let multiProjectDirty1 = MultiProjectDirty1(checker) + let multiProjectDirty2 = MultiProjectDirty2(checker, multiProjectDirty1) // register to count the file checks - let count = ref 0 - checker.FileChecked.Add (fun _ -> incr count) + let mutable count = 0 + + checker.FileChecked.Add (fun _ -> System.Threading.Interlocked.Increment &count |> ignore) //---------------- Write the first version of the file in project 1 and check the project -------------------- - let proj1options = MultiProjectDirty1.getOptions() + let proj1options = multiProjectDirty1.GetOptions() let wholeProjectResults1 = checker.ParseAndCheckProject(proj1options) |> Async.RunImmediate - count.Value |> shouldEqual 1 + count |> shouldEqual 1 let backgroundParseResults1, backgroundTypedParse1 = - checker.GetBackgroundCheckResultsForFileInProject(MultiProjectDirty1.fileName1, proj1options) + checker.GetBackgroundCheckResultsForFileInProject(multiProjectDirty1.FileName1, proj1options) |> Async.RunImmediate - count.Value |> shouldEqual 1 + count |> shouldEqual 1 //---------------- Get a symbol from project 1 and look up its uses in both projects -------------------- @@ -476,19 +478,19 @@ let ``Test multi project symbols should pick up changes in dependent projects`` printfn "Symbol found. Checking symbol uses in another project..." - let proj2options = MultiProjectDirty2.getOptions() + let proj2options = multiProjectDirty2.GetOptions() let wholeProjectResults2 = checker.ParseAndCheckProject(proj2options) |> Async.RunImmediate - count.Value |> shouldEqual 2 + count |> shouldEqual 2 let _ = checker.ParseAndCheckProject(proj2options) |> Async.RunImmediate - count.Value |> shouldEqual 2 // cached + count |> shouldEqual 2 // cached let usesOfXSymbolInProject1 = wholeProjectResults1.GetUsesOfSymbol(xSymbol) - |> Array.map (fun su -> su.Symbol.ToString(), MultiProjectDirty1.cleanFileName su.FileName, tups su.Range) + |> Array.map (fun su -> su.Symbol.ToString(), multiProjectDirty1.CleanFileName su.FileName, tups su.Range) usesOfXSymbolInProject1 |> shouldEqual @@ -496,7 +498,7 @@ let ``Test multi project symbols should pick up changes in dependent projects`` let usesOfXSymbolInProject2 = wholeProjectResults2.GetUsesOfSymbol(xSymbol) - |> Array.map (fun su -> su.Symbol.ToString(), MultiProjectDirty2.cleanFileName su.FileName, tups su.Range) + |> Array.map (fun su -> su.Symbol.ToString(), multiProjectDirty2.CleanFileName su.FileName, tups su.Range) usesOfXSymbolInProject2 |> shouldEqual @@ -506,22 +508,21 @@ let ``Test multi project symbols should pick up changes in dependent projects`` //---------------- Change the file by adding a line, then re-check everything -------------------- let wt0 = System.DateTime.UtcNow - let wt1 = FileSystem.GetLastWriteTimeShim MultiProjectDirty1.fileName1 - printfn "Writing new content to file '%s'" MultiProjectDirty1.fileName1 - - System.Threading.Thread.Sleep(1000) - FileSystem.OpenFileForWriteShim(MultiProjectDirty1.fileName1).Write(System.Environment.NewLine + MultiProjectDirty1.content) - printfn "Wrote new content to file '%s'" MultiProjectDirty1.fileName1 - let wt2 = FileSystem.GetLastWriteTimeShim MultiProjectDirty1.fileName1 + let wt1 = FileSystem.GetLastWriteTimeShim multiProjectDirty1.FileName1 + printfn "Writing new content to file '%s'" multiProjectDirty1.FileName1 + FileSystem.OpenFileForWriteShim(multiProjectDirty1.FileName1).Write(System.Environment.NewLine + multiProjectDirty1.Content) + printfn "Wrote new content to file '%s'" multiProjectDirty1.FileName1 + let wt2 = FileSystem.GetLastWriteTimeShim multiProjectDirty1.FileName1 + Assert.NotEqual(wt1, wt2) printfn "Current time: '%A', ticks = %d" wt0 wt0.Ticks printfn "Old write time: '%A', ticks = %d" wt1 wt1.Ticks printfn "New write time: '%A', ticks = %d" wt2 wt2.Ticks let wholeProjectResults1AfterChange1 = checker.ParseAndCheckProject(proj1options) |> Async.RunImmediate - count.Value |> shouldEqual 3 + count |> shouldEqual 3 let backgroundParseResults1AfterChange1, backgroundTypedParse1AfterChange1 = - checker.GetBackgroundCheckResultsForFileInProject(MultiProjectDirty1.fileName1, proj1options) + checker.GetBackgroundCheckResultsForFileInProject(multiProjectDirty1.FileName1, proj1options) |> Async.RunImmediate let xSymbolUseAfterChange1 = backgroundTypedParse1AfterChange1.GetSymbolUseAtLocation(4, 4, "", ["x"]) @@ -533,11 +534,11 @@ let ``Test multi project symbols should pick up changes in dependent projects`` let wholeProjectResults2AfterChange1 = checker.ParseAndCheckProject(proj2options) |> Async.RunImmediate - count.Value |> shouldEqual 4 + count |> shouldEqual 4 let usesOfXSymbolInProject1AfterChange1 = wholeProjectResults1AfterChange1.GetUsesOfSymbol(xSymbolAfterChange1) - |> Array.map (fun su -> su.Symbol.ToString(), MultiProjectDirty1.cleanFileName su.FileName, tups su.Range) + |> Array.map (fun su -> su.Symbol.ToString(), multiProjectDirty1.CleanFileName su.FileName, tups su.Range) usesOfXSymbolInProject1AfterChange1 |> shouldEqual @@ -545,7 +546,7 @@ let ``Test multi project symbols should pick up changes in dependent projects`` let usesOfXSymbolInProject2AfterChange1 = wholeProjectResults2AfterChange1.GetUsesOfSymbol(xSymbolAfterChange1) - |> Array.map (fun su -> su.Symbol.ToString(), MultiProjectDirty2.cleanFileName su.FileName, tups su.Range) + |> Array.map (fun su -> su.Symbol.ToString(), multiProjectDirty2.CleanFileName su.FileName, tups su.Range) usesOfXSymbolInProject2AfterChange1 |> shouldEqual @@ -555,29 +556,27 @@ let ``Test multi project symbols should pick up changes in dependent projects`` //---------------- Revert the change to the file -------------------- let wt0b = System.DateTime.UtcNow - let wt1b = FileSystem.GetLastWriteTimeShim MultiProjectDirty1.fileName1 - printfn "Writing old content to file '%s'" MultiProjectDirty1.fileName1 - System.Threading.Thread.Sleep(1000) - FileSystem.OpenFileForWriteShim(MultiProjectDirty1.fileName1).Write(MultiProjectDirty1.content) - printfn "Wrote old content to file '%s'" MultiProjectDirty1.fileName1 - let wt2b = FileSystem.GetLastWriteTimeShim MultiProjectDirty1.fileName1 + let wt1b = FileSystem.GetLastWriteTimeShim multiProjectDirty1.FileName1 + printfn "Writing old content to file '%s'" multiProjectDirty1.FileName1 + FileSystem.OpenFileForWriteShim(multiProjectDirty1.FileName1).Write(multiProjectDirty1.Content) + printfn "Wrote old content to file '%s'" multiProjectDirty1.FileName1 + let wt2b = FileSystem.GetLastWriteTimeShim multiProjectDirty1.FileName1 + Assert.NotEqual(wt1b, wt2b) printfn "Current time: '%A', ticks = %d" wt0b wt0b.Ticks printfn "Old write time: '%A', ticks = %d" wt1b wt1b.Ticks printfn "New write time: '%A', ticks = %d" wt2b wt2b.Ticks - count.Value |> shouldEqual 4 + count |> shouldEqual 4 let wholeProjectResults2AfterChange2 = checker.ParseAndCheckProject(proj2options) |> Async.RunImmediate - System.Threading.Thread.Sleep(1000) - count.Value |> shouldEqual 6 // note, causes two files to be type checked, one from each project - + count |> shouldEqual 6 // note, causes two files to be type checked, one from each project let wholeProjectResults1AfterChange2 = checker.ParseAndCheckProject(proj1options) |> Async.RunImmediate - count.Value |> shouldEqual 6 // the project is already checked + count |> shouldEqual 6 // the project is already checked let backgroundParseResults1AfterChange2, backgroundTypedParse1AfterChange2 = - checker.GetBackgroundCheckResultsForFileInProject(MultiProjectDirty1.fileName1, proj1options) + checker.GetBackgroundCheckResultsForFileInProject(multiProjectDirty1.FileName1, proj1options) |> Async.RunImmediate let xSymbolUseAfterChange2 = backgroundTypedParse1AfterChange2.GetSymbolUseAtLocation(4, 4, "", ["x"]) @@ -587,7 +586,7 @@ let ``Test multi project symbols should pick up changes in dependent projects`` let usesOfXSymbolInProject1AfterChange2 = wholeProjectResults1AfterChange2.GetUsesOfSymbol(xSymbolAfterChange2) - |> Array.map (fun su -> su.Symbol.ToString(), MultiProjectDirty1.cleanFileName su.FileName, tups su.Range) + |> Array.map (fun su -> su.Symbol.ToString(), multiProjectDirty1.CleanFileName su.FileName, tups su.Range) usesOfXSymbolInProject1AfterChange2 |> shouldEqual @@ -596,7 +595,7 @@ let ``Test multi project symbols should pick up changes in dependent projects`` let usesOfXSymbolInProject2AfterChange2 = wholeProjectResults2AfterChange2.GetUsesOfSymbol(xSymbolAfterChange2) - |> Array.map (fun su -> su.Symbol.ToString(), MultiProjectDirty2.cleanFileName su.FileName, tups su.Range) + |> Array.map (fun su -> su.Symbol.ToString(), multiProjectDirty2.CleanFileName su.FileName, tups su.Range) usesOfXSymbolInProject2AfterChange2 |> shouldEqual @@ -682,12 +681,8 @@ let v = Project2A.C().InternalMember // access an internal symbol ReferencedProjects = [| FSharpReferencedProject.FSharpReference(Project2A.dllName, Project2A.options); |] } let cleanFileName a = if a = fileName1 then "file1" else "??" -[] -[] -[] -let ``Test multi project2 errors`` useTransparentCompiler = - - let checker = if useTransparentCompiler then transparentCompilerChecker else checker +[] +let ``Test multi project2 errors`` () = let wholeProjectResults = checker.ParseAndCheckProject(Project2B.options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -700,12 +695,8 @@ let ``Test multi project2 errors`` useTransparentCompiler = wholeProjectResultsC.Diagnostics.Length |> shouldEqual 1 -[] -[] -[] -let ``Test multi project 2 all symbols`` useTransparentCompiler = - - let checker = if useTransparentCompiler then transparentCompilerChecker else checker +[] +let ``Test multi project 2 all symbols`` () = let mpA = checker.ParseAndCheckProject(Project2A.options) |> Async.RunImmediate let mpB = checker.ParseAndCheckProject(Project2B.options) |> Async.RunImmediate @@ -783,12 +774,8 @@ let fizzBuzz = function ReferencedProjects = [| FSharpReferencedProject.FSharpReference(Project3A.dllName, Project3A.options) |] } let cleanFileName a = if a = fileName1 then "file1" else "??" -[] -[] -[] -let ``Test multi project 3 whole project errors`` useTransparentCompiler = - - let checker = if useTransparentCompiler then transparentCompilerChecker else checker +[] +let ``Test multi project 3 whole project errors`` () = let wholeProjectResults = checker.ParseAndCheckProject(MultiProject3.options) |> Async.RunImmediate for e in wholeProjectResults.Diagnostics do @@ -796,12 +783,8 @@ let ``Test multi project 3 whole project errors`` useTransparentCompiler = wholeProjectResults.Diagnostics.Length |> shouldEqual 0 -[] -[] -[] -let ``Test active patterns' XmlDocSig declared in referenced projects`` useTransparentCompiler = - - let checker = if useTransparentCompiler then transparentCompilerChecker else checker +[] +let ``Test active patterns' XmlDocSig declared in referenced projects`` () = let wholeProjectResults = checker.ParseAndCheckProject(MultiProject3.options) |> Async.RunImmediate let backgroundParseResults1, backgroundTypedParse1 = @@ -830,13 +813,9 @@ let ``Test active patterns' XmlDocSig declared in referenced projects`` useTrans //------------------------------------------------------------------------------------ -[] -[] -[] -let ``In-memory cross-project references to projects using generative type provides should fallback to on-disk references`` useTransparentCompiler = - - let checker = if useTransparentCompiler then transparentCompilerChecker else checker - +[] +let ``In-memory cross-project references to projects using generative type provides should fallback to on-disk references`` () = + // The type provider and its dependency are compiled as part of the solution build #if DEBUG let csDLL = __SOURCE_DIRECTORY__ + @"/../../artifacts/bin/TestTP/Debug/netstandard2.0/CSharp_Analysis.dll" diff --git a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs index 9f86267886f..ac563c3e1ed 100644 --- a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs @@ -4639,11 +4639,9 @@ let callToOverload = B(5).Overload(4) let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, []) -[] -// [] // Flaky, reenable when stable -[] -let ``Test project36 FSharpMemberOrFunctionOrValue.IsBaseValue`` useTransparentCompiler = - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) +[] +let ``Test project36 FSharpMemberOrFunctionOrValue.IsBaseValue`` () = + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) with SourceFiles = Project36.fileNames } let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(options) @@ -4656,11 +4654,9 @@ let ``Test project36 FSharpMemberOrFunctionOrValue.IsBaseValue`` useTransparentC else None) |> fun baseSymbol -> shouldEqual true baseSymbol.IsBaseValue -[] -// [] // Flaky, reenable when stable -[] -let ``Test project36 FSharpMemberOrFunctionOrValue.IsConstructorThisValue & IsMemberThisValue`` useTransparentCompiler = - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) +[] +let ``Test project36 FSharpMemberOrFunctionOrValue.IsConstructorThisValue & IsMemberThisValue`` () = + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) with SourceFiles = Project36.fileNames } let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(options) |> Async.RunImmediate let declarations = @@ -4695,11 +4691,9 @@ let ``Test project36 FSharpMemberOrFunctionOrValue.IsConstructorThisValue & IsMe | _ -> failwith "unexpected expression" |> shouldEqual true -[] -// [] // Flaky, reenable when stable -[] -let ``Test project36 FSharpMemberOrFunctionOrValue.LiteralValue`` useTransparentCompiler = - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) +[] +let ``Test project36 FSharpMemberOrFunctionOrValue.LiteralValue`` () = + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (Project36.projFileName, Project36.args) with SourceFiles = Project36.fileNames } let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(options) |> Async.RunImmediate let project36Module = wholeProjectResults.AssemblySignature.Entities[0] @@ -5316,11 +5310,9 @@ let foo (a: Foo): bool = let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } -[] -// [] // Flaky, reenable when stable -[] -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 ``Test typed AST for struct unions`` () = // See https://github.com/fsharp/FSharp.Compiler.Service/issues/756 + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let wholeProjectResults = keepAssemblyContentsChecker.ParseAndCheckProject(ProjectStructUnions.options) |> Async.RunImmediate let declarations = @@ -5406,10 +5398,8 @@ let ``Test diagnostics with line directives ignored`` () = //------------------------------------------------------ -[] -// [] // Flaky, reenable when stable -[] -let ``ParseAndCheckFileResults contains ImplFile list if FSharpChecker is created with keepAssemblyContent flag set to true`` useTransparentCompiler = +[] +let ``ParseAndCheckFileResults contains ImplFile list if FSharpChecker is created with keepAssemblyContent flag set to true`` () = let fileName1 = Path.ChangeExtension(getTemporaryFileName (), ".fs") let base2 = getTemporaryFileName () @@ -5424,7 +5414,7 @@ type A(i:int) = let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, []) - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let fileCheckResults = @@ -5492,10 +5482,8 @@ let ``#4030, Incremental builder creation warnings 5`` () = //------------------------------------------------------ -[] -// [] // Flaky, reenable when stable -[] -let ``Unused opens in rec module smoke test 1`` useTransparentCompiler = +[] +let ``Unused opens in rec module smoke test 1`` () = let fileName1 = Path.ChangeExtension(getTemporaryFileName (), ".fs") let base2 = getTemporaryFileName () @@ -5543,7 +5531,7 @@ type UseTheThings(i:int) = let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, []) - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let fileCheckResults = @@ -5567,10 +5555,8 @@ type UseTheThings(i:int) = (((25, 5), (25, 21)), "open SomeUnusedModule")] unusedOpensData |> shouldEqual expected -[] -// [] // Flaky, reenable when stable -[] -let ``Unused opens in non rec module smoke test 1`` useTransparentCompiler = +[] +let ``Unused opens in non rec module smoke test 1`` () = let fileName1 = Path.ChangeExtension(getTemporaryFileName (), ".fs") let base2 = getTemporaryFileName () @@ -5630,7 +5616,7 @@ type UseTheThings(i:int) = let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, []) - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let fileCheckResults = @@ -5656,10 +5642,8 @@ type UseTheThings(i:int) = (((37, 10), (37, 21)), "open type FSharpEnum2 // Unused, should appear.")] unusedOpensData |> shouldEqual expected -[] -// [] // Flaky, reenable when stable -[] -let ``Unused opens smoke test auto open`` useTransparentCompiler = +[] +let ``Unused opens smoke test auto open`` () = let fileName1 = Path.ChangeExtension(getTemporaryFileName (), ".fs") let base2 = getTemporaryFileName () @@ -5715,7 +5699,7 @@ module M2 = let fileNames = [|fileName1|] let args = mkProjectCommandLineArgs (dllName, []) - let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=useTransparentCompiler) + let keepAssemblyContentsChecker = FSharpChecker.Create(keepAssemblyContents=true, useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } let fileCheckResults =