From 912d33da729ce5cbff3cabb18d7998062dcee2ea Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Wed, 8 Feb 2017 16:15:34 -0800 Subject: [PATCH] move down diagnostic analyzer executor down to workspace layer since all requires one now lives in workspace layer. --- .../ServiceTestExportProvider.cs | 2 +- .../DefaultDiagnosticAnalyzerService.cs | 4 +- .../Diagnostics/DiagnosticArguments.cs | 2 +- .../Diagnostics/DiagnosticResultSerializer.cs | 2 +- .../EngineV2/DiagnosticAnalyzerExecutor.cs | 242 ++++++++++++++++++ ...cCodeAnalysisDiagnosticAnalyzerExecutor.cs | 46 ---- src/Features/Core/Portable/Features.csproj | 4 +- .../VisualStudioDiagnosticAnalyzerExecutor.cs | 193 -------------- .../Core/Def/ServicesVisualStudio.csproj | 9 +- .../Test.Next/Remote/JsonConverterTests.cs | 4 +- ...alStudioDiagnosticAnalyzerExecutorTests.cs | 7 +- .../Remote/ServiceHub/ServiceHub.csproj | 2 - 12 files changed, 255 insertions(+), 262 deletions(-) rename src/{Workspaces/Remote/ServiceHub => Features/Core/Portable}/Diagnostics/DiagnosticArguments.cs (96%) rename src/{Workspaces/Remote/ServiceHub => Features/Core/Portable}/Diagnostics/DiagnosticResultSerializer.cs (99%) create mode 100644 src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs delete mode 100644 src/Features/Core/Portable/Diagnostics/EngineV2/InProcCodeAnalysisDiagnosticAnalyzerExecutor.cs delete mode 100644 src/VisualStudio/Core/Def/Implementation/Diagnostics/VisualStudioDiagnosticAnalyzerExecutor.cs diff --git a/src/EditorFeatures/TestUtilities/ServiceTestExportProvider.cs b/src/EditorFeatures/TestUtilities/ServiceTestExportProvider.cs index 78cbcddaf59..60318532a24 100644 --- a/src/EditorFeatures/TestUtilities/ServiceTestExportProvider.cs +++ b/src/EditorFeatures/TestUtilities/ServiceTestExportProvider.cs @@ -34,7 +34,7 @@ public static Type[] GetLanguageNeutralTypes() typeof(Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent.SmartIndentProvider), typeof(Microsoft.CodeAnalysis.Editor.Implementation.ForegroundNotification.ForegroundNotificationService), typeof(IncrementalCaches.SymbolTreeInfoIncrementalAnalyzerProvider), - typeof(CodeAnalysis.Diagnostics.EngineV2.InProcCodeAnalysisDiagnosticAnalyzerExecutor) + typeof(CodeAnalysis.Diagnostics.EngineV2.DiagnosticAnalyzerExecutor) }; return MinimalTestExportProvider.GetLanguageNeutralTypes().Concat(types).Distinct().ToArray(); diff --git a/src/Features/Core/Portable/Diagnostics/DefaultDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/DefaultDiagnosticAnalyzerService.cs index 4c06deeb74a..42c19dc2ebf 100644 --- a/src/Features/Core/Portable/Diagnostics/DefaultDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/DefaultDiagnosticAnalyzerService.cs @@ -6,14 +6,12 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Options; using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics +namespace Microsoft.CodeAnalysis.Diagnostics { [Shared] [ExportIncrementalAnalyzerProvider(WellKnownSolutionCrawlerAnalyzers.Diagnostic, workspaceKinds: null)] diff --git a/src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticArguments.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticArguments.cs similarity index 96% rename from src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticArguments.cs rename to src/Features/Core/Portable/Diagnostics/DiagnosticArguments.cs index 2c7eaba5658..42d0bea75bf 100644 --- a/src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticArguments.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticArguments.cs @@ -3,7 +3,7 @@ using System.Collections.Immutable; using System.Linq; -namespace Microsoft.CodeAnalysis.Remote.Diagnostics +namespace Microsoft.CodeAnalysis.Diagnostics { /// /// helper type to package diagnostic arguments to pass around between remote hosts diff --git a/src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticResultSerializer.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticResultSerializer.cs similarity index 99% rename from src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticResultSerializer.cs rename to src/Features/Core/Portable/Diagnostics/DiagnosticResultSerializer.cs index 3ba68de8f44..26803fe761f 100644 --- a/src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticResultSerializer.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticResultSerializer.cs @@ -11,7 +11,7 @@ using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Remote.Diagnostics +namespace Microsoft.CodeAnalysis.Diagnostics { internal static class DiagnosticResultSerializer { diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs new file mode 100644 index 00000000000..213e13d9168 --- /dev/null +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs @@ -0,0 +1,242 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics.Telemetry; +using Microsoft.CodeAnalysis.Execution; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Workspaces.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +{ + [ExportWorkspaceServiceFactory(typeof(ICodeAnalysisDiagnosticAnalyzerExecutor)), Shared] + internal class DiagnosticAnalyzerExecutor : IWorkspaceServiceFactory + { + private readonly IDiagnosticAnalyzerService _analyzerServiceOpt; + private readonly AbstractHostDiagnosticUpdateSource _hostDiagnosticUpdateSourceOpt; + + [ImportingConstructor] + public DiagnosticAnalyzerExecutor( + [Import(AllowDefault = true)]IDiagnosticAnalyzerService analyzerService, + [Import(AllowDefault = true)]AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource) + { + // analyzerService, hostDiagnosticUpdateSource can be null in unit test + _analyzerServiceOpt = analyzerService; + _hostDiagnosticUpdateSourceOpt = hostDiagnosticUpdateSource; + } + + public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) + { + return new AnalyzerExecutor(_analyzerServiceOpt, _hostDiagnosticUpdateSourceOpt); + } + + private class AnalyzerExecutor : ICodeAnalysisDiagnosticAnalyzerExecutor + { + private readonly IDiagnosticAnalyzerService _analyzerServiceOpt; + private readonly AbstractHostDiagnosticUpdateSource _hostDiagnosticUpdateSourceOpt; + + // TODO: this should be removed once we move options down to compiler layer + private readonly ConcurrentDictionary> _lastOptionSetPerLanguage; + + public AnalyzerExecutor( + IDiagnosticAnalyzerService analyzerService, + AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource) + { + _analyzerServiceOpt = analyzerService; + _hostDiagnosticUpdateSourceOpt = hostDiagnosticUpdateSource; + + // currently option is a bit wierd since it is not part of snapshot and + // we can't load all options without loading all language specific dlls. + // we have tracking issue for this. + // https://github.com/dotnet/roslyn/issues/13643 + _lastOptionSetPerLanguage = new ConcurrentDictionary>(); + } + + public async Task> AnalyzeAsync(CompilationWithAnalyzers analyzerDriver, Project project, CancellationToken cancellationToken) + { + var service = project.Solution.Workspace.Services.GetService(); + if (service == null) + { + // host doesn't support RemoteHostService such as under unit test + return await AnalyzeInProcAsync(analyzerDriver, project, cancellationToken).ConfigureAwait(false); + } + + var remoteHostClient = await service.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + if (remoteHostClient == null) + { + // remote host is not running. this can happen if remote host is disabled. + return await AnalyzeInProcAsync(analyzerDriver, project, cancellationToken).ConfigureAwait(false); + } + + var outOfProcResult = await AnalyzeOutOfProcAsync(remoteHostClient, analyzerDriver, project, cancellationToken).ConfigureAwait(false); + + // make sure things are not cancelled + cancellationToken.ThrowIfCancellationRequested(); + + return DiagnosticAnalysisResultMap.Create(outOfProcResult.AnalysisResult, outOfProcResult.TelemetryInfo); + } + + private async Task> AnalyzeInProcAsync( + CompilationWithAnalyzers analyzerDriver, Project project, CancellationToken cancellationToken) + { + if (analyzerDriver.Analyzers.Length == 0) + { + // quick bail out + return DiagnosticAnalysisResultMap.Create(ImmutableDictionary.Empty, ImmutableDictionary.Empty); + } + + var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); + + // PERF: Run all analyzers at once using the new GetAnalysisResultAsync API. + var analysisResult = await analyzerDriver.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false); + + // get compiler result builder map + var builderMap = analysisResult.ToResultBuilderMap(project, version, analyzerDriver.Compilation, analyzerDriver.Analyzers, cancellationToken); + + return DiagnosticAnalysisResultMap.Create(builderMap.ToImmutableDictionary(kv => kv.Key, kv => new DiagnosticAnalysisResult(kv.Value)), analysisResult.AnalyzerTelemetryInfo); + } + + private async Task> AnalyzeOutOfProcAsync( + RemoteHostClient client, CompilationWithAnalyzers analyzerDriver, Project project, CancellationToken cancellationToken) + { + var solution = project.Solution; + var snapshotService = solution.Workspace.Services.GetService(); + + // TODO: this should be moved out + var analyzerMap = CreateAnalyzerMap(analyzerDriver.Analyzers); + if (analyzerMap.Count == 0) + { + return DiagnosticAnalysisResultMap.Create(ImmutableDictionary.Empty, ImmutableDictionary.Empty); + } + + var optionAsset = GetOptionsAsset(solution, project.Language, cancellationToken); + var hostChecksums = GetHostAnalyzerReferences(snapshotService, project.Language, _analyzerServiceOpt?.GetHostAnalyzerReferences(), cancellationToken); + + var argument = new DiagnosticArguments( + analyzerDriver.AnalysisOptions.ReportSuppressedDiagnostics, + analyzerDriver.AnalysisOptions.LogAnalyzerExecutionTime, + project.Id, optionAsset.Checksum, hostChecksums, analyzerMap.Keys.ToArray()); + + // TODO: send telemetry on session + using (var session = await client.CreateCodeAnalysisServiceSessionAsync(solution, cancellationToken).ConfigureAwait(false)) + { + session.AddAdditionalAssets(optionAsset); + + var result = await session.InvokeAsync( + WellKnownServiceHubServices.CodeAnalysisService_CalculateDiagnosticsAsync, + new object[] { argument }, + (s, c) => GetCompilerAnalysisResultAsync(s, analyzerMap, project, c)).ConfigureAwait(false); + + ReportAnalyzerExceptions(project, result.Exceptions); + + return result; + } + } + + private CustomAsset GetOptionsAsset(Solution solution, string language, CancellationToken cancellationToken) + { + // TODO: we need better way to deal with options. optionSet itself is green node but + // it is not part of snapshot and can't save option to solution since we can't use language + // specific option without loading related language specific dlls + var options = solution.Options; + + // we have cached options + if (_lastOptionSetPerLanguage.TryGetValue(language, out var value) && value.Item1 == options) + { + return value.Item2; + } + + // otherwise, we need to build one. + var assetBuilder = new CustomAssetBuilder(solution); + var asset = assetBuilder.Build(options, language, cancellationToken); + + _lastOptionSetPerLanguage[language] = ValueTuple.Create(options, asset); + return asset; + } + + private CompilationWithAnalyzers CreateAnalyzerDriver(CompilationWithAnalyzers analyzerDriver, Func predicate) + { + var analyzers = analyzerDriver.Analyzers.Where(predicate).ToImmutableArray(); + if (analyzers.Length == 0) + { + // we can't create analyzer driver with 0 analyzers + return null; + } + + return analyzerDriver.Compilation.WithAnalyzers(analyzers, analyzerDriver.AnalysisOptions); + } + + private async Task> GetCompilerAnalysisResultAsync(Stream stream, Dictionary analyzerMap, Project project, CancellationToken cancellationToken) + { + // handling of cancellation and exception + var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); + + using (var reader = StreamObjectReader.TryGetReader(stream)) + { + Debug.Assert(reader != null, + @"We only ge a reader for data transmitted between live processes. +This data should always be correct as we're never persisting the data between sessions."); + return DiagnosticResultSerializer.Deserialize(reader, analyzerMap, project, version, cancellationToken); + } + } + + private void ReportAnalyzerExceptions(Project project, ImmutableDictionary> exceptions) + { + foreach (var kv in exceptions) + { + var analyzer = kv.Key; + foreach (var diagnostic in kv.Value) + { + _hostDiagnosticUpdateSourceOpt?.ReportAnalyzerDiagnostic(analyzer, diagnostic, project); + } + } + } + + private ImmutableArray GetHostAnalyzerReferences( + ISolutionSynchronizationService snapshotService, string language, IEnumerable references, CancellationToken cancellationToken) + { + if (references == null) + { + return ImmutableArray.Empty; + } + + // TODO: cache this to somewhere + var builder = ImmutableArray.CreateBuilder(); + foreach (var reference in references) + { + var analyzers = reference.GetAnalyzers(language); + if (analyzers.Length == 0) + { + // skip reference that doesn't contain any analyzers for the given language + // we do this so that we don't load analyzer dlls that MEF exported from vsix + // not related to this solution + continue; + } + + var asset = snapshotService.GetGlobalAsset(reference, cancellationToken); + builder.Add(asset.Checksum); + } + + return builder.ToImmutable(); + } + + private Dictionary CreateAnalyzerMap(IEnumerable analyzers) + { + // TODO: this needs to be cached. we can have 300+ analyzers + return analyzers.ToDictionary(a => a.GetAnalyzerIdAndVersion().Item1, a => a); + } + } + } +} diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/InProcCodeAnalysisDiagnosticAnalyzerExecutor.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/InProcCodeAnalysisDiagnosticAnalyzerExecutor.cs deleted file mode 100644 index 46475862261..00000000000 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/InProcCodeAnalysisDiagnosticAnalyzerExecutor.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Immutable; -using System.Composition; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics.Telemetry; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Workspaces.Diagnostics; - -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 -{ - [ExportWorkspaceServiceFactory(typeof(ICodeAnalysisDiagnosticAnalyzerExecutor)), Shared] - internal class InProcCodeAnalysisDiagnosticAnalyzerExecutor : IWorkspaceServiceFactory - { - public static readonly ICodeAnalysisDiagnosticAnalyzerExecutor Instance = new AnalyzerExecutor(); - - public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - { - return Instance; - } - - private class AnalyzerExecutor : ICodeAnalysisDiagnosticAnalyzerExecutor - { - public async Task> AnalyzeAsync(CompilationWithAnalyzers analyzerDriver, Project project, CancellationToken cancellationToken) - { - if (analyzerDriver.Analyzers.Length == 0) - { - // quick bail out - return DiagnosticAnalysisResultMap.Create(ImmutableDictionary.Empty, ImmutableDictionary.Empty); - } - - var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - - // PERF: Run all analyzers at once using the new GetAnalysisResultAsync API. - var analysisResult = await analyzerDriver.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false); - - // get compiler result builder map - var builderMap = analysisResult.ToResultBuilderMap(project, version, analyzerDriver.Compilation, analyzerDriver.Analyzers, cancellationToken); - - return DiagnosticAnalysisResultMap.Create(builderMap.ToImmutableDictionary(kv => kv.Key, kv => new DiagnosticAnalysisResult(kv.Value)), analysisResult.AnalyzerTelemetryInfo); - } - } - } -} diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index dbb49d31316..fa7a2c19200 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -114,6 +114,8 @@ + + @@ -327,7 +329,7 @@ - + diff --git a/src/VisualStudio/Core/Def/Implementation/Diagnostics/VisualStudioDiagnosticAnalyzerExecutor.cs b/src/VisualStudio/Core/Def/Implementation/Diagnostics/VisualStudioDiagnosticAnalyzerExecutor.cs deleted file mode 100644 index 05326a6fa1e..00000000000 --- a/src/VisualStudio/Core/Def/Implementation/Diagnostics/VisualStudioDiagnosticAnalyzerExecutor.cs +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Composition; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Diagnostics.EngineV2; -using Microsoft.CodeAnalysis.Diagnostics.Telemetry; -using Microsoft.CodeAnalysis.Execution; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Remote; -using Microsoft.CodeAnalysis.Remote.Diagnostics; -using Microsoft.CodeAnalysis.Workspaces.Diagnostics; -using Roslyn.Utilities; - -namespace Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics -{ - [ExportWorkspaceService(typeof(ICodeAnalysisDiagnosticAnalyzerExecutor), layer: ServiceLayer.Host), Shared] - internal class VisualStudioDiagnosticAnalyzerExecutor : ICodeAnalysisDiagnosticAnalyzerExecutor - { - private readonly IDiagnosticAnalyzerService _analyzerService; - private readonly AbstractHostDiagnosticUpdateSource _hostDiagnosticUpdateSource; - - // TODO: this should be removed once we move options down to compiler layer - private readonly ConcurrentDictionary> _lastOptionSetPerLanguage; - - [ImportingConstructor] - public VisualStudioDiagnosticAnalyzerExecutor( - IDiagnosticAnalyzerService analyzerService, - AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource) - { - _analyzerService = analyzerService; - _hostDiagnosticUpdateSource = hostDiagnosticUpdateSource; - - // currently option is a bit wierd since it is not part of snapshot and - // we can't load all options without loading all language specific dlls. - // we have tracking issue for this. - // https://github.com/dotnet/roslyn/issues/13643 - _lastOptionSetPerLanguage = new ConcurrentDictionary>(); - } - - public async Task> AnalyzeAsync(CompilationWithAnalyzers analyzerDriver, Project project, CancellationToken cancellationToken) - { - var remoteHostClient = await project.Solution.Workspace.Services.GetService().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); - if (remoteHostClient == null) - { - // remote host is not running. this can happen if remote host is disabled. - return await InProcCodeAnalysisDiagnosticAnalyzerExecutor.Instance.AnalyzeAsync(analyzerDriver, project, cancellationToken).ConfigureAwait(false); - } - - var outOfProcResult = await AnalyzeOutOfProcAsync(remoteHostClient, analyzerDriver, project, cancellationToken).ConfigureAwait(false); - - // make sure things are not cancelled - cancellationToken.ThrowIfCancellationRequested(); - - return DiagnosticAnalysisResultMap.Create(outOfProcResult.AnalysisResult, outOfProcResult.TelemetryInfo); - } - - private async Task> AnalyzeOutOfProcAsync( - RemoteHostClient client, CompilationWithAnalyzers analyzerDriver, Project project, CancellationToken cancellationToken) - { - var solution = project.Solution; - var snapshotService = solution.Workspace.Services.GetService(); - - // TODO: this should be moved out - var analyzerMap = CreateAnalyzerMap(analyzerDriver.Analyzers); - if (analyzerMap.Count == 0) - { - return DiagnosticAnalysisResultMap.Create(ImmutableDictionary.Empty, ImmutableDictionary.Empty); - } - - var optionAsset = GetOptionsAsset(solution, project.Language, cancellationToken); - var hostChecksums = GetHostAnalyzerReferences(snapshotService, project.Language, _analyzerService.GetHostAnalyzerReferences(), cancellationToken); - - var argument = new DiagnosticArguments( - analyzerDriver.AnalysisOptions.ReportSuppressedDiagnostics, - analyzerDriver.AnalysisOptions.LogAnalyzerExecutionTime, - project.Id, optionAsset.Checksum, hostChecksums, analyzerMap.Keys.ToArray()); - - // TODO: send telemetry on session - using (var session = await client.CreateCodeAnalysisServiceSessionAsync(solution, cancellationToken).ConfigureAwait(false)) - { - session.AddAdditionalAssets(optionAsset); - - var result = await session.InvokeAsync( - WellKnownServiceHubServices.CodeAnalysisService_CalculateDiagnosticsAsync, - new object[] { argument }, - (s, c) => GetCompilerAnalysisResultAsync(s, analyzerMap, project, c)).ConfigureAwait(false); - - ReportAnalyzerExceptions(project, result.Exceptions); - - return result; - } - } - - private CustomAsset GetOptionsAsset(Solution solution, string language, CancellationToken cancellationToken) - { - // TODO: we need better way to deal with options. optionSet itself is green node but - // it is not part of snapshot and can't save option to solution since we can't use language - // specific option without loading related language specific dlls - var options = solution.Options; - - // we have cached options - if (_lastOptionSetPerLanguage.TryGetValue(language, out var value) && value.Item1 == options) - { - return value.Item2; - } - - // otherwise, we need to build one. - var assetBuilder = new CustomAssetBuilder(solution); - var asset = assetBuilder.Build(options, language, cancellationToken); - - _lastOptionSetPerLanguage[language] = ValueTuple.Create(options, asset); - return asset; - } - - private CompilationWithAnalyzers CreateAnalyzerDriver(CompilationWithAnalyzers analyzerDriver, Func predicate) - { - var analyzers = analyzerDriver.Analyzers.Where(predicate).ToImmutableArray(); - if (analyzers.Length == 0) - { - // we can't create analyzer driver with 0 analyzers - return null; - } - - return analyzerDriver.Compilation.WithAnalyzers(analyzers, analyzerDriver.AnalysisOptions); - } - - private async Task> GetCompilerAnalysisResultAsync(Stream stream, Dictionary analyzerMap, Project project, CancellationToken cancellationToken) - { - // handling of cancellation and exception - var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - - using (var reader = StreamObjectReader.TryGetReader(stream)) - { - Debug.Assert(reader != null, -@"We only ge a reader for data transmitted between live processes. -This data should always be correct as we're never persisting the data between sessions."); - return DiagnosticResultSerializer.Deserialize(reader, analyzerMap, project, version, cancellationToken); - } - } - - private void ReportAnalyzerExceptions(Project project, ImmutableDictionary> exceptions) - { - foreach (var kv in exceptions) - { - var analyzer = kv.Key; - foreach (var diagnostic in kv.Value) - { - _hostDiagnosticUpdateSource.ReportAnalyzerDiagnostic(analyzer, diagnostic, project); - } - } - } - - private ImmutableArray GetHostAnalyzerReferences( - ISolutionSynchronizationService snapshotService, string language, IEnumerable references, CancellationToken cancellationToken) - { - // TODO: cache this to somewhere - var builder = ImmutableArray.CreateBuilder(); - foreach (var reference in references) - { - var analyzers = reference.GetAnalyzers(language); - if (analyzers.Length == 0) - { - // skip reference that doesn't contain any analyzers for the given language - // we do this so that we don't load analyzer dlls that MEF exported from vsix - // not related to this solution - continue; - } - - var asset = snapshotService.GetGlobalAsset(reference, cancellationToken); - builder.Add(asset.Checksum); - } - - return builder.ToImmutable(); - } - - private Dictionary CreateAnalyzerMap(IEnumerable analyzers) - { - // TODO: this needs to be cached. we can have 300+ analyzers - return analyzers.ToDictionary(a => a.GetAnalyzerIdAndVersion().Item1, a => a); - } - } -} diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index 5a40d68de05..44c29fd1b44 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -47,12 +47,6 @@ InternalUtilities\ShadowCopyAnalyzerAssemblyLoader.cs - - Implementation\Diagnostics\DiagnosticArguments.cs - - - Implementation\Diagnostics\DiagnosticResultSerializer.cs - @@ -64,7 +58,6 @@ - @@ -704,4 +697,4 @@ - + \ No newline at end of file diff --git a/src/VisualStudio/Core/Test.Next/Remote/JsonConverterTests.cs b/src/VisualStudio/Core/Test.Next/Remote/JsonConverterTests.cs index c1a5b34c202..220847fa780 100644 --- a/src/VisualStudio/Core/Test.Next/Remote/JsonConverterTests.cs +++ b/src/VisualStudio/Core/Test.Next/Remote/JsonConverterTests.cs @@ -7,7 +7,7 @@ using System.IO; using System.Linq; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; using Newtonsoft.Json; using Roslyn.Test.Utilities; @@ -44,7 +44,7 @@ public void TestDocumentId() [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] public void TestDiagnosticArguments() { - var arguments = new hub::Microsoft.CodeAnalysis.Remote.Diagnostics.DiagnosticArguments( + var arguments = new DiagnosticArguments( reportSuppressedDiagnostics: true, logAnalyzerExecutionTime: false, projectId: ProjectId.CreateNewId("project"), diff --git a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs index 39a18887013..ff6ebdf0e80 100644 --- a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -12,13 +11,13 @@ using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Diagnostics.EngineV2; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Execution; using Microsoft.CodeAnalysis.Shared.Options; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic.UseNullPropagation; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; -using Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; using Microsoft.VisualStudio.LanguageServices.Remote; using Moq; using Roslyn.Test.Utilities; @@ -155,7 +154,7 @@ void Method() // run analysis var project = workspace.CurrentSolution.Projects.First(); - var executor = new VisualStudioDiagnosticAnalyzerExecutor(mockAnalyzerService, new MyUpdateSource(workspace)); + var executor = (ICodeAnalysisDiagnosticAnalyzerExecutor)new DiagnosticAnalyzerExecutor(mockAnalyzerService, new MyUpdateSource(workspace)).CreateService(workspace.Services); var analyzerDriver = (await project.GetCompilationAsync()).WithAnalyzers(analyzerReference.GetAnalyzers(project.Language).Where(a => a.GetType() == analyzerType).ToImmutableArray()); var result = await executor.AnalyzeAsync(analyzerDriver, project, CancellationToken.None); @@ -170,7 +169,7 @@ void Method() private static async Task AnalyzeAsync(TestWorkspace workspace, ProjectId projectId, Type analyzerType, CancellationToken cancellationToken = default(CancellationToken)) { var diagnosticService = workspace.ExportProvider.GetExportedValue(); - var executor = new VisualStudioDiagnosticAnalyzerExecutor(diagnosticService, new MyUpdateSource(workspace)); + var executor = (ICodeAnalysisDiagnosticAnalyzerExecutor)new DiagnosticAnalyzerExecutor(diagnosticService, new MyUpdateSource(workspace)).CreateService(workspace.Services); var analyzerReference = new AnalyzerFileReference(analyzerType.Assembly.Location, new TestAnalyzerAssemblyLoader()); var project = workspace.CurrentSolution.GetProject(projectId).AddAnalyzerReference(analyzerReference); diff --git a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj index 294cee95432..8183b71e689 100644 --- a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj +++ b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj @@ -60,8 +60,6 @@ - - -- GitLab