From 09099a7649b87daecac25d8f16cd319933b65181 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 20 Sep 2023 15:49:11 -0500 Subject: [PATCH 1/2] Create export provider asynchronously --- .../AnalyzerTest`1.cs | 28 +++---------- .../ExportProviderFactory.cs | 41 +++++++++++++++++++ .../PublicAPI.Unshipped.txt | 4 +- 3 files changed, 49 insertions(+), 24 deletions(-) create mode 100644 src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/ExportProviderFactory.cs diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs index c511f1acd7..b5089ffe8f 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs @@ -32,25 +32,8 @@ namespace Microsoft.CodeAnalysis.Testing public abstract class AnalyzerTest where TVerifier : IVerifier, new() { - private static readonly Lazy ExportProviderFactory; private static readonly ConditionalWeakTable NonLocalDiagnostics = new ConditionalWeakTable(); - static AnalyzerTest() - { - ExportProviderFactory = new Lazy( - () => - { - var discovery = new AttributedPartDiscovery(Resolver.DefaultInstance, isNonPublicSupported: true); - var parts = Task.Run(() => discovery.CreatePartsAsync(MefHostServices.DefaultAssemblies)).GetAwaiter().GetResult(); - var catalog = ComposableCatalog.Create(Resolver.DefaultInstance).AddParts(parts).WithDocumentTextDifferencingService(); - - var configuration = CompositionConfiguration.Create(catalog); - var runtimeComposition = RuntimeComposition.CreateRuntimeComposition(configuration); - return runtimeComposition.CreateExportProviderFactory(); - }, - LazyThreadSafetyMode.ExecutionAndPublication); - } - /// /// Gets the default verifier for the test. /// @@ -1587,7 +1570,7 @@ protected virtual async Task CreateSolutionAsync(ProjectId projectId, var parseOptions = CreateParseOptions() .WithDocumentationMode(projectState.DocumentationMode); - var workspace = CreateWorkspace(); + var workspace = await CreateWorkspaceAsync().ConfigureAwait(false); foreach (var transform in OptionsTransforms) { workspace.Options = transform(workspace.Options); @@ -1660,16 +1643,17 @@ protected virtual Project ApplyCompilationOptions(Project project) return solution.GetProject(project.Id); } - public Workspace CreateWorkspace() + public async Task CreateWorkspaceAsync() { - var workspace = CreateWorkspaceImpl(); + var workspace = await CreateWorkspaceImplAsync().ConfigureAwait(false); _workspaces.Add(workspace); return workspace; } - protected virtual Workspace CreateWorkspaceImpl() + protected virtual async Task CreateWorkspaceImplAsync() { - var exportProvider = ExportProviderFactory.Value.CreateExportProvider(); + var exportProviderFactory = await ExportProviderFactory.GetOrCreateExportProviderFactoryAsync().ConfigureAwait(false); + var exportProvider = exportProviderFactory.CreateExportProvider(); var host = MefHostServices.Create(exportProvider.AsCompositionContext()); return new AdhocWorkspace(host); } diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/ExportProviderFactory.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/ExportProviderFactory.cs new file mode 100644 index 0000000000..ac60a89b1d --- /dev/null +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/ExportProviderFactory.cs @@ -0,0 +1,41 @@ +// 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.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.VisualStudio.Composition; + +namespace Microsoft.CodeAnalysis.Testing +{ + internal static class ExportProviderFactory + { + private static readonly object s_lock = new(); + private static Task? s_exportProviderFactory; + + public static Task GetOrCreateExportProviderFactoryAsync() + { + if (s_exportProviderFactory is { } exportProviderFactory) + { + return exportProviderFactory; + } + + lock (s_lock) + { + s_exportProviderFactory ??= Task.Run(CreateExportProviderFactorySlowAsync); + return s_exportProviderFactory; + } + + static async Task CreateExportProviderFactorySlowAsync() + { + var discovery = new AttributedPartDiscovery(Resolver.DefaultInstance, isNonPublicSupported: true); + var parts = await discovery.CreatePartsAsync(MefHostServices.DefaultAssemblies).ConfigureAwait(false); + var catalog = ComposableCatalog.Create(Resolver.DefaultInstance).AddParts(parts).WithDocumentTextDifferencingService(); + + var configuration = CompositionConfiguration.Create(catalog); + var runtimeComposition = RuntimeComposition.CreateRuntimeComposition(configuration); + return runtimeComposition.CreateExportProviderFactory(); + } + } + } +} diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt index ed5558e972..7909cfb31a 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt @@ -3,7 +3,7 @@ Microsoft.CodeAnalysis.Testing.AnalyzerTest.AnalyzerTest() -> void Microsoft.CodeAnalysis.Testing.AnalyzerTest.CompilerDiagnostics.get -> Microsoft.CodeAnalysis.Testing.CompilerDiagnostics Microsoft.CodeAnalysis.Testing.AnalyzerTest.CompilerDiagnostics.set -> void Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateProjectAsync(Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState primaryProject, System.Collections.Immutable.ImmutableArray additionalProjects, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateWorkspace() -> Microsoft.CodeAnalysis.Workspace +Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateWorkspaceAsync() -> System.Threading.Tasks.Task Microsoft.CodeAnalysis.Testing.AnalyzerTest.DiagnosticVerifier.get -> System.Action Microsoft.CodeAnalysis.Testing.AnalyzerTest.DiagnosticVerifier.set -> void Microsoft.CodeAnalysis.Testing.AnalyzerTest.DisabledDiagnostics.get -> System.Collections.Generic.List @@ -349,7 +349,7 @@ virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.ApplyCompilationO virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateCompilationWithAnalyzers(Microsoft.CodeAnalysis.Compilation compilation, System.Collections.Immutable.ImmutableArray analyzers, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateProjectImplAsync(Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState primaryProject, System.Collections.Immutable.ImmutableArray additionalProjects, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateSolutionAsync(Microsoft.CodeAnalysis.ProjectId projectId, Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState projectState, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateWorkspaceImpl() -> Microsoft.CodeAnalysis.Workspace +virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateWorkspaceImplAsync() -> System.Threading.Tasks.Task virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.DefaultFilePath.get -> string virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.DefaultFilePathPrefix.get -> string virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.DefaultTestProjectName.get -> string From c5d471ab7d9c4c8ba87bb911022baa2b6e70b2ba Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 22 Sep 2023 15:34:13 -0500 Subject: [PATCH 2/2] Retain old APIs with ObsoleteAttribute to help users update --- .../Directory.Build.props | 2 +- .../AnalyzerTest`1.cs | 11 +++++++++++ .../PublicAPI.Unshipped.txt | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.CodeAnalysis.Testing/Directory.Build.props b/src/Microsoft.CodeAnalysis.Testing/Directory.Build.props index e0430e7607..3d5c9b2922 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Directory.Build.props +++ b/src/Microsoft.CodeAnalysis.Testing/Directory.Build.props @@ -34,7 +34,7 @@ - 9 + 10 diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs index b5089ffe8f..81f4bebe33 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs @@ -6,6 +6,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; +using System.ComponentModel; using System.IO; using System.Linq; using System.Reflection; @@ -1643,6 +1644,16 @@ protected virtual Project ApplyCompilationOptions(Project project) return solution.GetProject(project.Id); } + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete($"Use {nameof(CreateWorkspaceAsync)} instead. https://github.com/dotnet/roslyn-sdk/pull/1120", error: true)] + public Workspace CreateWorkspace() + => throw new NotSupportedException(); + + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete($"Use {nameof(CreateWorkspaceImplAsync)} instead. https://github.com/dotnet/roslyn-sdk/pull/1120", error: true)] + protected virtual Workspace CreateWorkspaceImpl() + => throw new NotSupportedException(); + public async Task CreateWorkspaceAsync() { var workspace = await CreateWorkspaceImplAsync().ConfigureAwait(false); diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt index 7909cfb31a..4d04de1541 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt @@ -3,6 +3,7 @@ Microsoft.CodeAnalysis.Testing.AnalyzerTest.AnalyzerTest() -> void Microsoft.CodeAnalysis.Testing.AnalyzerTest.CompilerDiagnostics.get -> Microsoft.CodeAnalysis.Testing.CompilerDiagnostics Microsoft.CodeAnalysis.Testing.AnalyzerTest.CompilerDiagnostics.set -> void Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateProjectAsync(Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState primaryProject, System.Collections.Immutable.ImmutableArray additionalProjects, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateWorkspace() -> Microsoft.CodeAnalysis.Workspace Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateWorkspaceAsync() -> System.Threading.Tasks.Task Microsoft.CodeAnalysis.Testing.AnalyzerTest.DiagnosticVerifier.get -> System.Action Microsoft.CodeAnalysis.Testing.AnalyzerTest.DiagnosticVerifier.set -> void @@ -349,6 +350,7 @@ virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.ApplyCompilationO virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateCompilationWithAnalyzers(Microsoft.CodeAnalysis.Compilation compilation, System.Collections.Immutable.ImmutableArray analyzers, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateProjectImplAsync(Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState primaryProject, System.Collections.Immutable.ImmutableArray additionalProjects, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateSolutionAsync(Microsoft.CodeAnalysis.ProjectId projectId, Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState projectState, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateWorkspaceImpl() -> Microsoft.CodeAnalysis.Workspace virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateWorkspaceImplAsync() -> System.Threading.Tasks.Task virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.DefaultFilePath.get -> string virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.DefaultFilePathPrefix.get -> string