From ed3f297f40e04a830f6b673e2aae07a4058968f6 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 18 May 2021 17:20:43 -0700 Subject: [PATCH 01/17] Suppress ExecutionContext flow on SQLite hot path Fixes https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1326123 --- .../v2/SQLitePersistentStorage_Threading.cs | 23 +++++++++++-- .../Portable/Utilities/FlowControlHelper.cs | 33 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/Workspaces/Core/Portable/Utilities/FlowControlHelper.cs diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_Threading.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_Threading.cs index ff0557b384d9d..061470a34a43b 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_Threading.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_Threading.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SQLite.v2 { @@ -28,12 +29,30 @@ private static async Task PerformTaskAsync( // Read tasks go to the concurrent-scheduler where they can run concurrently with other read // tasks. private Task PerformReadAsync(Func func, TArg arg, CancellationToken cancellationToken) where TArg : struct - => PerformTaskAsync(func, arg, _connectionPoolService.Scheduler.ConcurrentScheduler, cancellationToken); + { + // Suppress ExecutionContext flow for asynchronous operations that write to the database. In addition to + // avoiding ExecutionContext allocations, this clears the LogicalCallContext and avoids the need to clone + // data set by CallContext.LogicalSetData at each yielding await in the task tree. + // + // ⚠ DO NOT AWAIT INSIDE THE USING. The Dispose method that restores ExecutionContext flow must run on the + // same thread where SuppressFlow was originally run. + using var _ = FlowControlHelper.TrySuppressFlow(); + return PerformTaskAsync(func, arg, _connectionPoolService.Scheduler.ConcurrentScheduler, cancellationToken); + } // Write tasks go to the exclusive-scheduler so they run exclusively of all other threading // tasks we need to do. public Task PerformWriteAsync(Func func, TArg arg, CancellationToken cancellationToken) where TArg : struct - => PerformTaskAsync(func, arg, _connectionPoolService.Scheduler.ExclusiveScheduler, cancellationToken); + { + // Suppress ExecutionContext flow for asynchronous operations that write to the database. In addition to + // avoiding ExecutionContext allocations, this clears the LogicalCallContext and avoids the need to clone + // data set by CallContext.LogicalSetData at each yielding await in the task tree. + // + // ⚠ DO NOT AWAIT INSIDE THE USING. The Dispose method that restores ExecutionContext flow must run on the + // same thread where SuppressFlow was originally run. + using var _ = FlowControlHelper.TrySuppressFlow(); + return PerformTaskAsync(func, arg, _connectionPoolService.Scheduler.ExclusiveScheduler, cancellationToken); + } public Task PerformWriteAsync(Action action, CancellationToken cancellationToken) => PerformWriteAsync(vt => diff --git a/src/Workspaces/Core/Portable/Utilities/FlowControlHelper.cs b/src/Workspaces/Core/Portable/Utilities/FlowControlHelper.cs new file mode 100644 index 0000000000000..686c87f456b96 --- /dev/null +++ b/src/Workspaces/Core/Portable/Utilities/FlowControlHelper.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; + +namespace Roslyn.Utilities +{ + internal static class FlowControlHelper + { + public static AsyncFlowControlHelper TrySuppressFlow() + => new(ExecutionContext.IsFlowSuppressed() ? default : ExecutionContext.SuppressFlow()); + + public struct AsyncFlowControlHelper : IDisposable + { + private readonly AsyncFlowControl _asyncFlowControl; + + public AsyncFlowControlHelper(AsyncFlowControl asyncFlowControl) + { + _asyncFlowControl = asyncFlowControl; + } + + public void Dispose() + { + if (_asyncFlowControl != default) + { + _asyncFlowControl.Dispose(); + } + } + } + } +} From f956d76875a3d3cfd06a61dbb817855b7c5115e1 Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Fri, 21 May 2021 14:10:34 -0700 Subject: [PATCH 02/17] enables auto-attach to servicehub processes --- src/Deployment/Properties/launchSettings.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Deployment/Properties/launchSettings.json b/src/Deployment/Properties/launchSettings.json index 01d32bd540287..a565a742c27e1 100644 --- a/src/Deployment/Properties/launchSettings.json +++ b/src/Deployment/Properties/launchSettings.json @@ -3,12 +3,18 @@ "Visual Studio Extension": { "commandName": "Executable", "executablePath": "$(DevEnvDir)devenv.exe", - "commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log" + "commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log", + "environmentVariables": { + "SERVICEHUBDEBUGHOSTONSTARTUP": "All" + } }, "Visual Studio Extension (with Native Debugging)": { "commandName": "Executable", "executablePath": "$(DevEnvDir)devenv.exe", "commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log", + "environmentVariables": { + "SERVICEHUBDEBUGHOSTONSTARTUP": "All" + }, "nativeDebugging": true } } From 1f6d15e5aef58b7db77c934e358e417d68dc1f8a Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Fri, 21 May 2021 14:39:05 -0700 Subject: [PATCH 03/17] have service hub debugging be its own profile --- src/Deployment/Properties/launchSettings.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Deployment/Properties/launchSettings.json b/src/Deployment/Properties/launchSettings.json index a565a742c27e1..570c6c4af71fb 100644 --- a/src/Deployment/Properties/launchSettings.json +++ b/src/Deployment/Properties/launchSettings.json @@ -1,6 +1,11 @@ { "profiles": { "Visual Studio Extension": { + "commandName": "Executable", + "executablePath": "$(DevEnvDir)devenv.exe", + "commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log" + }, + "Visual Studio Extension (with ServiceHub Debugging)": { "commandName": "Executable", "executablePath": "$(DevEnvDir)devenv.exe", "commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log", @@ -12,9 +17,6 @@ "commandName": "Executable", "executablePath": "$(DevEnvDir)devenv.exe", "commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log", - "environmentVariables": { - "SERVICEHUBDEBUGHOSTONSTARTUP": "All" - }, "nativeDebugging": true } } From 1f902ca3398adf50b4964adf5648272ea398f1fd Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Thu, 27 May 2021 12:38:34 -0700 Subject: [PATCH 04/17] Don't validate workspace invariants in release builds This method appears in some performance traces when you have lots of workspace operations being done. The validations are useful but mostly would catch an error early during development, so there's no reason to run these except in DEBUG. --- .../Core/Portable/Workspace/Solution/SolutionState.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 0b2fdc942ef0b..fcd5eab374c23 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -190,7 +190,8 @@ public SolutionState WithNewWorkspace(Workspace workspace, int workspaceVersion) /// public IReadOnlyList ProjectIds { get; } - // [Conditional("DEBUG")] + // Only run this in debug builds; even the .Any() call across all projects can be expensive when there's a lot of them. + [Conditional("DEBUG")] private void CheckInvariants() { Contract.ThrowIfFalse(_projectIdToProjectStateMap.Count == ProjectIds.Count); From 65808616e18965265178dc1dcf9350abfa0dcc7b Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sun, 30 May 2021 13:34:34 +0000 Subject: [PATCH 05/17] Update dependencies from https://github.com/dotnet/arcade build 20210528.1 (#53780) [main] Update dependencies from dotnet/arcade --- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9ba50a1064826..edb415d07ed59 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,18 +13,18 @@ - + https://github.com/dotnet/arcade - c3e0e4070f6cd3873b775e3f4136e29f4d66cb49 + b5ca0997b26992dcc4e55ec3d87d71000be295ce https://github.com/dotnet/roslyn 5b972bceb846f5d15f991a479e285067a75103e4 - + https://github.com/dotnet/arcade - c3e0e4070f6cd3873b775e3f4136e29f4d66cb49 + b5ca0997b26992dcc4e55ec3d87d71000be295ce diff --git a/global.json b/global.json index 8355bc0c4e997..e26c60ad2e517 100644 --- a/global.json +++ b/global.json @@ -12,7 +12,7 @@ "xcopy-msbuild": "16.8.0-preview2.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21277.1", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21277.1" + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21278.1", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21278.1" } } From 879ef604441c507e784e79e0d858e3780a760bdd Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 31 May 2021 10:53:56 -0700 Subject: [PATCH 06/17] Do not persist diagnostics just to purge them from memory --- .../DiagnosticAnalyzerServiceTests.cs | 2 +- .../DiagnosticDataSerializerTests.cs | 4 + ...ticAnalyzerService_BuildSynchronization.cs | 6 +- ...gnosticIncrementalAnalyzer.AnalysisData.cs | 4 +- .../DiagnosticIncrementalAnalyzer.Executor.cs | 2 +- ...gnosticIncrementalAnalyzer.ProjectState.cs | 110 ++++++++---------- ...gnosticIncrementalAnalyzer.StateManager.cs | 12 +- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 8 +- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 4 +- ...ncrementalAnalyzer_BuildSynchronization.cs | 12 +- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 8 +- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 9 +- .../ExternalErrorDiagnosticUpdateSource.cs | 44 +++---- .../Diagnostics/DiagnosticDataSerializer.cs | 4 + 14 files changed, 104 insertions(+), 125 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index f28edd0623202..a9835b0a83577 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -367,7 +367,7 @@ public async Task TestSynchronizeWithBuild() // cause analysis var location = Location.Create(document.FilePath, textSpan: default, lineSpan: default); - await service.SynchronizeWithBuildAsync( + service.SynchronizeWithBuild( workspace, ImmutableDictionary>.Empty.Add( document.Project.Id, diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticDataSerializerTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticDataSerializerTests.cs index 6002a22d63a53..a433266053f4e 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticDataSerializerTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticDataSerializerTests.cs @@ -4,6 +4,8 @@ #nullable disable +#if false + using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -362,3 +364,5 @@ public ValueTask DisposeAsync() } } } + +#endif diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_BuildSynchronization.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_BuildSynchronization.cs index 387dda2d9bc76..a3e89e47a299d 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_BuildSynchronization.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_BuildSynchronization.cs @@ -16,7 +16,7 @@ internal partial class DiagnosticAnalyzerService /// /// Synchronize build errors with live error. /// - public Task SynchronizeWithBuildAsync( + public void SynchronizeWithBuild( Workspace workspace, ImmutableDictionary> diagnostics, @@ -26,10 +26,8 @@ public Task SynchronizeWithBuildAsync( { if (_map.TryGetValue(workspace, out var analyzer)) { - return analyzer.SynchronizeWithBuildAsync(diagnostics, postBuildAndErrorListRefreshTaskQueue, onBuildCompleted, cancellationToken); + analyzer.SynchronizeWithBuild(diagnostics, postBuildAndErrorListRefreshTaskQueue, onBuildCompleted, cancellationToken); } - - return Task.CompletedTask; } } } diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index 8db109536a919..d9c11caf65950 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -109,7 +109,7 @@ public ProjectAnalysisData( public DiagnosticAnalysisResult GetResult(DiagnosticAnalyzer analyzer) => GetResultOrEmpty(Result, analyzer, ProjectId, Version); - public static async Task CreateAsync(IPersistentStorageService persistentService, Project project, IEnumerable stateSets, bool avoidLoadingData, CancellationToken cancellationToken) + public static async Task CreateAsync(Project project, IEnumerable stateSets, bool avoidLoadingData, CancellationToken cancellationToken) { VersionStamp? version = null; @@ -117,7 +117,7 @@ public static async Task CreateAsync(IPersistentStorageServ foreach (var stateSet in stateSets) { var state = stateSet.GetOrCreateProjectState(project.Id); - var result = await state.GetAnalysisDataAsync(persistentService, project, avoidLoadingData, cancellationToken).ConfigureAwait(false); + var result = await state.GetAnalysisDataAsync(project, avoidLoadingData, cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(project.Id == result.ProjectId); if (!version.HasValue) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index ca002cf383572..f12d8156595e4 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -102,7 +102,7 @@ private async Task GetProjectAnalysisDataAsync( // PERF: We need to flip this to false when we do actual diffing. var avoidLoadingData = true; var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - var existingData = await ProjectAnalysisData.CreateAsync(PersistentStorageService, project, stateSets, avoidLoadingData, cancellationToken).ConfigureAwait(false); + var existingData = await ProjectAnalysisData.CreateAsync(project, stateSets, avoidLoadingData, cancellationToken).ConfigureAwait(false); // We can't return here if we have open file only analyzers since saved data for open file only analyzer // is incomplete -- it only contains info on open files rather than whole project. diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 4447e2cad8116..83f967e627a74 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -47,7 +47,7 @@ public bool IsEmpty(DocumentId documentId) /// /// Return all diagnostics for the given project stored in this state /// - public async Task GetAnalysisDataAsync(IPersistentStorageService persistentService, Project project, bool avoidLoadingData, CancellationToken cancellationToken) + public async Task GetAnalysisDataAsync(Project project, bool avoidLoadingData, CancellationToken cancellationToken) { // make a copy of last result. var lastResult = _lastResult; @@ -55,7 +55,7 @@ public async Task GetAnalysisDataAsync(IPersistentStor if (lastResult.IsDefault) { - return await LoadInitialAnalysisDataAsync(persistentService, project, cancellationToken).ConfigureAwait(false); + return await LoadInitialAnalysisDataAsync(project, cancellationToken).ConfigureAwait(false); } RoslynDebug.Assert(lastResult.DocumentIds != null); @@ -75,7 +75,7 @@ public async Task GetAnalysisDataAsync(IPersistentStor } // loading data can be cancelled any time. - var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, lastResult.Version); + var serializerVersion = lastResult.Version; var builder = new Builder(project, lastResult.Version, lastResult.DocumentIds); foreach (var documentId in lastResult.DocumentIds) @@ -88,7 +88,7 @@ public async Task GetAnalysisDataAsync(IPersistentStor continue; } - if (!await TryDeserializeDocumentDiagnosticsAsync(persistentService, serializer, document, builder, cancellationToken).ConfigureAwait(false)) + if (!TryDeserializeDocumentDiagnostics(serializerVersion, document, builder)) { Debug.Assert(lastResult.Version == VersionStamp.Default); @@ -98,7 +98,7 @@ public async Task GetAnalysisDataAsync(IPersistentStor } } - if (!await TryDeserializeProjectDiagnosticsAsync(persistentService, serializer, project, builder, cancellationToken).ConfigureAwait(false)) + if (!TryDeserializeProjectDiagnostics(serializerVersion, project, builder)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. @@ -110,7 +110,7 @@ public async Task GetAnalysisDataAsync(IPersistentStor /// /// Return all diagnostics for the given document stored in this state including non local diagnostics for this document /// - public async Task GetAnalysisDataAsync(IPersistentStorageService persistentService, TextDocument document, bool avoidLoadingData, CancellationToken cancellationToken) + public async Task GetAnalysisDataAsync(TextDocument document, bool avoidLoadingData, CancellationToken cancellationToken) { // make a copy of last result. var lastResult = _lastResult; @@ -118,7 +118,7 @@ public async Task GetAnalysisDataAsync(IPersistentStor if (lastResult.IsDefault) { - return await LoadInitialAnalysisDataAsync(persistentService, document, cancellationToken).ConfigureAwait(false); + return await LoadInitialAnalysisDataAsync(document, cancellationToken).ConfigureAwait(false); } var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); @@ -134,10 +134,10 @@ public async Task GetAnalysisDataAsync(IPersistentStor } // loading data can be cancelled any time. - var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, lastResult.Version); + var serializerVersion = lastResult.Version; var builder = new Builder(document.Project, lastResult.Version); - if (!await TryDeserializeDocumentDiagnosticsAsync(persistentService, serializer, document, builder, cancellationToken).ConfigureAwait(false)) + if (!TryDeserializeDocumentDiagnostics(serializerVersion, document, builder)) { Debug.Assert(lastResult.Version == VersionStamp.Default); @@ -151,7 +151,7 @@ public async Task GetAnalysisDataAsync(IPersistentStor /// /// Return all no location diagnostics for the given project stored in this state /// - public async Task GetProjectAnalysisDataAsync(IPersistentStorageService persistentService, Project project, bool avoidLoadingData, CancellationToken cancellationToken) + public async Task GetProjectAnalysisDataAsync(Project project, bool avoidLoadingData, CancellationToken cancellationToken) { // make a copy of last result. var lastResult = _lastResult; @@ -159,7 +159,7 @@ public async Task GetProjectAnalysisDataAsync(IPersist if (lastResult.IsDefault) { - return await LoadInitialProjectAnalysisDataAsync(persistentService, project, cancellationToken).ConfigureAwait(false); + return await LoadInitialProjectAnalysisDataAsync(project, cancellationToken).ConfigureAwait(false); } var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); @@ -175,10 +175,10 @@ public async Task GetProjectAnalysisDataAsync(IPersist } // loading data can be cancelled any time. - var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, lastResult.Version); + var serializerVersion = lastResult.Version; var builder = new Builder(project, lastResult.Version); - if (!await TryDeserializeProjectDiagnosticsAsync(persistentService, serializer, project, builder, cancellationToken).ConfigureAwait(false)) + if (!TryDeserializeProjectDiagnostics(serializerVersion, project, builder)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. @@ -187,13 +187,10 @@ public async Task GetProjectAnalysisDataAsync(IPersist return builder.ToResult(); } - public Task SaveAsync(IPersistentStorageService persistentService, Project project, DiagnosticAnalysisResult result) - => SaveCoreAsync(project, result, persistentService); + public void SaveInMemoryCache(Project project, DiagnosticAnalysisResult result) + => SaveCore(project, result); - public Task SaveInMemoryCacheAsync(Project project, DiagnosticAnalysisResult result) - => SaveCoreAsync(project, result, persistentService: null); - - private async Task SaveCoreAsync(Project project, DiagnosticAnalysisResult result, IPersistentStorageService? persistentService) + private void SaveCore(Project project, DiagnosticAnalysisResult result) { Contract.ThrowIfTrue(result.IsAggregatedForm); Contract.ThrowIfNull(result.DocumentIds); @@ -204,7 +201,7 @@ private async Task SaveCoreAsync(Project project, DiagnosticAnalysisResult resul _lastResult = result.ToAggregatedForm(); // serialization can't be cancelled. - var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, result.Version); + var serializerVersion = result.Version; foreach (var documentId in result.DocumentIds) { var document = project.GetTextDocument(documentId); @@ -220,12 +217,12 @@ private async Task SaveCoreAsync(Project project, DiagnosticAnalysisResult resul continue; } - await SerializeAsync(persistentService, serializer, project, document, document.Id, _owner.SyntaxStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax)).ConfigureAwait(false); - await SerializeAsync(persistentService, serializer, project, document, document.Id, _owner.SemanticStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic)).ConfigureAwait(false); - await SerializeAsync(persistentService, serializer, project, document, document.Id, _owner.NonLocalStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.NonLocal)).ConfigureAwait(false); + Serialize(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax)); + Serialize(serializerVersion, project, document, document.Id, _owner.SemanticStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic)); + Serialize(serializerVersion, project, document, document.Id, _owner.NonLocalStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.NonLocal)); } - await SerializeAsync(persistentService, serializer, project, document: null, result.ProjectId, _owner.NonLocalStateName, result.GetOtherDiagnostics()).ConfigureAwait(false); + Serialize(serializerVersion, project, document: null, result.ProjectId, _owner.NonLocalStateName, result.GetOtherDiagnostics()); } public void ResetVersion() @@ -234,7 +231,7 @@ public void ResetVersion() _lastResult = _lastResult.Reset(); } - public async Task MergeAsync(IPersistentStorageService persistentService, ActiveFileState state, TextDocument document) + public async Task MergeAsync(ActiveFileState state, TextDocument document) { Contract.ThrowIfFalse(state.DocumentId == document.Id); @@ -270,11 +267,11 @@ public async Task MergeAsync(IPersistentStorageService persistentService, Active var version = VersionStamp.Default; // serialization can't be cancelled. - var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version); + var serializerVersion = version; // save active file diagnostics back to project state - await SerializeAsync(persistentService, serializer, project, document, document.Id, _owner.SyntaxStateName, syntax.Items).ConfigureAwait(false); - await SerializeAsync(persistentService, serializer, project, document, document.Id, _owner.SemanticStateName, semantic.Items).ConfigureAwait(false); + Serialize(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, syntax.Items); + Serialize(serializerVersion, project, document, document.Id, _owner.SemanticStateName, semantic.Items); // save last aggregated form of analysis result _lastResult = _lastResult.UpdateAggregatedResult(version, state.DocumentId, fromBuild); @@ -292,24 +289,24 @@ public bool OnProjectRemoved(ProjectId id) return !IsEmpty(); } - private async Task LoadInitialAnalysisDataAsync(IPersistentStorageService persistentService, Project project, CancellationToken cancellationToken) + private async Task LoadInitialAnalysisDataAsync(Project project, CancellationToken cancellationToken) { // loading data can be cancelled any time. var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version); + var serializerVersion = version; var builder = new Builder(project, version); foreach (var document in project.Documents) { cancellationToken.ThrowIfCancellationRequested(); - if (!await TryDeserializeDocumentDiagnosticsAsync(persistentService, serializer, document, builder, cancellationToken).ConfigureAwait(false)) + if (!TryDeserializeDocumentDiagnostics(serializerVersion, document, builder)) { continue; } } - if (!await TryDeserializeProjectDiagnosticsAsync(persistentService, serializer, project, builder, cancellationToken).ConfigureAwait(false)) + if (!TryDeserializeProjectDiagnostics(serializerVersion, project, builder)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -317,16 +314,16 @@ private async Task LoadInitialAnalysisDataAsync(IPersi return builder.ToResult(); } - private async Task LoadInitialAnalysisDataAsync(IPersistentStorageService persistentService, TextDocument document, CancellationToken cancellationToken) + private async Task LoadInitialAnalysisDataAsync(TextDocument document, CancellationToken cancellationToken) { // loading data can be cancelled any time. var project = document.Project; var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version); + var serializerVersion = version; var builder = new Builder(project, version); - if (!await TryDeserializeDocumentDiagnosticsAsync(persistentService, serializer, document, builder, cancellationToken).ConfigureAwait(false)) + if (!TryDeserializeDocumentDiagnostics(serializerVersion, document, builder)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -334,14 +331,14 @@ private async Task LoadInitialAnalysisDataAsync(IPersi return builder.ToResult(); } - private async Task LoadInitialProjectAnalysisDataAsync(IPersistentStorageService persistentService, Project project, CancellationToken cancellationToken) + private async Task LoadInitialProjectAnalysisDataAsync(Project project, CancellationToken cancellationToken) { // loading data can be cancelled any time. var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version); + var serializerVersion = version; var builder = new Builder(project, version); - if (!await TryDeserializeProjectDiagnosticsAsync(persistentService, serializer, project, builder, cancellationToken).ConfigureAwait(false)) + if (!TryDeserializeProjectDiagnostics(serializerVersion, project, builder)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -349,30 +346,21 @@ private async Task LoadInitialProjectAnalysisDataAsync return builder.ToResult(); } - private async Task SerializeAsync(IPersistentStorageService? persistentService, DiagnosticDataSerializer serializer, Project project, TextDocument? document, object key, string stateKey, ImmutableArray diagnostics) + private void Serialize(VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey, ImmutableArray diagnostics) { Contract.ThrowIfFalse(document == null || document.Project == project); - // try to serialize it - if (persistentService != null && - await serializer.SerializeAsync(persistentService, project, document, stateKey, diagnostics, CancellationToken.None).ConfigureAwait(false)) - { - // we succeeded saving it to persistent storage. remove it from in memory cache if it exists - RemoveInMemoryCacheEntry(key, stateKey); - return; - } - // if serialization fail, hold it in the memory - InMemoryStorage.Cache(_owner.Analyzer, (key, stateKey), new CacheEntry(serializer.Version, diagnostics)); + InMemoryStorage.Cache(_owner.Analyzer, (key, stateKey), new CacheEntry(serializerVersion, diagnostics)); } - private async Task TryDeserializeDocumentDiagnosticsAsync(IPersistentStorageService persistentService, DiagnosticDataSerializer serializer, TextDocument document, Builder builder, CancellationToken cancellationToken) + private bool TryDeserializeDocumentDiagnostics(VersionStamp serializerVersion, TextDocument document, Builder builder) { var success = true; var project = document.Project; var documentId = document.Id; - var diagnostics = await DeserializeDiagnosticsAsync(persistentService, serializer, project, document, documentId, _owner.SyntaxStateName, cancellationToken).ConfigureAwait(false); + var diagnostics = DeserializeDiagnostics(serializerVersion, project, document, documentId, _owner.SyntaxStateName); if (!diagnostics.IsDefault) { builder.AddSyntaxLocals(documentId, diagnostics); @@ -382,7 +370,7 @@ private async Task TryDeserializeDocumentDiagnosticsAsync(IPersistentStora success = false; } - diagnostics = await DeserializeDiagnosticsAsync(persistentService, serializer, project, document, documentId, _owner.SemanticStateName, cancellationToken).ConfigureAwait(false); + diagnostics = DeserializeDiagnostics(serializerVersion, project, document, documentId, _owner.SemanticStateName); if (!diagnostics.IsDefault) { builder.AddSemanticLocals(documentId, diagnostics); @@ -392,7 +380,7 @@ private async Task TryDeserializeDocumentDiagnosticsAsync(IPersistentStora success = false; } - diagnostics = await DeserializeDiagnosticsAsync(persistentService, serializer, project, document, documentId, _owner.NonLocalStateName, cancellationToken).ConfigureAwait(false); + diagnostics = DeserializeDiagnostics(serializerVersion, project, document, documentId, _owner.NonLocalStateName); if (!diagnostics.IsDefault) { builder.AddNonLocals(documentId, diagnostics); @@ -405,9 +393,9 @@ private async Task TryDeserializeDocumentDiagnosticsAsync(IPersistentStora return success; } - private async Task TryDeserializeProjectDiagnosticsAsync(IPersistentStorageService persistentService, DiagnosticDataSerializer serializer, Project project, Builder builder, CancellationToken cancellationToken) + private bool TryDeserializeProjectDiagnostics(VersionStamp serializerVersion, Project project, Builder builder) { - var diagnostics = await DeserializeDiagnosticsAsync(persistentService, serializer, project, document: null, project.Id, _owner.NonLocalStateName, cancellationToken).ConfigureAwait(false); + var diagnostics = DeserializeDiagnostics(serializerVersion, project, document: null, project.Id, _owner.NonLocalStateName); if (!diagnostics.IsDefault) { builder.AddOthers(diagnostics); @@ -417,16 +405,14 @@ private async Task TryDeserializeProjectDiagnosticsAsync(IPersistentStorag return false; } - private ValueTask> DeserializeDiagnosticsAsync(IPersistentStorageService persistentService, DiagnosticDataSerializer serializer, Project project, TextDocument? document, object key, string stateKey, CancellationToken cancellationToken) + private ImmutableArray DeserializeDiagnostics( + VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey) { Contract.ThrowIfFalse(document == null || document.Project == project); - if (InMemoryStorage.TryGetValue(_owner.Analyzer, (key, stateKey), out var entry) && serializer.Version == entry.Version) - { - return ValueTaskFactory.FromResult(entry.Diagnostics); - } - - return serializer.DeserializeAsync(persistentService, project, document, stateKey, cancellationToken); + return InMemoryStorage.TryGetValue(_owner.Analyzer, (key, stateKey), out var entry) && serializerVersion == entry.Version + ? entry.Diagnostics + : default; } private void RemoveInMemoryCache(DiagnosticAnalysisResult lastResult) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index a20a81d9b4241..b1b073227bdd1 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -21,7 +21,6 @@ internal partial class DiagnosticIncrementalAnalyzer /// private partial class StateManager { - private readonly IPersistentStorageService _persistentStorageService; private readonly DiagnosticAnalyzerInfoCache _analyzerInfoCache; /// @@ -40,9 +39,8 @@ private partial class StateManager /// public event EventHandler? ProjectAnalyzerReferenceChanged; - public StateManager(IPersistentStorageService persistentStorageService, DiagnosticAnalyzerInfoCache analyzerInfoCache) + public StateManager(DiagnosticAnalyzerInfoCache analyzerInfoCache) { - _persistentStorageService = persistentStorageService; _analyzerInfoCache = analyzerInfoCache; _hostAnalyzerStateMap = ImmutableDictionary.Empty; @@ -202,25 +200,25 @@ public static bool OnDocumentReset(IEnumerable stateSets, TextDocument return removed; } - public async Task OnDocumentOpenedAsync(IEnumerable stateSets, TextDocument document) + public static async Task OnDocumentOpenedAsync(IEnumerable stateSets, TextDocument document) { // can not be cancelled var opened = false; foreach (var stateSet in stateSets) { - opened |= await stateSet.OnDocumentOpenedAsync(_persistentStorageService, document).ConfigureAwait(false); + opened |= await stateSet.OnDocumentOpenedAsync(document).ConfigureAwait(false); } return opened; } - public async Task OnDocumentClosedAsync(IEnumerable stateSets, TextDocument document) + public static async Task OnDocumentClosedAsync(IEnumerable stateSets, TextDocument document) { // can not be cancelled var removed = false; foreach (var stateSet in stateSets) { - removed |= await stateSet.OnDocumentClosedAsync(_persistentStorageService, document).ConfigureAwait(false); + removed |= await stateSet.OnDocumentClosedAsync(document).ConfigureAwait(false); } return removed; diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index ef05f8fe17921..8819d9588d544 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -132,7 +132,7 @@ public ActiveFileState GetOrCreateActiveFileState(DocumentId documentId) public ProjectState GetOrCreateProjectState(ProjectId projectId) => _projectStates.GetOrAdd(projectId, id => new ProjectState(this, id)); - public async Task OnDocumentOpenedAsync(IPersistentStorageService persistentStorageService, TextDocument document) + public async Task OnDocumentOpenedAsync(TextDocument document) { // can not be cancelled if (!TryGetProjectState(document.Project.Id, out var projectState) || @@ -142,7 +142,7 @@ public async Task OnDocumentOpenedAsync(IPersistentStorageService persiste return false; } - var result = await projectState.GetAnalysisDataAsync(persistentStorageService, document, avoidLoadingData: false, CancellationToken.None).ConfigureAwait(false); + var result = await projectState.GetAnalysisDataAsync(document, avoidLoadingData: false, CancellationToken.None).ConfigureAwait(false); // store analysis result to active file state: var activeFileState = GetOrCreateActiveFileState(document.Id); @@ -153,7 +153,7 @@ public async Task OnDocumentOpenedAsync(IPersistentStorageService persiste return true; } - public async Task OnDocumentClosedAsync(IPersistentStorageService persistentStorageService, TextDocument document) + public async Task OnDocumentClosedAsync(TextDocument document) { // can not be cancelled // remove active file state and put it in project state @@ -164,7 +164,7 @@ public async Task OnDocumentClosedAsync(IPersistentStorageService persiste // active file exist, put it in the project state var projectState = GetOrCreateProjectState(document.Project.Id); - await projectState.MergeAsync(persistentStorageService, activeFileState, document).ConfigureAwait(false); + await projectState.MergeAsync(activeFileState, document).ConfigureAwait(false); return true; } diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 71198eb522c26..1366c6d094351 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -38,7 +38,6 @@ internal partial class DiagnosticIncrementalAnalyzer : IIncrementalAnalyzer2 internal DiagnosticAnalyzerService AnalyzerService { get; } internal Workspace Workspace { get; } - internal IPersistentStorageService PersistentStorageService { get; } [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] public DiagnosticIncrementalAnalyzer( @@ -51,12 +50,11 @@ public DiagnosticIncrementalAnalyzer( AnalyzerService = analyzerService; Workspace = workspace; - PersistentStorageService = workspace.Services.GetRequiredService(); _documentTrackingService = workspace.Services.GetRequiredService(); _correlationId = correlationId; - _stateManager = new StateManager(PersistentStorageService, analyzerInfoCache); + _stateManager = new StateManager(analyzerInfoCache); _stateManager.ProjectAnalyzerReferenceChanged += OnProjectAnalyzerReferenceChanged; _telemetry = new DiagnosticAnalyzerTelemetry(); diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs index 91c5cc2eb3392..46635cc7d4496 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer { - public async Task SynchronizeWithBuildAsync( + public void SynchronizeWithBuild( ImmutableDictionary> buildDiagnostics, TaskQueue postBuildAndErrorListRefreshTaskQueue, @@ -66,13 +66,7 @@ public async Task SynchronizeWithBuildAsync( var result = GetResultOrEmpty(newResult, stateSet.Analyzer, project.Id, VersionStamp.Default); // Save into in-memory cache. - await state.SaveInMemoryCacheAsync(project, result).ConfigureAwait(false); - - // Save into persistent storage on a separate post-build and post error list refresh task queue. - _ = postBuildAndErrorListRefreshTaskQueue.ScheduleTask( - nameof(SynchronizeWithBuildAsync), - () => state.SaveAsync(PersistentStorageService, project, result), - cancellationToken); + state.SaveInMemoryCache(project, result); } // Raise diagnostic updated events after the new diagnostics have been stored into the in-memory cache. @@ -97,7 +91,7 @@ public async Task SynchronizeWithBuildAsync( // Enqueue remaining re-analysis with normal priority on a separate task queue // that will execute at the end of all the post build and error list refresh tasks. - _ = postBuildAndErrorListRefreshTaskQueue.ScheduleTask(nameof(SynchronizeWithBuildAsync), () => + _ = postBuildAndErrorListRefreshTaskQueue.ScheduleTask(nameof(SynchronizeWithBuild), () => { // Enqueue re-analysis of open documents. AnalyzerService.Reanalyze(Workspace, documentIds: Workspace.GetOpenDocumentIds()); diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index c9632f514a68d..7ede412128fb9 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -179,7 +179,7 @@ public async Task> GetSpecificDiagnosticsAsync(Di return IncludeSuppressedDiagnostics ? diagnostics : diagnostics.WhereAsArray(d => !d.IsSuppressed); } - private async Task> GetDiagnosticsAsync(StateSet stateSet, Project project, DocumentId? documentId, AnalysisKind kind, CancellationToken cancellationToken) + private static async Task> GetDiagnosticsAsync(StateSet stateSet, Project project, DocumentId? documentId, AnalysisKind kind, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -193,7 +193,7 @@ private async Task> GetDiagnosticsAsync(StateSet return await GetProjectStateDiagnosticsAsync(stateSet, project, documentId, kind, cancellationToken).ConfigureAwait(false); } - private async Task> GetProjectStateDiagnosticsAsync(StateSet stateSet, Project project, DocumentId? documentId, AnalysisKind kind, CancellationToken cancellationToken) + private static async Task> GetProjectStateDiagnosticsAsync(StateSet stateSet, Project project, DocumentId? documentId, AnalysisKind kind, CancellationToken cancellationToken) { if (!stateSet.TryGetProjectState(project.Id, out var state)) { @@ -210,12 +210,12 @@ private async Task> GetProjectStateDiagnosticsAsy return ImmutableArray.Empty; } - var result = await state.GetAnalysisDataAsync(Owner.PersistentStorageService, document, avoidLoadingData: false, cancellationToken: cancellationToken).ConfigureAwait(false); + var result = await state.GetAnalysisDataAsync(document, avoidLoadingData: false, cancellationToken).ConfigureAwait(false); return result.GetDocumentDiagnostics(documentId, kind); } Contract.ThrowIfFalse(kind == AnalysisKind.NonLocal); - var nonLocalResult = await state.GetProjectAnalysisDataAsync(Owner.PersistentStorageService, project, avoidLoadingData: false, cancellationToken: cancellationToken).ConfigureAwait(false); + var nonLocalResult = await state.GetProjectAnalysisDataAsync(project, avoidLoadingData: false, cancellationToken: cancellationToken).ConfigureAwait(false); return nonLocalResult.GetOtherDiagnostics(); } } diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 2b9812266dfa6..07278b3322050 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -8,15 +8,12 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics.Telemetry; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Options; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; @@ -158,7 +155,7 @@ private async Task AnalyzeProjectAsync(Project project, bool forceAnalyzerRun, C { var state = stateSet.GetOrCreateProjectState(project.Id); - await state.SaveAsync(PersistentStorageService, project, result.GetResult(stateSet.Analyzer)).ConfigureAwait(false); + state.SaveInMemoryCache(project, result.GetResult(stateSet.Analyzer)); } RaiseProjectDiagnosticsIfNeeded(project, stateSets, result.OldResult, result.Result); @@ -183,7 +180,7 @@ private async Task TextDocumentOpenAsync(TextDocument document, CancellationToke // let other component knows about this event ClearCompilationsWithAnalyzersCache(); - await _stateManager.OnDocumentOpenedAsync(stateSets, document).ConfigureAwait(false); + await StateManager.OnDocumentOpenedAsync(stateSets, document).ConfigureAwait(false); } } @@ -201,7 +198,7 @@ private async Task TextDocumentCloseAsync(TextDocument document, CancellationTok // let other components knows about this event ClearCompilationsWithAnalyzersCache(); - var documentHadDiagnostics = await _stateManager.OnDocumentClosedAsync(stateSets, document).ConfigureAwait(false); + var documentHadDiagnostics = await StateManager.OnDocumentClosedAsync(stateSets, document).ConfigureAwait(false); RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(document, stateSets, documentHadDiagnostics); } } diff --git a/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs index 4a5452e089c6e..43752426181eb 100644 --- a/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs @@ -52,7 +52,7 @@ internal sealed class ExternalErrorDiagnosticUpdateSource : IDiagnosticUpdateSou /// /// Task queue to serialize all the post-build and post error list refresh tasks. /// Error list refresh requires build/live diagnostics de-duping to complete, which happens during - /// . + /// . /// Computationally expensive tasks such as writing build errors into persistent storage, /// invoking background analysis on open files/solution after build completes, etc. /// are added to this task queue to help ensure faster error list refresh. @@ -168,13 +168,13 @@ public void ClearErrors(ProjectId projectId) // Update the state to clear diagnostics and raise corresponding diagnostic updated events // on a serialized task queue. - _taskQueue.ScheduleTask(nameof(ClearErrors), async () => + _taskQueue.ScheduleTask(nameof(ClearErrors), () => { if (state == null) { // TODO: Is it possible that ClearErrors can be invoked while the build is not in progress? // We fallback to current solution in the workspace and clear errors for the project. - await ClearErrorsCoreAsync(projectId, _workspace.CurrentSolution, state).ConfigureAwait(false); + ClearErrorsCore(projectId, _workspace.CurrentSolution, state); } else { @@ -191,7 +191,7 @@ public void ClearErrors(ProjectId projectId) var solution = state.Solution; - await ClearErrorsCoreAsync(projectId, solution, state).ConfigureAwait(false); + ClearErrorsCore(projectId, solution, state); var transitiveProjectIds = solution.GetProjectDependencyGraph().GetProjectsThatTransitivelyDependOnThisProject(projectId); foreach (var projectId in transitiveProjectIds) @@ -201,14 +201,14 @@ public void ClearErrors(ProjectId projectId) continue; } - await ClearErrorsCoreAsync(projectId, solution, state).ConfigureAwait(false); + ClearErrorsCore(projectId, solution, state); } } }, GetApplicableCancellationToken(state)); return; - async Task ClearErrorsCoreAsync(ProjectId projectId, Solution solution, InProgressState? state) + void ClearErrorsCore(ProjectId projectId, Solution solution, InProgressState? state) { Debug.Assert(state == null || !state.WereProjectErrorsCleared(projectId)); @@ -220,7 +220,7 @@ async Task ClearErrorsCoreAsync(ProjectId projectId, Solution solution, InProgre ClearBuildOnlyProjectErrors(solution, projectId); - await SetLiveErrorsForProjectAsync(projectId, ImmutableArray.Empty, GetApplicableCancellationToken(state)).ConfigureAwait(false); + SetLiveErrorsForProject(projectId, ImmutableArray.Empty, GetApplicableCancellationToken(state)); state?.MarkErrorsCleared(projectId); @@ -312,7 +312,7 @@ internal void OnSolutionBuildCompleted() using var operation = _notificationService.Start("BuildDone"); if (_diagnosticService is DiagnosticAnalyzerService diagnosticService) { - await SyncBuildErrorsAndReportOnBuildCompletedAsync(diagnosticService, inProgressState).ConfigureAwait(false); + SyncBuildErrorsAndReportOnBuildCompleted(diagnosticService, inProgressState); } // Mark build as complete. @@ -330,7 +330,7 @@ internal void OnSolutionBuildCompleted() /// It raises diagnostic update events for both the Build-only diagnostics and Build + Intellisense diagnostics /// in the error list. /// - private async Task SyncBuildErrorsAndReportOnBuildCompletedAsync(DiagnosticAnalyzerService diagnosticService, InProgressState inProgressState) + private void SyncBuildErrorsAndReportOnBuildCompleted(DiagnosticAnalyzerService diagnosticService, InProgressState inProgressState) { var solution = inProgressState.Solution; var cancellationToken = inProgressState.CancellationToken; @@ -357,7 +357,7 @@ private async Task SyncBuildErrorsAndReportOnBuildCompletedAsync(DiagnosticAnaly } // Report pending live errors - await diagnosticService.SynchronizeWithBuildAsync(_workspace, pendingLiveErrorsToSync, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: true, cancellationToken).ConfigureAwait(false); + diagnosticService.SynchronizeWithBuild(_workspace, pendingLiveErrorsToSync, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: true, cancellationToken); } private void ReportBuildErrors(T item, Solution solution, ImmutableArray buildErrors) @@ -399,9 +399,9 @@ public void AddNewErrors(ProjectId projectId, DiagnosticData diagnostic) // Capture state that will be processed in background thread. var state = GetOrCreateInProgressState(); - _taskQueue.ScheduleTask("Project New Errors", async () => + _taskQueue.ScheduleTask("Project New Errors", () => { - await ReportPreviousProjectErrorsIfRequiredAsync(projectId, state).ConfigureAwait(false); + ReportPreviousProjectErrorsIfRequired(projectId, state); state.AddError(projectId, diagnostic); }, state.CancellationToken); } @@ -411,9 +411,9 @@ public void AddNewErrors(DocumentId documentId, DiagnosticData diagnostic) // Capture state that will be processed in background thread. var state = GetOrCreateInProgressState(); - _taskQueue.ScheduleTask("Document New Errors", async () => + _taskQueue.ScheduleTask("Document New Errors", () => { - await ReportPreviousProjectErrorsIfRequiredAsync(documentId.ProjectId, state).ConfigureAwait(false); + ReportPreviousProjectErrorsIfRequired(documentId.ProjectId, state); state.AddError(documentId, diagnostic); }, state.CancellationToken); } @@ -424,9 +424,9 @@ public void AddNewErrors( // Capture state that will be processed in background thread var state = GetOrCreateInProgressState(); - _taskQueue.ScheduleTask("Project New Errors", async () => + _taskQueue.ScheduleTask("Project New Errors", () => { - await ReportPreviousProjectErrorsIfRequiredAsync(projectId, state).ConfigureAwait(false); + ReportPreviousProjectErrorsIfRequired(projectId, state); foreach (var kv in documentErrorMap) { @@ -445,29 +445,29 @@ public void AddNewErrors( /// This ensures that error list keeps getting refreshed while a build is in progress, as opposed to doing all the work /// and a single refresh when the build completes. /// - private async Task ReportPreviousProjectErrorsIfRequiredAsync(ProjectId projectId, InProgressState state) + private void ReportPreviousProjectErrorsIfRequired(ProjectId projectId, InProgressState state) { if (state.TryGetLastProjectWithReportedErrors() is ProjectId lastProjectId && lastProjectId != projectId) { - await SetLiveErrorsForProjectAsync(lastProjectId, state).ConfigureAwait(false); + SetLiveErrorsForProject(lastProjectId, state); } } - private async Task SetLiveErrorsForProjectAsync(ProjectId projectId, InProgressState state) + private void SetLiveErrorsForProject(ProjectId projectId, InProgressState state) { var diagnostics = state.GetLiveErrorsForProject(projectId); - await SetLiveErrorsForProjectAsync(projectId, diagnostics, state.CancellationToken).ConfigureAwait(false); + SetLiveErrorsForProject(projectId, diagnostics, state.CancellationToken); state.MarkLiveErrorsReported(projectId); } - private async Task SetLiveErrorsForProjectAsync(ProjectId projectId, ImmutableArray diagnostics, CancellationToken cancellationToken) + private void SetLiveErrorsForProject(ProjectId projectId, ImmutableArray diagnostics, CancellationToken cancellationToken) { if (_diagnosticService is DiagnosticAnalyzerService diagnosticAnalyzerService) { // make those errors live errors var map = ProjectErrorMap.Empty.Add(projectId, diagnostics); - await diagnosticAnalyzerService.SynchronizeWithBuildAsync(_workspace, map, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: false, cancellationToken).ConfigureAwait(false); + diagnosticAnalyzerService.SynchronizeWithBuild(_workspace, map, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: false, cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs index 69c9dae08b888..46fb361669853 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -366,3 +368,5 @@ private static ImmutableArray GetCustomTags(ObjectReader reader, int cou } } } + +#endif From c34fa7a00ddbc2286bc6bf83bb30c85e3f498828 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 31 May 2021 10:58:12 -0700 Subject: [PATCH 07/17] Rename members for clarity --- ...gnosticIncrementalAnalyzer.ProjectState.cs | 49 +++++++++---------- ...ncrementalAnalyzer_BuildSynchronization.cs | 2 +- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 83f967e627a74..2e3d76cbab51e 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -88,7 +88,7 @@ public async Task GetAnalysisDataAsync(Project project continue; } - if (!TryDeserializeDocumentDiagnostics(serializerVersion, document, builder)) + if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) { Debug.Assert(lastResult.Version == VersionStamp.Default); @@ -98,7 +98,7 @@ public async Task GetAnalysisDataAsync(Project project } } - if (!TryDeserializeProjectDiagnostics(serializerVersion, project, builder)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. @@ -137,7 +137,7 @@ public async Task GetAnalysisDataAsync(TextDocument do var serializerVersion = lastResult.Version; var builder = new Builder(document.Project, lastResult.Version); - if (!TryDeserializeDocumentDiagnostics(serializerVersion, document, builder)) + if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) { Debug.Assert(lastResult.Version == VersionStamp.Default); @@ -178,7 +178,7 @@ public async Task GetProjectAnalysisDataAsync(Project var serializerVersion = lastResult.Version; var builder = new Builder(project, lastResult.Version); - if (!TryDeserializeProjectDiagnostics(serializerVersion, project, builder)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. @@ -187,10 +187,7 @@ public async Task GetProjectAnalysisDataAsync(Project return builder.ToResult(); } - public void SaveInMemoryCache(Project project, DiagnosticAnalysisResult result) - => SaveCore(project, result); - - private void SaveCore(Project project, DiagnosticAnalysisResult result) + public void SaveToInMemoryStorage(Project project, DiagnosticAnalysisResult result) { Contract.ThrowIfTrue(result.IsAggregatedForm); Contract.ThrowIfNull(result.DocumentIds); @@ -217,12 +214,12 @@ private void SaveCore(Project project, DiagnosticAnalysisResult result) continue; } - Serialize(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax)); - Serialize(serializerVersion, project, document, document.Id, _owner.SemanticStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic)); - Serialize(serializerVersion, project, document, document.Id, _owner.NonLocalStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.NonLocal)); + AddToInMemoryStorage(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax)); + AddToInMemoryStorage(serializerVersion, project, document, document.Id, _owner.SemanticStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic)); + AddToInMemoryStorage(serializerVersion, project, document, document.Id, _owner.NonLocalStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.NonLocal)); } - Serialize(serializerVersion, project, document: null, result.ProjectId, _owner.NonLocalStateName, result.GetOtherDiagnostics()); + AddToInMemoryStorage(serializerVersion, project, document: null, result.ProjectId, _owner.NonLocalStateName, result.GetOtherDiagnostics()); } public void ResetVersion() @@ -270,8 +267,8 @@ public async Task MergeAsync(ActiveFileState state, TextDocument document) var serializerVersion = version; // save active file diagnostics back to project state - Serialize(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, syntax.Items); - Serialize(serializerVersion, project, document, document.Id, _owner.SemanticStateName, semantic.Items); + AddToInMemoryStorage(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, syntax.Items); + AddToInMemoryStorage(serializerVersion, project, document, document.Id, _owner.SemanticStateName, semantic.Items); // save last aggregated form of analysis result _lastResult = _lastResult.UpdateAggregatedResult(version, state.DocumentId, fromBuild); @@ -300,13 +297,13 @@ private async Task LoadInitialAnalysisDataAsync(Projec { cancellationToken.ThrowIfCancellationRequested(); - if (!TryDeserializeDocumentDiagnostics(serializerVersion, document, builder)) + if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) { continue; } } - if (!TryDeserializeProjectDiagnostics(serializerVersion, project, builder)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -323,7 +320,7 @@ private async Task LoadInitialAnalysisDataAsync(TextDo var serializerVersion = version; var builder = new Builder(project, version); - if (!TryDeserializeDocumentDiagnostics(serializerVersion, document, builder)) + if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -338,7 +335,7 @@ private async Task LoadInitialProjectAnalysisDataAsync var serializerVersion = version; var builder = new Builder(project, version); - if (!TryDeserializeProjectDiagnostics(serializerVersion, project, builder)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -346,7 +343,7 @@ private async Task LoadInitialProjectAnalysisDataAsync return builder.ToResult(); } - private void Serialize(VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey, ImmutableArray diagnostics) + private void AddToInMemoryStorage(VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey, ImmutableArray diagnostics) { Contract.ThrowIfFalse(document == null || document.Project == project); @@ -354,13 +351,13 @@ private void Serialize(VersionStamp serializerVersion, Project project, TextDocu InMemoryStorage.Cache(_owner.Analyzer, (key, stateKey), new CacheEntry(serializerVersion, diagnostics)); } - private bool TryDeserializeDocumentDiagnostics(VersionStamp serializerVersion, TextDocument document, Builder builder) + private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, TextDocument document, Builder builder) { var success = true; var project = document.Project; var documentId = document.Id; - var diagnostics = DeserializeDiagnostics(serializerVersion, project, document, documentId, _owner.SyntaxStateName); + var diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, project, document, documentId, _owner.SyntaxStateName); if (!diagnostics.IsDefault) { builder.AddSyntaxLocals(documentId, diagnostics); @@ -370,7 +367,7 @@ private bool TryDeserializeDocumentDiagnostics(VersionStamp serializerVersion, T success = false; } - diagnostics = DeserializeDiagnostics(serializerVersion, project, document, documentId, _owner.SemanticStateName); + diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, project, document, documentId, _owner.SemanticStateName); if (!diagnostics.IsDefault) { builder.AddSemanticLocals(documentId, diagnostics); @@ -380,7 +377,7 @@ private bool TryDeserializeDocumentDiagnostics(VersionStamp serializerVersion, T success = false; } - diagnostics = DeserializeDiagnostics(serializerVersion, project, document, documentId, _owner.NonLocalStateName); + diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, project, document, documentId, _owner.NonLocalStateName); if (!diagnostics.IsDefault) { builder.AddNonLocals(documentId, diagnostics); @@ -393,9 +390,9 @@ private bool TryDeserializeDocumentDiagnostics(VersionStamp serializerVersion, T return success; } - private bool TryDeserializeProjectDiagnostics(VersionStamp serializerVersion, Project project, Builder builder) + private bool TryGetProjectDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, Project project, Builder builder) { - var diagnostics = DeserializeDiagnostics(serializerVersion, project, document: null, project.Id, _owner.NonLocalStateName); + var diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, project, document: null, project.Id, _owner.NonLocalStateName); if (!diagnostics.IsDefault) { builder.AddOthers(diagnostics); @@ -405,7 +402,7 @@ private bool TryDeserializeProjectDiagnostics(VersionStamp serializerVersion, Pr return false; } - private ImmutableArray DeserializeDiagnostics( + private ImmutableArray GetDiagnosticsFromInMemoryStorage( VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey) { Contract.ThrowIfFalse(document == null || document.Project == project); diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs index 46635cc7d4496..12e43daf2e6d2 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs @@ -66,7 +66,7 @@ public void SynchronizeWithBuild( var result = GetResultOrEmpty(newResult, stateSet.Analyzer, project.Id, VersionStamp.Default); // Save into in-memory cache. - state.SaveInMemoryCache(project, result); + state.SaveToInMemoryStorage(project, result); } // Raise diagnostic updated events after the new diagnostics have been stored into the in-memory cache. diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 07278b3322050..3268ef2055e99 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -155,7 +155,7 @@ private async Task AnalyzeProjectAsync(Project project, bool forceAnalyzerRun, C { var state = stateSet.GetOrCreateProjectState(project.Id); - state.SaveInMemoryCache(project, result.GetResult(stateSet.Analyzer)); + state.SaveToInMemoryStorage(project, result.GetResult(stateSet.Analyzer)); } RaiseProjectDiagnosticsIfNeeded(project, stateSets, result.OldResult, result.Result); From f944fc1563d6269a59eb93a534df873ce5f747cc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 31 May 2021 10:59:22 -0700 Subject: [PATCH 08/17] REmove unused code --- .../DiagnosticDataSerializerTests.cs | 368 ----------------- .../Diagnostics/DiagnosticDataSerializer.cs | 372 ------------------ 2 files changed, 740 deletions(-) delete mode 100644 src/EditorFeatures/Test/Diagnostics/DiagnosticDataSerializerTests.cs delete mode 100644 src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticDataSerializerTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticDataSerializerTests.cs deleted file mode 100644 index a433266053f4e..0000000000000 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticDataSerializerTests.cs +++ /dev/null @@ -1,368 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -#if false - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Composition; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Diagnostics.EngineV2; -using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Test.Utilities; -using Roslyn.Utilities; -using Xunit; - -namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics -{ - [UseExportProvider] - public class DiagnosticDataSerializerTests : TestBase - { - [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] - public async Task SerializationTest_Document() - { - using var workspace = new TestWorkspace(composition: EditorTestCompositions.EditorFeatures.AddParts( - typeof(TestPersistentStorageServiceFactory))); - - var document = workspace.CurrentSolution.AddProject("TestProject", "TestProject", LanguageNames.CSharp).AddDocument("TestDocument", ""); - - var diagnostics = new[] - { - new DiagnosticData( - id: "test1", - category: "Test", - message: "test1 message", - enuMessageForBingSearch: "test1 message format", - severity: DiagnosticSeverity.Info, - defaultSeverity:DiagnosticSeverity.Info, - isEnabledByDefault: false, - warningLevel: 1, - customTags: ImmutableArray.Empty, - properties: ImmutableDictionary.Empty, - document.Project.Id, - new DiagnosticDataLocation(document.Id, new TextSpan(10, 20), "originalFile1", 30, 30, 40, 40, "mappedFile1", 10, 10, 20, 20), - language: LanguageNames.CSharp), - - new DiagnosticData( - id: "test2", - category: "Test", - message: "test2 message", - enuMessageForBingSearch: "test2 message format", - severity: DiagnosticSeverity.Warning, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - warningLevel: 0, - customTags: ImmutableArray.Create("Test2"), - properties: ImmutableDictionary.Empty.Add("propertyKey", "propertyValue"), - document.Project.Id, - new DiagnosticDataLocation(document.Id, new TextSpan(30, 40), "originalFile2", 70, 70, 80, 80, "mappedFile2", 50, 50, 60, 60), - language: "VB", - title: "test2 title", - description: "test2 description", - helpLink: "http://test2link"), - - new DiagnosticData( - id: "test3", - category: "Test", - message: "test3 message", - enuMessageForBingSearch: "test3 message format", - severity:DiagnosticSeverity.Error, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - warningLevel: 2, - customTags: ImmutableArray.Create("Test3", "Test3_2"), - properties: ImmutableDictionary.Empty.Add("p1Key", "p1Value").Add("p2Key", "p2Value"), - document.Project.Id, - new DiagnosticDataLocation(document.Id, new TextSpan(50, 60), "originalFile3", 110, 110, 120, 120, "mappedFile3", 90, 90, 100, 100), - title: "test3 title", - description: "test3 description", - helpLink: "http://test3link"), - - }.ToImmutableArray(); - - var utcTime = DateTime.UtcNow; - var analyzerVersion = VersionStamp.Create(utcTime); - var version = VersionStamp.Create(utcTime.AddDays(1)); - - var key = "document"; - - var persistentService = workspace.Services.GetRequiredService(); - var serializer = new CodeAnalysis.Workspaces.Diagnostics.DiagnosticDataSerializer(analyzerVersion, version); - - Assert.True(await serializer.SerializeAsync(persistentService, document.Project, document, key, diagnostics, CancellationToken.None).ConfigureAwait(false)); - - var recovered = await serializer.DeserializeAsync(persistentService, document.Project, document, key, CancellationToken.None); - - AssertDiagnostics(diagnostics, recovered); - } - - [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] - public async Task SerializationTest_Project() - { - using var workspace = new TestWorkspace(composition: EditorTestCompositions.EditorFeatures.AddParts( - typeof(TestPersistentStorageServiceFactory))); - - var document = workspace.CurrentSolution.AddProject("TestProject", "TestProject", LanguageNames.CSharp).AddDocument("TestDocument", ""); - - var diagnostics = new[] - { - new DiagnosticData( - id: "test1", - category: "Test", - message: "test1 message", - enuMessageForBingSearch: "test1 message format", - severity: DiagnosticSeverity.Info, - defaultSeverity: DiagnosticSeverity.Info, - isEnabledByDefault: false, - warningLevel: 1, - customTags: ImmutableArray.Empty, - properties: ImmutableDictionary.Empty, - projectId: document.Project.Id, - language: LanguageNames.VisualBasic, - description: "test1 description", - helpLink: "http://test1link"), - - new DiagnosticData( - id: "test2", - category: "Test", - message: "test2 message", - enuMessageForBingSearch: "test2 message format", - severity: DiagnosticSeverity.Warning, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - warningLevel: 0, - customTags: ImmutableArray.Create("Test2"), - properties: ImmutableDictionary.Empty.Add("p1Key", "p2Value"), - projectId: document.Project.Id), - - new DiagnosticData( - id: "test3", - category: "Test", - message: "test3 message", - enuMessageForBingSearch: "test3 message format", - severity: DiagnosticSeverity.Error, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - warningLevel: 2, - customTags: ImmutableArray.Create("Test3", "Test3_2"), - properties: ImmutableDictionary.Empty.Add("p2Key", "p2Value").Add("p1Key", "p1Value"), - projectId: document.Project.Id, - description: "test3 description", - helpLink: "http://test3link"), - - }.ToImmutableArray(); - - var utcTime = DateTime.UtcNow; - var analyzerVersion = VersionStamp.Create(utcTime); - var version = VersionStamp.Create(utcTime.AddDays(1)); - - var key = "project"; - var persistentService = workspace.Services.GetRequiredService(); - var serializer = new CodeAnalysis.Workspaces.Diagnostics.DiagnosticDataSerializer(analyzerVersion, version); - - Assert.True(await serializer.SerializeAsync(persistentService, document.Project, document, key, diagnostics, CancellationToken.None).ConfigureAwait(false)); - var recovered = await serializer.DeserializeAsync(persistentService, document.Project, document, key, CancellationToken.None); - - AssertDiagnostics(diagnostics, recovered); - } - - [WorkItem(6104, "https://github.com/dotnet/roslyn/issues/6104")] - [Fact] - public void DiagnosticEquivalence() - { -#if DEBUG - var source = -@"class C -{ - static int F(string s) { return 1; } - static int x = F(new { }); - static int y = F(new { A = 1 }); -}"; - var tree = SyntaxFactory.ParseSyntaxTree(source); - var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, concurrentBuild: false); - var compilation = CSharpCompilation.Create(GetUniqueName(), new[] { tree }, new[] { MscorlibRef }, options); - var model = compilation.GetSemanticModel(tree); - - // Each call to GetDiagnostics will bind field initializers - // (see https://github.com/dotnet/roslyn/issues/6264). - var diagnostics1 = model.GetDiagnostics().ToArray(); - var diagnostics2 = model.GetDiagnostics().ToArray(); - - diagnostics1.Verify( - // (4,22): error CS1503: Argument 1: cannot convert from '' to 'string' - // static int x = F(new { }); - Diagnostic(1503, "new { }").WithArguments("1", "", "string").WithLocation(4, 22), - // (5,22): error CS1503: Argument 1: cannot convert from '' to 'string' - // static int y = F(new { A = 1 }); - Diagnostic(1503, "new { A = 1 }").WithArguments("1", "", "string").WithLocation(5, 22)); - - Assert.NotSame(diagnostics1[0], diagnostics2[0]); - Assert.NotSame(diagnostics1[1], diagnostics2[1]); - Assert.Equal(diagnostics1, diagnostics2); - Assert.True(AnalyzerHelper.AreEquivalent(diagnostics1, diagnostics2)); - - // Verify that not all collections are treated as equivalent. - diagnostics1 = new[] { diagnostics1[0] }; - diagnostics2 = new[] { diagnostics2[1] }; - - Assert.NotEqual(diagnostics1, diagnostics2); - Assert.False(AnalyzerHelper.AreEquivalent(diagnostics1, diagnostics2)); -#endif - } - - private static void AssertDiagnostics(ImmutableArray items1, ImmutableArray items2) - { - Assert.Equal(items1.Length, items2.Length); - - for (var i = 0; i < items1.Length; i++) - { - AssertDiagnostics(items1[i], items2[i]); - } - } - - private static void AssertDiagnostics(DiagnosticData item1, DiagnosticData item2) - { - Assert.Equal(item1.Id, item2.Id); - Assert.Equal(item1.Category, item2.Category); - Assert.Equal(item1.Message, item2.Message); - Assert.Equal(item1.ENUMessageForBingSearch, item2.ENUMessageForBingSearch); - Assert.Equal(item1.Severity, item2.Severity); - Assert.Equal(item1.IsEnabledByDefault, item2.IsEnabledByDefault); - Assert.Equal(item1.WarningLevel, item2.WarningLevel); - Assert.Equal(item1.DefaultSeverity, item2.DefaultSeverity); - - Assert.Equal(item1.CustomTags.Length, item2.CustomTags.Length); - for (var j = 0; j < item1.CustomTags.Length; j++) - Assert.Equal(item1.CustomTags[j], item2.CustomTags[j]); - - Assert.Equal(item1.Properties.Count, item2.Properties.Count); - Assert.True(item1.Properties.SetEquals(item2.Properties)); - - Assert.Equal(item1.ProjectId, item2.ProjectId); - Assert.Equal(item1.DocumentId, item2.DocumentId); - - Assert.Equal(item1.HasTextSpan, item2.HasTextSpan); - if (item1.HasTextSpan) - { - Assert.Equal(item1.GetTextSpan(), item2.GetTextSpan()); - } - - Assert.Equal(item1.DataLocation?.MappedFilePath, item2.DataLocation?.MappedFilePath); - Assert.Equal(item1.DataLocation?.MappedStartLine, item2.DataLocation?.MappedStartLine); - Assert.Equal(item1.DataLocation?.MappedStartColumn, item2.DataLocation?.MappedStartColumn); - Assert.Equal(item1.DataLocation?.MappedEndLine, item2.DataLocation?.MappedEndLine); - Assert.Equal(item1.DataLocation?.MappedEndColumn, item2.DataLocation?.MappedEndColumn); - - Assert.Equal(item1.DataLocation?.OriginalFilePath, item2.DataLocation?.OriginalFilePath); - Assert.Equal(item1.DataLocation?.OriginalStartLine, item2.DataLocation?.OriginalStartLine); - Assert.Equal(item1.DataLocation?.OriginalStartColumn, item2.DataLocation?.OriginalStartColumn); - Assert.Equal(item1.DataLocation?.OriginalEndLine, item2.DataLocation?.OriginalEndLine); - Assert.Equal(item1.DataLocation?.OriginalEndColumn, item2.DataLocation?.OriginalEndColumn); - - Assert.Equal(item1.Description, item2.Description); - Assert.Equal(item1.HelpLink, item2.HelpLink); - } - - [ExportWorkspaceServiceFactory(typeof(IPersistentStorageService), ServiceLayer.Test), Shared, PartNotDiscoverable] - public class TestPersistentStorageServiceFactory : IWorkspaceServiceFactory - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public TestPersistentStorageServiceFactory() - { - } - - public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new Service(); - - public class Service : IPersistentStorageService - { - private readonly Storage _instance = new(); - - IPersistentStorage IPersistentStorageService.GetStorage(Solution solution) - => _instance; - - ValueTask IPersistentStorageService.GetStorageAsync(Solution solution, CancellationToken cancellationToken) - => new(_instance); - - internal class Storage : IPersistentStorage - { - private readonly Dictionary _map = new(); - - public Task ReadStreamAsync(string name, CancellationToken cancellationToken = default) - { - var stream = _map[name]; - stream.Position = 0; - - return Task.FromResult(stream); - } - - public Task ReadStreamAsync(Project project, string name, CancellationToken cancellationToken = default) - { - var stream = _map[Tuple.Create(project, name)]; - stream.Position = 0; - - return Task.FromResult(stream); - } - - public Task ReadStreamAsync(Document document, string name, CancellationToken cancellationToken = default) - { - var stream = _map[Tuple.Create(document, name)]; - stream.Position = 0; - - return Task.FromResult(stream); - } - - public Task WriteStreamAsync(string name, Stream stream, CancellationToken cancellationToken = default) - { - _map[name] = new MemoryStream(); - stream.CopyTo(_map[name]); - - return SpecializedTasks.True; - } - - public Task WriteStreamAsync(Project project, string name, Stream stream, CancellationToken cancellationToken = default) - { - _map[Tuple.Create(project, name)] = new MemoryStream(); - stream.CopyTo(_map[Tuple.Create(project, name)]); - - return SpecializedTasks.True; - } - - public Task WriteStreamAsync(Document document, string name, Stream stream, CancellationToken cancellationToken = default) - { - _map[Tuple.Create(document, name)] = new MemoryStream(); - stream.CopyTo(_map[Tuple.Create(document, name)]); - - return SpecializedTasks.True; - } - - public void Dispose() - { - } - - public ValueTask DisposeAsync() - { - return ValueTaskFactory.CompletedTask; - } - } - } - } - } -} - -#endif diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs deleted file mode 100644 index 46fb361669853..0000000000000 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataSerializer.cs +++ /dev/null @@ -1,372 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Collections.ObjectModel; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Workspaces.Diagnostics -{ - /// - /// DiagnosticData serializer - /// - internal readonly struct DiagnosticDataSerializer - { - // version of serialized format - private const int FormatVersion = 1; - - // version of analyzer that produced this data - public readonly VersionStamp AnalyzerVersion; - - // version of project this data belong to - public readonly VersionStamp Version; - - public DiagnosticDataSerializer(VersionStamp analyzerVersion, VersionStamp version) - { - AnalyzerVersion = analyzerVersion; - Version = version; - } - - public async Task SerializeAsync(IPersistentStorageService persistentService, Project project, TextDocument? textDocument, string key, ImmutableArray items, CancellationToken cancellationToken) - { - Contract.ThrowIfFalse(textDocument == null || textDocument.Project == project); - - using var stream = SerializableBytes.CreateWritableStream(); - - using (var writer = new ObjectWriter(stream, leaveOpen: true, cancellationToken)) - { - WriteDiagnosticData(writer, items, cancellationToken); - } - - var storage = await persistentService.GetStorageAsync(project.Solution, cancellationToken).ConfigureAwait(false); - await using var _ = storage.ConfigureAwait(false); - - stream.Position = 0; - - var writeTask = (textDocument != null) ? - textDocument is Document document ? - storage.WriteStreamAsync(document, key, stream, cancellationToken) : - storage.WriteStreamAsync(GetSerializationKeyForNonSourceDocument(textDocument, key), stream, cancellationToken) : - storage.WriteStreamAsync(project, key, stream, cancellationToken); - - return await writeTask.ConfigureAwait(false); - } - - private static string GetSerializationKeyForNonSourceDocument(TextDocument document, string key) - => document.Id + ";" + key; - - public async ValueTask> DeserializeAsync(IPersistentStorageService persistentService, Project project, TextDocument? textDocument, string key, CancellationToken cancellationToken) - { - Contract.ThrowIfFalse(textDocument == null || textDocument.Project == project); - - var storage = await persistentService.GetStorageAsync(project.Solution, cancellationToken).ConfigureAwait(false); - await using var _ = storage.ConfigureAwait(false); - - var readTask = (textDocument != null) ? - textDocument is Document document ? - storage.ReadStreamAsync(document, key, cancellationToken) : - storage.ReadStreamAsync(GetSerializationKeyForNonSourceDocument(textDocument, key), cancellationToken) : - storage.ReadStreamAsync(project, key, cancellationToken); - - using var stream = await readTask.ConfigureAwait(false); - using var reader = ObjectReader.TryGetReader(stream, cancellationToken: cancellationToken); - - if (reader == null || - !TryReadDiagnosticData(reader, project, textDocument, cancellationToken, out var data)) - { - return default; - } - - return data; - } - - public void WriteDiagnosticData(ObjectWriter writer, ImmutableArray items, CancellationToken cancellationToken) - { - writer.WriteInt32(FormatVersion); - - AnalyzerVersion.WriteTo(writer); - Version.WriteTo(writer); - - writer.WriteInt32(items.Length); - - foreach (var item in items) - { - cancellationToken.ThrowIfCancellationRequested(); - - writer.WriteString(item.Id); - writer.WriteString(item.Category); - - writer.WriteString(item.Message); - writer.WriteString(item.ENUMessageForBingSearch); - writer.WriteString(item.Title); - writer.WriteString(item.Description); - writer.WriteString(item.HelpLink); - writer.WriteInt32((int)item.Severity); - writer.WriteInt32((int)item.DefaultSeverity); - writer.WriteBoolean(item.IsEnabledByDefault); - writer.WriteBoolean(item.IsSuppressed); - writer.WriteInt32(item.WarningLevel); - - // unused - writer.WriteInt32(0); - writer.WriteInt32(0); - - WriteLocation(writer, item.DataLocation); - WriteAdditionalLocations(writer, item.AdditionalLocations, cancellationToken); - - writer.WriteInt32(item.CustomTags.Length); - foreach (var tag in item.CustomTags) - writer.WriteString(tag); - - writer.WriteInt32(item.Properties.Count); - foreach (var property in item.Properties) - { - writer.WriteString(property.Key); - writer.WriteString(property.Value); - } - } - } - - private static void WriteAdditionalLocations(ObjectWriter writer, IReadOnlyCollection additionalLocations, CancellationToken cancellationToken) - { - writer.WriteInt32(additionalLocations.Count); - - foreach (var location in additionalLocations) - { - cancellationToken.ThrowIfCancellationRequested(); - WriteLocation(writer, location); - } - } - - private static void WriteLocation(ObjectWriter writer, DiagnosticDataLocation? item) - { - if (item == null) - { - writer.WriteBoolean(false); - return; - } - - writer.WriteBoolean(true); - - if (item.SourceSpan.HasValue) - { - writer.WriteBoolean(true); - writer.WriteInt32(item.SourceSpan.Value.Start); - writer.WriteInt32(item.SourceSpan.Value.Length); - } - else - { - writer.WriteBoolean(false); - } - - writer.WriteString(item.OriginalFilePath); - writer.WriteInt32(item.OriginalStartLine); - writer.WriteInt32(item.OriginalStartColumn); - writer.WriteInt32(item.OriginalEndLine); - writer.WriteInt32(item.OriginalEndColumn); - - writer.WriteString(item.MappedFilePath); - writer.WriteInt32(item.MappedStartLine); - writer.WriteInt32(item.MappedStartColumn); - writer.WriteInt32(item.MappedEndLine); - writer.WriteInt32(item.MappedEndColumn); - } - - public bool TryReadDiagnosticData( - ObjectReader reader, - Project project, - TextDocument? document, - CancellationToken cancellationToken, - out ImmutableArray data) - { - data = default; - - try - { - var format = reader.ReadInt32(); - if (format != FormatVersion) - { - return false; - } - - // saved data is for same analyzer of different version of dll - var analyzerVersion = VersionStamp.ReadFrom(reader); - if (analyzerVersion != AnalyzerVersion) - { - return false; - } - - var version = VersionStamp.ReadFrom(reader); - if (version != VersionStamp.Default && version != Version) - { - return false; - } - - data = ReadDiagnosticDataArray(reader, project, document, cancellationToken); - return true; - } - catch (EndOfStreamException) when (cancellationToken.IsCancellationRequested) - { - // The reader was closed due to a cancellation request while in the process of reading a value. Make - // sure to propagate the exception as cancellation and not an error. - cancellationToken.ThrowIfCancellationRequested(); - throw ExceptionUtilities.Unreachable; - } - catch (Exception ex) when (FatalError.ReportAndCatchUnlessCanceled(ex)) - { - return false; - } - } - - private static ImmutableArray ReadDiagnosticDataArray(ObjectReader reader, Project project, TextDocument? document, CancellationToken cancellationToken) - { - var count = reader.ReadInt32(); - if (count == 0) - { - return ImmutableArray.Empty; - } - - var builder = ArrayBuilder.GetInstance(count); - - for (var i = 0; i < count; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - - var id = reader.ReadString(); - var category = reader.ReadString(); - - var message = reader.ReadString(); - var messageFormat = reader.ReadString(); - var title = reader.ReadString(); - var description = reader.ReadString(); - var helpLink = reader.ReadString(); - var severity = (DiagnosticSeverity)reader.ReadInt32(); - var defaultSeverity = (DiagnosticSeverity)reader.ReadInt32(); - var isEnabledByDefault = reader.ReadBoolean(); - var isSuppressed = reader.ReadBoolean(); - var warningLevel = reader.ReadInt32(); - - // these fields are unused - the actual span is read in ReadLocation - _ = reader.ReadInt32(); - _ = reader.ReadInt32(); - - var location = ReadLocation(project, reader, document); - var additionalLocations = ReadAdditionalLocations(project, reader); - - var customTagsCount = reader.ReadInt32(); - var customTags = GetCustomTags(reader, customTagsCount); - - var propertiesCount = reader.ReadInt32(); - var properties = GetProperties(reader, propertiesCount); - - builder.Add(new DiagnosticData( - id: id, - category: category, - message: message, - enuMessageForBingSearch: messageFormat, - severity: severity, - defaultSeverity: defaultSeverity, - isEnabledByDefault: isEnabledByDefault, - warningLevel: warningLevel, - customTags: customTags, - properties: properties, - projectId: project.Id, - location: location, - additionalLocations: additionalLocations, - language: project.Language, - title: title, - description: description, - helpLink: helpLink, - isSuppressed: isSuppressed)); - } - - return builder.ToImmutableAndFree(); - } - - private static DiagnosticDataLocation? ReadLocation(Project project, ObjectReader reader, TextDocument? document) - { - var exists = reader.ReadBoolean(); - if (!exists) - { - return null; - } - - TextSpan? sourceSpan = null; - if (reader.ReadBoolean()) - { - sourceSpan = new TextSpan(reader.ReadInt32(), reader.ReadInt32()); - } - - var originalFile = reader.ReadString(); - var originalStartLine = reader.ReadInt32(); - var originalStartColumn = reader.ReadInt32(); - var originalEndLine = reader.ReadInt32(); - var originalEndColumn = reader.ReadInt32(); - - var mappedFile = reader.ReadString(); - var mappedStartLine = reader.ReadInt32(); - var mappedStartColumn = reader.ReadInt32(); - var mappedEndLine = reader.ReadInt32(); - var mappedEndColumn = reader.ReadInt32(); - - var documentId = document != null - ? document.Id - : project.Solution.GetDocumentIdsWithFilePath(originalFile).FirstOrDefault(documentId => documentId.ProjectId == project.Id); - - return new DiagnosticDataLocation(documentId, sourceSpan, - originalFile, originalStartLine, originalStartColumn, originalEndLine, originalEndColumn, - mappedFile, mappedStartLine, mappedStartColumn, mappedEndLine, mappedEndColumn); - } - - private static ImmutableArray ReadAdditionalLocations(Project project, ObjectReader reader) - { - var count = reader.ReadInt32(); - using var _ = ArrayBuilder.GetInstance(count, out var result); - for (var i = 0; i < count; i++) - result.AddIfNotNull(ReadLocation(project, reader, document: null)); - - return result.ToImmutable(); - } - - private static ImmutableDictionary GetProperties(ObjectReader reader, int count) - { - if (count > 0) - { - var properties = ImmutableDictionary.CreateBuilder(); - for (var i = 0; i < count; i++) - { - properties.Add(reader.ReadString(), reader.ReadString()); - } - - return properties.ToImmutable(); - } - - return ImmutableDictionary.Empty; - } - - private static ImmutableArray GetCustomTags(ObjectReader reader, int count) - { - using var _ = ArrayBuilder.GetInstance(count, out var tags); - for (var i = 0; i < count; i++) - tags.Add(reader.ReadString()); - - return tags.ToImmutable(); - } - } -} - -#endif From de666d222b31bda15ac26d6f8d429585c2193fcf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 31 May 2021 11:08:02 -0700 Subject: [PATCH 09/17] MOve static methods --- ...gnosticIncrementalAnalyzer.StateManager.cs | 47 ----------------- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 51 +++++++++++++++++-- 2 files changed, 47 insertions(+), 51 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index b1b073227bdd1..3d398a1b76fc5 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -188,53 +188,6 @@ public ImmutableArray CreateBuildOnlyProjectStateSet(Project project) return stateSets.ToImmutable(); } - public static bool OnDocumentReset(IEnumerable stateSets, TextDocument document) - { - // can not be cancelled - var removed = false; - foreach (var stateSet in stateSets) - { - removed |= stateSet.OnDocumentReset(document); - } - - return removed; - } - - public static async Task OnDocumentOpenedAsync(IEnumerable stateSets, TextDocument document) - { - // can not be cancelled - var opened = false; - foreach (var stateSet in stateSets) - { - opened |= await stateSet.OnDocumentOpenedAsync(document).ConfigureAwait(false); - } - - return opened; - } - - public static async Task OnDocumentClosedAsync(IEnumerable stateSets, TextDocument document) - { - // can not be cancelled - var removed = false; - foreach (var stateSet in stateSets) - { - removed |= await stateSet.OnDocumentClosedAsync(document).ConfigureAwait(false); - } - - return removed; - } - - public static bool OnDocumentRemoved(IEnumerable stateSets, DocumentId documentId) - { - var removed = false; - foreach (var stateSet in stateSets) - { - removed |= stateSet.OnDocumentRemoved(documentId); - } - - return removed; - } - public bool OnProjectRemoved(IEnumerable stateSets, ProjectId projectId) { var removed = false; diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 3268ef2055e99..ad391eaba8429 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -180,7 +180,19 @@ private async Task TextDocumentOpenAsync(TextDocument document, CancellationToke // let other component knows about this event ClearCompilationsWithAnalyzersCache(); - await StateManager.OnDocumentOpenedAsync(stateSets, document).ConfigureAwait(false); + await OnDocumentOpenedAsync(stateSets, document).ConfigureAwait(false); + } + + return; + + static async Task OnDocumentOpenedAsync(IEnumerable stateSets, TextDocument document) + { + // can not be canceled + var opened = false; + foreach (var stateSet in stateSets) + opened |= await stateSet.OnDocumentOpenedAsync(document).ConfigureAwait(false); + + return opened; } } @@ -198,9 +210,21 @@ private async Task TextDocumentCloseAsync(TextDocument document, CancellationTok // let other components knows about this event ClearCompilationsWithAnalyzersCache(); - var documentHadDiagnostics = await StateManager.OnDocumentClosedAsync(stateSets, document).ConfigureAwait(false); + var documentHadDiagnostics = await OnDocumentClosedAsync(stateSets, document).ConfigureAwait(false); RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(document, stateSets, documentHadDiagnostics); } + + return; + + static async Task OnDocumentClosedAsync(IEnumerable stateSets, TextDocument document) + { + // can not be canceled + var removed = false; + foreach (var stateSet in stateSets) + removed |= await stateSet.OnDocumentClosedAsync(document).ConfigureAwait(false); + + return removed; + } } public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) @@ -217,11 +241,21 @@ private Task TextDocumentResetAsync(TextDocument document, CancellationToken can // let other components knows about this event ClearCompilationsWithAnalyzersCache(); - var documentHadDiagnostics = StateManager.OnDocumentReset(stateSets, document); + var documentHadDiagnostics = OnDocumentReset(stateSets, document); RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(document, stateSets, documentHadDiagnostics); } return Task.CompletedTask; + + static bool OnDocumentReset(IEnumerable stateSets, TextDocument document) + { + // can not be canceled + var removed = false; + foreach (var stateSet in stateSets) + removed |= stateSet.OnDocumentReset(document); + + return removed; + } } private void RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(TextDocument document, IEnumerable stateSets, bool documentHadDiagnostics) @@ -252,7 +286,7 @@ public Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancell // let other components knows about this event ClearCompilationsWithAnalyzersCache(); - var changed = StateManager.OnDocumentRemoved(stateSets, documentId); + var changed = OnDocumentRemoved(stateSets, documentId); // if there was no diagnostic reported for this document, nothing to clean up // this is Perf to reduce raising events unnecessarily. @@ -263,6 +297,15 @@ public Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancell } return Task.CompletedTask; + + static bool OnDocumentRemoved(IEnumerable stateSets, DocumentId documentId) + { + var removed = false; + foreach (var stateSet in stateSets) + removed |= stateSet.OnDocumentRemoved(documentId); + + return removed; + } } private void RaiseDiagnosticsRemovedForDocument(DocumentId documentId, IEnumerable stateSets) From 4aeccc3b6b127073ba5b27e7b3ed2263a60ccd5f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 31 May 2021 11:28:09 -0700 Subject: [PATCH 10/17] Remove unused field --- .../EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index 8819d9588d544..62753183d32f5 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -24,8 +24,6 @@ private sealed class StateSet public readonly DiagnosticAnalyzer Analyzer; public readonly string ErrorSourceName; - // analyzer version this state belong to - public readonly VersionStamp AnalyzerVersion; private readonly PersistentNames _persistentNames; private readonly ConcurrentDictionary _activeFileStates; @@ -37,9 +35,6 @@ public StateSet(string language, DiagnosticAnalyzer analyzer, string errorSource Analyzer = analyzer; ErrorSourceName = errorSourceName; - var (_, version) = Analyzer.GetAnalyzerIdAndVersion(); - AnalyzerVersion = version; - _persistentNames = PersistentNames.Create(Analyzer); _activeFileStates = new ConcurrentDictionary(concurrencyLevel: 2, capacity: 10); From 0addecfb8fc9ba6e5e27bc252066daaf470f97ff Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 13:34:58 +0000 Subject: [PATCH 11/17] Update dependencies from https://github.com/dotnet/arcade build 20210531.1 (#53808) [main] Update dependencies from dotnet/arcade --- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index edb415d07ed59..787450c63bb57 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,18 +13,18 @@ - + https://github.com/dotnet/arcade - b5ca0997b26992dcc4e55ec3d87d71000be295ce + c7d6bd607715f334cda90e01967bb0c02dee09be https://github.com/dotnet/roslyn 5b972bceb846f5d15f991a479e285067a75103e4 - + https://github.com/dotnet/arcade - b5ca0997b26992dcc4e55ec3d87d71000be295ce + c7d6bd607715f334cda90e01967bb0c02dee09be diff --git a/global.json b/global.json index e26c60ad2e517..ac386b3a4ad17 100644 --- a/global.json +++ b/global.json @@ -12,7 +12,7 @@ "xcopy-msbuild": "16.8.0-preview2.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21278.1", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21278.1" + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21281.1", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21281.1" } } From 87bf7c21fdff1c0ec26cf69342c6a2e992b37006 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 1 Jun 2021 17:42:35 -0700 Subject: [PATCH 12/17] Inline further --- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 56 +++++-------------- 1 file changed, 13 insertions(+), 43 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index ad391eaba8429..14c70a855f3a1 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -180,19 +180,10 @@ private async Task TextDocumentOpenAsync(TextDocument document, CancellationToke // let other component knows about this event ClearCompilationsWithAnalyzersCache(); - await OnDocumentOpenedAsync(stateSets, document).ConfigureAwait(false); - } - - return; - static async Task OnDocumentOpenedAsync(IEnumerable stateSets, TextDocument document) - { // can not be canceled - var opened = false; foreach (var stateSet in stateSets) - opened |= await stateSet.OnDocumentOpenedAsync(document).ConfigureAwait(false); - - return opened; + await stateSet.OnDocumentOpenedAsync(document).ConfigureAwait(false); } } @@ -210,20 +201,13 @@ private async Task TextDocumentCloseAsync(TextDocument document, CancellationTok // let other components knows about this event ClearCompilationsWithAnalyzersCache(); - var documentHadDiagnostics = await OnDocumentClosedAsync(stateSets, document).ConfigureAwait(false); - RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(document, stateSets, documentHadDiagnostics); - } - - return; - static async Task OnDocumentClosedAsync(IEnumerable stateSets, TextDocument document) - { // can not be canceled - var removed = false; + var documentHadDiagnostics = false; foreach (var stateSet in stateSets) - removed |= await stateSet.OnDocumentClosedAsync(document).ConfigureAwait(false); + documentHadDiagnostics |= await stateSet.OnDocumentClosedAsync(document).ConfigureAwait(false); - return removed; + RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(document, stateSets, documentHadDiagnostics); } } @@ -241,21 +225,15 @@ private Task TextDocumentResetAsync(TextDocument document, CancellationToken can // let other components knows about this event ClearCompilationsWithAnalyzersCache(); - var documentHadDiagnostics = OnDocumentReset(stateSets, document); - RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(document, stateSets, documentHadDiagnostics); - } - - return Task.CompletedTask; - - static bool OnDocumentReset(IEnumerable stateSets, TextDocument document) - { // can not be canceled - var removed = false; + var documentHadDiagnostics = false; foreach (var stateSet in stateSets) - removed |= stateSet.OnDocumentReset(document); + documentHadDiagnostics |= stateSet.OnDocumentReset(document); - return removed; + RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(document, stateSets, documentHadDiagnostics); } + + return Task.CompletedTask; } private void RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(TextDocument document, IEnumerable stateSets, bool documentHadDiagnostics) @@ -286,26 +264,18 @@ public Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancell // let other components knows about this event ClearCompilationsWithAnalyzersCache(); - var changed = OnDocumentRemoved(stateSets, documentId); + + var changed = false; + foreach (var stateSet in stateSets) + changed |= stateSet.OnDocumentRemoved(documentId); // if there was no diagnostic reported for this document, nothing to clean up // this is Perf to reduce raising events unnecessarily. if (changed) - { RaiseDiagnosticsRemovedForDocument(documentId, stateSets); - } } return Task.CompletedTask; - - static bool OnDocumentRemoved(IEnumerable stateSets, DocumentId documentId) - { - var removed = false; - foreach (var stateSet in stateSets) - removed |= stateSet.OnDocumentRemoved(documentId); - - return removed; - } } private void RaiseDiagnosticsRemovedForDocument(DocumentId documentId, IEnumerable stateSets) From 7e06c9a2bd34f35c5e12546d9e4eeacd8d2b7729 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 2 Jun 2021 11:22:14 -0700 Subject: [PATCH 13/17] Make async again --- .../DiagnosticAnalyzerServiceTests.cs | 2 +- ...ticAnalyzerService_BuildSynchronization.cs | 9 ++-- ...gnosticIncrementalAnalyzer.ProjectState.cs | 37 ++++++------- ...ncrementalAnalyzer_BuildSynchronization.cs | 7 ++- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- .../ExternalErrorDiagnosticUpdateSource.cs | 52 +++++++++---------- 6 files changed, 54 insertions(+), 55 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index a9835b0a83577..f28edd0623202 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -367,7 +367,7 @@ public async Task TestSynchronizeWithBuild() // cause analysis var location = Location.Create(document.FilePath, textSpan: default, lineSpan: default); - service.SynchronizeWithBuild( + await service.SynchronizeWithBuildAsync( workspace, ImmutableDictionary>.Empty.Add( document.Project.Id, diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_BuildSynchronization.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_BuildSynchronization.cs index a3e89e47a299d..62a6da55c689b 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_BuildSynchronization.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_BuildSynchronization.cs @@ -16,7 +16,7 @@ internal partial class DiagnosticAnalyzerService /// /// Synchronize build errors with live error. /// - public void SynchronizeWithBuild( + public ValueTask SynchronizeWithBuildAsync( Workspace workspace, ImmutableDictionary> diagnostics, @@ -24,10 +24,9 @@ public void SynchronizeWithBuild( bool onBuildCompleted, CancellationToken cancellationToken) { - if (_map.TryGetValue(workspace, out var analyzer)) - { - analyzer.SynchronizeWithBuild(diagnostics, postBuildAndErrorListRefreshTaskQueue, onBuildCompleted, cancellationToken); - } + return _map.TryGetValue(workspace, out var analyzer) + ? analyzer.SynchronizeWithBuildAsync(diagnostics, postBuildAndErrorListRefreshTaskQueue, onBuildCompleted, cancellationToken) + : default; } } } diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 2e3d76cbab51e..0481c83f98fa5 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -74,7 +74,7 @@ public async Task GetAnalysisDataAsync(Project project return DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version); } - // loading data can be cancelled any time. + // loading data can be canceled any time. var serializerVersion = lastResult.Version; var builder = new Builder(project, lastResult.Version, lastResult.DocumentIds); @@ -133,7 +133,7 @@ public async Task GetAnalysisDataAsync(TextDocument do return DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version); } - // loading data can be cancelled any time. + // loading data can be canceled any time. var serializerVersion = lastResult.Version; var builder = new Builder(document.Project, lastResult.Version); @@ -168,13 +168,13 @@ public async Task GetProjectAnalysisDataAsync(Project return lastResult; } - // if given document doesnt have any diagnostics, return empty. + // if given document doesn't have any diagnostics, return empty. if (lastResult.IsEmpty) { return DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version); } - // loading data can be cancelled any time. + // loading data can be canceled any time. var serializerVersion = lastResult.Version; var builder = new Builder(project, lastResult.Version); @@ -187,7 +187,7 @@ public async Task GetProjectAnalysisDataAsync(Project return builder.ToResult(); } - public void SaveToInMemoryStorage(Project project, DiagnosticAnalysisResult result) + public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAnalysisResult result) { Contract.ThrowIfTrue(result.IsAggregatedForm); Contract.ThrowIfNull(result.DocumentIds); @@ -197,7 +197,7 @@ public void SaveToInMemoryStorage(Project project, DiagnosticAnalysisResult resu // save last aggregated form of analysis result _lastResult = result.ToAggregatedForm(); - // serialization can't be cancelled. + // serialization can't be canceled. var serializerVersion = result.Version; foreach (var documentId in result.DocumentIds) { @@ -214,12 +214,12 @@ public void SaveToInMemoryStorage(Project project, DiagnosticAnalysisResult resu continue; } - AddToInMemoryStorage(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax)); - AddToInMemoryStorage(serializerVersion, project, document, document.Id, _owner.SemanticStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic)); - AddToInMemoryStorage(serializerVersion, project, document, document.Id, _owner.NonLocalStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.NonLocal)); + await AddToInMemoryStorageAsync(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax)).ConfigureAwait(false); + await AddToInMemoryStorageAsync(serializerVersion, project, document, document.Id, _owner.SemanticStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic)).ConfigureAwait(false); + await AddToInMemoryStorageAsync(serializerVersion, project, document, document.Id, _owner.NonLocalStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.NonLocal)).ConfigureAwait(false); } - AddToInMemoryStorage(serializerVersion, project, document: null, result.ProjectId, _owner.NonLocalStateName, result.GetOtherDiagnostics()); + await AddToInMemoryStorageAsync(serializerVersion, project, document: null, result.ProjectId, _owner.NonLocalStateName, result.GetOtherDiagnostics()).ConfigureAwait(false); } public void ResetVersion() @@ -228,7 +228,7 @@ public void ResetVersion() _lastResult = _lastResult.Reset(); } - public async Task MergeAsync(ActiveFileState state, TextDocument document) + public async ValueTask MergeAsync(ActiveFileState state, TextDocument document) { Contract.ThrowIfFalse(state.DocumentId == document.Id); @@ -263,12 +263,12 @@ public async Task MergeAsync(ActiveFileState state, TextDocument document) // we have mixed versions or full analysis is off, set it to default so that it can be re-calculated next time so data can be in sync. var version = VersionStamp.Default; - // serialization can't be cancelled. + // serialization can't be canceled. var serializerVersion = version; // save active file diagnostics back to project state - AddToInMemoryStorage(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, syntax.Items); - AddToInMemoryStorage(serializerVersion, project, document, document.Id, _owner.SemanticStateName, semantic.Items); + await AddToInMemoryStorageAsync(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, syntax.Items).ConfigureAwait(false); + await AddToInMemoryStorageAsync(serializerVersion, project, document, document.Id, _owner.SemanticStateName, semantic.Items).ConfigureAwait(false); // save last aggregated form of analysis result _lastResult = _lastResult.UpdateAggregatedResult(version, state.DocumentId, fromBuild); @@ -288,7 +288,7 @@ public bool OnProjectRemoved(ProjectId id) private async Task LoadInitialAnalysisDataAsync(Project project, CancellationToken cancellationToken) { - // loading data can be cancelled any time. + // loading data can be canceled any time. var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); var serializerVersion = version; var builder = new Builder(project, version); @@ -313,7 +313,7 @@ private async Task LoadInitialAnalysisDataAsync(Projec private async Task LoadInitialAnalysisDataAsync(TextDocument document, CancellationToken cancellationToken) { - // loading data can be cancelled any time. + // loading data can be canceled any time. var project = document.Project; var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); @@ -330,7 +330,7 @@ private async Task LoadInitialAnalysisDataAsync(TextDo private async Task LoadInitialProjectAnalysisDataAsync(Project project, CancellationToken cancellationToken) { - // loading data can be cancelled any time. + // loading data can be canceled any time. var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); var serializerVersion = version; var builder = new Builder(project, version); @@ -343,12 +343,13 @@ private async Task LoadInitialProjectAnalysisDataAsync return builder.ToResult(); } - private void AddToInMemoryStorage(VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey, ImmutableArray diagnostics) + private ValueTask AddToInMemoryStorageAsync(VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey, ImmutableArray diagnostics) { Contract.ThrowIfFalse(document == null || document.Project == project); // if serialization fail, hold it in the memory InMemoryStorage.Cache(_owner.Analyzer, (key, stateKey), new CacheEntry(serializerVersion, diagnostics)); + return default; } private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, TextDocument document, Builder builder) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs index 12e43daf2e6d2..11f101e46b0b5 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer { - public void SynchronizeWithBuild( + public async ValueTask SynchronizeWithBuildAsync( ImmutableDictionary> buildDiagnostics, TaskQueue postBuildAndErrorListRefreshTaskQueue, @@ -65,8 +65,7 @@ public void SynchronizeWithBuild( var state = stateSet.GetOrCreateProjectState(project.Id); var result = GetResultOrEmpty(newResult, stateSet.Analyzer, project.Id, VersionStamp.Default); - // Save into in-memory cache. - state.SaveToInMemoryStorage(project, result); + await state.SaveToInMemoryStorageAsync(project, result).ConfigureAwait(false); } // Raise diagnostic updated events after the new diagnostics have been stored into the in-memory cache. @@ -91,7 +90,7 @@ public void SynchronizeWithBuild( // Enqueue remaining re-analysis with normal priority on a separate task queue // that will execute at the end of all the post build and error list refresh tasks. - _ = postBuildAndErrorListRefreshTaskQueue.ScheduleTask(nameof(SynchronizeWithBuild), () => + _ = postBuildAndErrorListRefreshTaskQueue.ScheduleTask(nameof(SynchronizeWithBuildAsync), () => { // Enqueue re-analysis of open documents. AnalyzerService.Reanalyze(Workspace, documentIds: Workspace.GetOpenDocumentIds()); diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 14c70a855f3a1..89b9e32a07948 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -155,7 +155,7 @@ private async Task AnalyzeProjectAsync(Project project, bool forceAnalyzerRun, C { var state = stateSet.GetOrCreateProjectState(project.Id); - state.SaveToInMemoryStorage(project, result.GetResult(stateSet.Analyzer)); + await state.SaveToInMemoryStorageAsync(project, result.GetResult(stateSet.Analyzer)).ConfigureAwait(false); } RaiseProjectDiagnosticsIfNeeded(project, stateSets, result.OldResult, result.Result); diff --git a/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs index 43752426181eb..37edecb1bc305 100644 --- a/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs @@ -52,7 +52,7 @@ internal sealed class ExternalErrorDiagnosticUpdateSource : IDiagnosticUpdateSou /// /// Task queue to serialize all the post-build and post error list refresh tasks. /// Error list refresh requires build/live diagnostics de-duping to complete, which happens during - /// . + /// . /// Computationally expensive tasks such as writing build errors into persistent storage, /// invoking background analysis on open files/solution after build completes, etc. /// are added to this task queue to help ensure faster error list refresh. @@ -168,13 +168,13 @@ public void ClearErrors(ProjectId projectId) // Update the state to clear diagnostics and raise corresponding diagnostic updated events // on a serialized task queue. - _taskQueue.ScheduleTask(nameof(ClearErrors), () => + _taskQueue.ScheduleTask(nameof(ClearErrors), async () => { if (state == null) { // TODO: Is it possible that ClearErrors can be invoked while the build is not in progress? // We fallback to current solution in the workspace and clear errors for the project. - ClearErrorsCore(projectId, _workspace.CurrentSolution, state); + await ClearErrorsCoreAsync(projectId, _workspace.CurrentSolution, state).ConfigureAwait(false); } else { @@ -191,7 +191,7 @@ public void ClearErrors(ProjectId projectId) var solution = state.Solution; - ClearErrorsCore(projectId, solution, state); + await ClearErrorsCoreAsync(projectId, solution, state).ConfigureAwait(false); var transitiveProjectIds = solution.GetProjectDependencyGraph().GetProjectsThatTransitivelyDependOnThisProject(projectId); foreach (var projectId in transitiveProjectIds) @@ -201,14 +201,14 @@ public void ClearErrors(ProjectId projectId) continue; } - ClearErrorsCore(projectId, solution, state); + await ClearErrorsCoreAsync(projectId, solution, state).ConfigureAwait(false); } } }, GetApplicableCancellationToken(state)); return; - void ClearErrorsCore(ProjectId projectId, Solution solution, InProgressState? state) + async ValueTask ClearErrorsCoreAsync(ProjectId projectId, Solution solution, InProgressState? state) { Debug.Assert(state == null || !state.WereProjectErrorsCleared(projectId)); @@ -220,7 +220,7 @@ void ClearErrorsCore(ProjectId projectId, Solution solution, InProgressState? st ClearBuildOnlyProjectErrors(solution, projectId); - SetLiveErrorsForProject(projectId, ImmutableArray.Empty, GetApplicableCancellationToken(state)); + await SetLiveErrorsForProjectAsync(projectId, ImmutableArray.Empty, GetApplicableCancellationToken(state)).ConfigureAwait(false); state?.MarkErrorsCleared(projectId); @@ -311,9 +311,7 @@ internal void OnSolutionBuildCompleted() // pause live analyzer using var operation = _notificationService.Start("BuildDone"); if (_diagnosticService is DiagnosticAnalyzerService diagnosticService) - { - SyncBuildErrorsAndReportOnBuildCompleted(diagnosticService, inProgressState); - } + await SyncBuildErrorsAndReportOnBuildCompletedAsync(diagnosticService, inProgressState).ConfigureAwait(false); // Mark build as complete. OnBuildProgressChanged(inProgressState, BuildProgress.Done); @@ -330,7 +328,7 @@ internal void OnSolutionBuildCompleted() /// It raises diagnostic update events for both the Build-only diagnostics and Build + Intellisense diagnostics /// in the error list. /// - private void SyncBuildErrorsAndReportOnBuildCompleted(DiagnosticAnalyzerService diagnosticService, InProgressState inProgressState) + private ValueTask SyncBuildErrorsAndReportOnBuildCompletedAsync(DiagnosticAnalyzerService diagnosticService, InProgressState inProgressState) { var solution = inProgressState.Solution; var cancellationToken = inProgressState.CancellationToken; @@ -357,7 +355,7 @@ private void SyncBuildErrorsAndReportOnBuildCompleted(DiagnosticAnalyzerService } // Report pending live errors - diagnosticService.SynchronizeWithBuild(_workspace, pendingLiveErrorsToSync, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: true, cancellationToken); + return diagnosticService.SynchronizeWithBuildAsync(_workspace, pendingLiveErrorsToSync, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: true, cancellationToken); } private void ReportBuildErrors(T item, Solution solution, ImmutableArray buildErrors) @@ -399,9 +397,9 @@ public void AddNewErrors(ProjectId projectId, DiagnosticData diagnostic) // Capture state that will be processed in background thread. var state = GetOrCreateInProgressState(); - _taskQueue.ScheduleTask("Project New Errors", () => + _taskQueue.ScheduleTask("Project New Errors", async () => { - ReportPreviousProjectErrorsIfRequired(projectId, state); + await ReportPreviousProjectErrorsIfRequiredAsync(projectId, state).ConfigureAwait(false); state.AddError(projectId, diagnostic); }, state.CancellationToken); } @@ -411,9 +409,9 @@ public void AddNewErrors(DocumentId documentId, DiagnosticData diagnostic) // Capture state that will be processed in background thread. var state = GetOrCreateInProgressState(); - _taskQueue.ScheduleTask("Document New Errors", () => + _taskQueue.ScheduleTask("Document New Errors", async () => { - ReportPreviousProjectErrorsIfRequired(documentId.ProjectId, state); + await ReportPreviousProjectErrorsIfRequiredAsync(documentId.ProjectId, state).ConfigureAwait(false); state.AddError(documentId, diagnostic); }, state.CancellationToken); } @@ -424,14 +422,12 @@ public void AddNewErrors( // Capture state that will be processed in background thread var state = GetOrCreateInProgressState(); - _taskQueue.ScheduleTask("Project New Errors", () => + _taskQueue.ScheduleTask("Project New Errors", async () => { - ReportPreviousProjectErrorsIfRequired(projectId, state); + await ReportPreviousProjectErrorsIfRequiredAsync(projectId, state).ConfigureAwait(false); foreach (var kv in documentErrorMap) - { state.AddErrors(kv.Key, kv.Value); - } state.AddErrors(projectId, projectErrors); }, state.CancellationToken); @@ -445,30 +441,34 @@ public void AddNewErrors( /// This ensures that error list keeps getting refreshed while a build is in progress, as opposed to doing all the work /// and a single refresh when the build completes. /// - private void ReportPreviousProjectErrorsIfRequired(ProjectId projectId, InProgressState state) + private ValueTask ReportPreviousProjectErrorsIfRequiredAsync(ProjectId projectId, InProgressState state) { if (state.TryGetLastProjectWithReportedErrors() is ProjectId lastProjectId && lastProjectId != projectId) { - SetLiveErrorsForProject(lastProjectId, state); + return SetLiveErrorsForProjectAsync(lastProjectId, state); } + + return default; } - private void SetLiveErrorsForProject(ProjectId projectId, InProgressState state) + private async ValueTask SetLiveErrorsForProjectAsync(ProjectId projectId, InProgressState state) { var diagnostics = state.GetLiveErrorsForProject(projectId); - SetLiveErrorsForProject(projectId, diagnostics, state.CancellationToken); + await SetLiveErrorsForProjectAsync(projectId, diagnostics, state.CancellationToken).ConfigureAwait(false); state.MarkLiveErrorsReported(projectId); } - private void SetLiveErrorsForProject(ProjectId projectId, ImmutableArray diagnostics, CancellationToken cancellationToken) + private ValueTask SetLiveErrorsForProjectAsync(ProjectId projectId, ImmutableArray diagnostics, CancellationToken cancellationToken) { if (_diagnosticService is DiagnosticAnalyzerService diagnosticAnalyzerService) { // make those errors live errors var map = ProjectErrorMap.Empty.Add(projectId, diagnostics); - diagnosticAnalyzerService.SynchronizeWithBuild(_workspace, map, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: false, cancellationToken); + return diagnosticAnalyzerService.SynchronizeWithBuildAsync(_workspace, map, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: false, cancellationToken); } + + return default; } private CancellationToken GetApplicableCancellationToken(InProgressState? state) From 253a00d497b7a686085dc66568fdc9761cf3d39f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 2 Jun 2021 11:32:12 -0700 Subject: [PATCH 14/17] Make async again --- ...gnosticIncrementalAnalyzer.ProjectState.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 0481c83f98fa5..d7b56fd30caa2 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -88,7 +88,7 @@ public async Task GetAnalysisDataAsync(Project project continue; } - if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) + if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder).ConfigureAwait(false)) { Debug.Assert(lastResult.Version == VersionStamp.Default); @@ -98,7 +98,7 @@ public async Task GetAnalysisDataAsync(Project project } } - if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) + if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder).ConfigureAwait(false)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. @@ -137,7 +137,7 @@ public async Task GetAnalysisDataAsync(TextDocument do var serializerVersion = lastResult.Version; var builder = new Builder(document.Project, lastResult.Version); - if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) + if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder).ConfigureAwait(false)) { Debug.Assert(lastResult.Version == VersionStamp.Default); @@ -178,7 +178,7 @@ public async Task GetProjectAnalysisDataAsync(Project var serializerVersion = lastResult.Version; var builder = new Builder(project, lastResult.Version); - if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) + if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder).ConfigureAwait(false)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. @@ -297,13 +297,13 @@ private async Task LoadInitialAnalysisDataAsync(Projec { cancellationToken.ThrowIfCancellationRequested(); - if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) + if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder).ConfigureAwait(false)) { continue; } } - if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) + if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder).ConfigureAwait(false)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -320,7 +320,7 @@ private async Task LoadInitialAnalysisDataAsync(TextDo var serializerVersion = version; var builder = new Builder(project, version); - if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) + if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder).ConfigureAwait(false)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -335,7 +335,7 @@ private async Task LoadInitialProjectAnalysisDataAsync var serializerVersion = version; var builder = new Builder(project, version); - if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) + if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder).ConfigureAwait(false)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -352,13 +352,13 @@ private ValueTask AddToInMemoryStorageAsync(VersionStamp serializerVersion, Proj return default; } - private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, TextDocument document, Builder builder) + private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionStamp serializerVersion, TextDocument document, Builder builder) { var success = true; var project = document.Project; var documentId = document.Id; - var diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, project, document, documentId, _owner.SyntaxStateName); + var diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document, documentId, _owner.SyntaxStateName).ConfigureAwait(false); if (!diagnostics.IsDefault) { builder.AddSyntaxLocals(documentId, diagnostics); @@ -368,7 +368,7 @@ private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion success = false; } - diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, project, document, documentId, _owner.SemanticStateName); + diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document, documentId, _owner.SemanticStateName).ConfigureAwait(false); if (!diagnostics.IsDefault) { builder.AddSemanticLocals(documentId, diagnostics); @@ -378,7 +378,7 @@ private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion success = false; } - diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, project, document, documentId, _owner.NonLocalStateName); + diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document, documentId, _owner.NonLocalStateName).ConfigureAwait(false); if (!diagnostics.IsDefault) { builder.AddNonLocals(documentId, diagnostics); @@ -391,9 +391,9 @@ private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion return success; } - private bool TryGetProjectDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, Project project, Builder builder) + private async ValueTask TryGetProjectDiagnosticsFromInMemoryStorageAsync(VersionStamp serializerVersion, Project project, Builder builder) { - var diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, project, document: null, project.Id, _owner.NonLocalStateName); + var diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document: null, project.Id, _owner.NonLocalStateName).ConfigureAwait(false); if (!diagnostics.IsDefault) { builder.AddOthers(diagnostics); @@ -403,13 +403,13 @@ private bool TryGetProjectDiagnosticsFromInMemoryStorage(VersionStamp serializer return false; } - private ImmutableArray GetDiagnosticsFromInMemoryStorage( + private ValueTask> GetDiagnosticsFromInMemoryStorageAsync( VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey) { Contract.ThrowIfFalse(document == null || document.Project == project); return InMemoryStorage.TryGetValue(_owner.Analyzer, (key, stateKey), out var entry) && serializerVersion == entry.Version - ? entry.Diagnostics + ? new(entry.Diagnostics) : default; } From 5cabbd15f382c30f4e93857e522b07c02621f382 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 2 Jun 2021 11:41:07 -0700 Subject: [PATCH 15/17] Cancellation tokens --- ...gnosticIncrementalAnalyzer.ProjectState.cs | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index d7b56fd30caa2..134c608d5367f 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -84,11 +84,9 @@ public async Task GetAnalysisDataAsync(Project project var document = project.GetDocument(documentId); if (document == null) - { continue; - } - if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder).ConfigureAwait(false)) + if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder, cancellationToken).ConfigureAwait(false)) { Debug.Assert(lastResult.Version == VersionStamp.Default); @@ -98,7 +96,7 @@ public async Task GetAnalysisDataAsync(Project project } } - if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder).ConfigureAwait(false)) + if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder, cancellationToken).ConfigureAwait(false)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. @@ -137,7 +135,7 @@ public async Task GetAnalysisDataAsync(TextDocument do var serializerVersion = lastResult.Version; var builder = new Builder(document.Project, lastResult.Version); - if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder).ConfigureAwait(false)) + if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder, cancellationToken).ConfigureAwait(false)) { Debug.Assert(lastResult.Version == VersionStamp.Default); @@ -178,7 +176,7 @@ public async Task GetProjectAnalysisDataAsync(Project var serializerVersion = lastResult.Version; var builder = new Builder(project, lastResult.Version); - if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder).ConfigureAwait(false)) + if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder, cancellationToken).ConfigureAwait(false)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. @@ -297,13 +295,11 @@ private async Task LoadInitialAnalysisDataAsync(Projec { cancellationToken.ThrowIfCancellationRequested(); - if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder).ConfigureAwait(false)) - { + if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder, cancellationToken).ConfigureAwait(false)) continue; - } } - if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder).ConfigureAwait(false)) + if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder, cancellationToken).ConfigureAwait(false)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -320,7 +316,7 @@ private async Task LoadInitialAnalysisDataAsync(TextDo var serializerVersion = version; var builder = new Builder(project, version); - if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder).ConfigureAwait(false)) + if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder, cancellationToken).ConfigureAwait(false)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -335,7 +331,7 @@ private async Task LoadInitialProjectAnalysisDataAsync var serializerVersion = version; var builder = new Builder(project, version); - if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder).ConfigureAwait(false)) + if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder, cancellationToken).ConfigureAwait(false)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -352,13 +348,13 @@ private ValueTask AddToInMemoryStorageAsync(VersionStamp serializerVersion, Proj return default; } - private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionStamp serializerVersion, TextDocument document, Builder builder) + private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionStamp serializerVersion, TextDocument document, Builder builder, CancellationToken cancellationToken) { var success = true; var project = document.Project; var documentId = document.Id; - var diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document, documentId, _owner.SyntaxStateName).ConfigureAwait(false); + var diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document, documentId, _owner.SyntaxStateName, cancellationToken).ConfigureAwait(false); if (!diagnostics.IsDefault) { builder.AddSyntaxLocals(documentId, diagnostics); @@ -368,7 +364,7 @@ private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionS success = false; } - diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document, documentId, _owner.SemanticStateName).ConfigureAwait(false); + diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document, documentId, _owner.SemanticStateName, cancellationToken).ConfigureAwait(false); if (!diagnostics.IsDefault) { builder.AddSemanticLocals(documentId, diagnostics); @@ -378,7 +374,7 @@ private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionS success = false; } - diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document, documentId, _owner.NonLocalStateName).ConfigureAwait(false); + diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document, documentId, _owner.NonLocalStateName, cancellationToken).ConfigureAwait(false); if (!diagnostics.IsDefault) { builder.AddNonLocals(documentId, diagnostics); @@ -391,9 +387,9 @@ private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionS return success; } - private async ValueTask TryGetProjectDiagnosticsFromInMemoryStorageAsync(VersionStamp serializerVersion, Project project, Builder builder) + private async ValueTask TryGetProjectDiagnosticsFromInMemoryStorageAsync(VersionStamp serializerVersion, Project project, Builder builder, CancellationToken cancellationToken) { - var diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document: null, project.Id, _owner.NonLocalStateName).ConfigureAwait(false); + var diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document: null, project.Id, _owner.NonLocalStateName, cancellationToken).ConfigureAwait(false); if (!diagnostics.IsDefault) { builder.AddOthers(diagnostics); @@ -404,7 +400,7 @@ private async ValueTask TryGetProjectDiagnosticsFromInMemoryStorageAsync(V } private ValueTask> GetDiagnosticsFromInMemoryStorageAsync( - VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey) + VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey, CancellationToken _) { Contract.ThrowIfFalse(document == null || document.Project == project); From 97d2d1640339287ac14d9785e6914fff7cf5cc99 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 2 Jun 2021 11:45:19 -0700 Subject: [PATCH 16/17] Simplify --- .../EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 134c608d5367f..4c8861098d704 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -300,9 +300,7 @@ private async Task LoadInitialAnalysisDataAsync(Projec } if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder, cancellationToken).ConfigureAwait(false)) - { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); - } return builder.ToResult(); } @@ -332,9 +330,7 @@ private async Task LoadInitialProjectAnalysisDataAsync var builder = new Builder(project, version); if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder, cancellationToken).ConfigureAwait(false)) - { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); - } return builder.ToResult(); } From a56336b0ae0b994b0d5b00b445aa27fb59a6f937 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 2 Jun 2021 13:24:22 -0700 Subject: [PATCH 17/17] Store more information when an exception happens when making RPC calls to oop server --- .../Host/Interactive/Core/InteractiveHost.cs | 1 + .../LanguageClient/AbstractInProcLanguageClient.cs | 6 +++++- .../Core/Test.Next/Services/LspDiagnosticsTests.cs | 10 ++++++++-- src/Workspaces/Remote/Core/RemoteEndPoint.cs | 3 ++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Interactive/Host/Interactive/Core/InteractiveHost.cs b/src/Interactive/Host/Interactive/Core/InteractiveHost.cs index 1bb5b525bc595..a7d291b934226 100644 --- a/src/Interactive/Host/Interactive/Core/InteractiveHost.cs +++ b/src/Interactive/Host/Interactive/Core/InteractiveHost.cs @@ -307,6 +307,7 @@ private static JsonRpc CreateRpc(Stream stream, object? incomingCallTarget) var rpc = new JsonRpc(new HeaderDelimitedMessageHandler(stream, jsonFormatter)) { CancelLocallyInvokedMethodsWhenConnectionIsClosed = true, + ExceptionStrategy = ExceptionProcessing.ISerializable, }; if (incomingCallTarget != null) diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageClient/AbstractInProcLanguageClient.cs b/src/VisualStudio/Core/Def/Implementation/LanguageClient/AbstractInProcLanguageClient.cs index 181f16f555280..aedcb21c1a2a6 100644 --- a/src/VisualStudio/Core/Def/Implementation/LanguageClient/AbstractInProcLanguageClient.cs +++ b/src/VisualStudio/Core/Def/Implementation/LanguageClient/AbstractInProcLanguageClient.cs @@ -195,7 +195,11 @@ internal static async Task CreateAsync( var jsonMessageFormatter = new JsonMessageFormatter(); VSExtensionUtilities.AddVSExtensionConverters(jsonMessageFormatter.JsonSerializer); - var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream, jsonMessageFormatter)); + var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream, jsonMessageFormatter)) + { + ExceptionStrategy = ExceptionProcessing.ISerializable, + }; + var serverTypeName = languageClient.GetType().Name; var logger = await CreateLoggerAsync(asyncServiceProvider, serverTypeName, clientName, jsonRpc, cancellationToken).ConfigureAwait(false); diff --git a/src/VisualStudio/Core/Test.Next/Services/LspDiagnosticsTests.cs b/src/VisualStudio/Core/Test.Next/Services/LspDiagnosticsTests.cs index c86284df6c6b9..0067a33d28229 100644 --- a/src/VisualStudio/Core/Test.Next/Services/LspDiagnosticsTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/LspDiagnosticsTests.cs @@ -375,7 +375,10 @@ public async Task ClearAllDiagnosticsForMappedFileToManyDocumentsTestAsync() // Notification target for tests to receive the notification details var callback = new Callback(expectedNumberOfCallbacks); - using var jsonRpc = new JsonRpc(clientStream, clientStream, callback); + using var jsonRpc = new JsonRpc(clientStream, clientStream, callback) + { + ExceptionStrategy = ExceptionProcessing.ISerializable, + }; // The json rpc messages won't necessarily come back in order by default. // So use a synchronization context to preserve the original ordering. @@ -399,7 +402,10 @@ static VisualStudioInProcLanguageServer CreateLanguageServer(Stream inputStream, var lspWorkspaceRegistrationService = workspace.ExportProvider.GetExportedValue(); var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); - var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream)); + var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream)) + { + ExceptionStrategy = ExceptionProcessing.ISerializable, + }; var languageServer = new VisualStudioInProcLanguageServer( dispatcherFactory, diff --git a/src/Workspaces/Remote/Core/RemoteEndPoint.cs b/src/Workspaces/Remote/Core/RemoteEndPoint.cs index 83fc6538e543b..824b62a96c2ba 100644 --- a/src/Workspaces/Remote/Core/RemoteEndPoint.cs +++ b/src/Workspaces/Remote/Core/RemoteEndPoint.cs @@ -66,7 +66,8 @@ public RemoteEndPoint(Stream stream, TraceSource logger, object? incomingCallTar _rpc = new JsonRpc(new HeaderDelimitedMessageHandler(stream, jsonFormatter)) { CancelLocallyInvokedMethodsWhenConnectionIsClosed = true, - TraceSource = logger + TraceSource = logger, + ExceptionStrategy = ExceptionProcessing.ISerializable, }; if (incomingCallTarget != null)