diff --git a/eng/Versions.props b/eng/Versions.props index c8deabcd0bb4d..6c648e85f4e34 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -173,8 +173,8 @@ 12.0.30110 12.1.30328 16.0.0 - 16.7.53 - 16.6.13 + 16.7.54 + 16.7.54 16.6.29925.50 15.5.31 2.8.0 @@ -246,7 +246,7 @@ create a test insertion in Visual Studio to validate. --> 12.0.2 - 2.6.21-alpha + 2.6.86-alpha 5.0.0-preview.8.20407.11 5.0.0-preview.8.20407.11 1.1.1 diff --git a/eng/targets/Services.props b/eng/targets/Services.props index 0c216727f9b27..972b09b076ea1 100644 --- a/eng/targets/Services.props +++ b/eng/targets/Services.props @@ -11,15 +11,26 @@ Note that brokered services must be defined in Microsoft.VisualStudio service namespace in order to be considered first party. --> - - + + + + + + + + + + + + + - - + + diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerTelemetry.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerTelemetry.cs index 1e4968e80cc1d..62eca7c0b1910 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerTelemetry.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerTelemetry.cs @@ -5,114 +5,136 @@ #nullable enable using System; +using System.Runtime.Serialization; namespace Microsoft.CodeAnalysis.Diagnostics.Telemetry { /// /// Contains telemetry info for a specific analyzer, such as count of registered actions, the total execution time, etc. /// + [DataContract] public sealed class AnalyzerTelemetryInfo { /// /// Count of registered compilation start actions. /// - public int CompilationStartActionsCount { get; set; } = 0; + [DataMember(Order = 0)] + public int CompilationStartActionsCount { get; set; } /// /// Count of registered compilation end actions. /// - public int CompilationEndActionsCount { get; set; } = 0; + [DataMember(Order = 1)] + public int CompilationEndActionsCount { get; set; } /// /// Count of registered compilation actions. /// - public int CompilationActionsCount { get; set; } = 0; + [DataMember(Order = 2)] + public int CompilationActionsCount { get; set; } /// /// Count of registered syntax tree actions. /// - public int SyntaxTreeActionsCount { get; set; } = 0; + [DataMember(Order = 3)] + public int SyntaxTreeActionsCount { get; set; } /// /// Count of registered additional file actions. /// - public int AdditionalFileActionsCount { get; set; } = 0; + [DataMember(Order = 4)] + public int AdditionalFileActionsCount { get; set; } /// /// Count of registered semantic model actions. /// - public int SemanticModelActionsCount { get; set; } = 0; + [DataMember(Order = 5)] + public int SemanticModelActionsCount { get; set; } /// /// Count of registered symbol actions. /// - public int SymbolActionsCount { get; set; } = 0; + [DataMember(Order = 6)] + public int SymbolActionsCount { get; set; } /// /// Count of registered symbol start actions. /// - public int SymbolStartActionsCount { get; set; } = 0; + [DataMember(Order = 7)] + public int SymbolStartActionsCount { get; set; } /// /// Count of registered symbol end actions. /// - public int SymbolEndActionsCount { get; set; } = 0; + [DataMember(Order = 8)] + public int SymbolEndActionsCount { get; set; } /// /// Count of registered syntax node actions. /// - public int SyntaxNodeActionsCount { get; set; } = 0; + [DataMember(Order = 9)] + public int SyntaxNodeActionsCount { get; set; } /// /// Count of registered code block start actions. /// - public int CodeBlockStartActionsCount { get; set; } = 0; + [DataMember(Order = 10)] + public int CodeBlockStartActionsCount { get; set; } /// /// Count of registered code block end actions. /// - public int CodeBlockEndActionsCount { get; set; } = 0; + [DataMember(Order = 11)] + public int CodeBlockEndActionsCount { get; set; } /// /// Count of registered code block actions. /// - public int CodeBlockActionsCount { get; set; } = 0; + [DataMember(Order = 12)] + public int CodeBlockActionsCount { get; set; } /// /// Count of registered operation actions. /// - public int OperationActionsCount { get; set; } = 0; + [DataMember(Order = 13)] + public int OperationActionsCount { get; set; } /// /// Count of registered operation block start actions. /// - public int OperationBlockStartActionsCount { get; set; } = 0; + [DataMember(Order = 14)] + public int OperationBlockStartActionsCount { get; set; } /// /// Count of registered operation block end actions. /// - public int OperationBlockEndActionsCount { get; set; } = 0; + [DataMember(Order = 15)] + public int OperationBlockEndActionsCount { get; set; } /// /// Count of registered operation block actions. /// - public int OperationBlockActionsCount { get; set; } = 0; + [DataMember(Order = 16)] + public int OperationBlockActionsCount { get; set; } /// /// Count of registered suppression actions. /// This is the same as count of s as each suppressor /// has a single suppression action, i.e. . /// - public int SuppressionActionsCount { get; set; } = 0; + [DataMember(Order = 17)] + public int SuppressionActionsCount { get; set; } /// /// Total execution time. /// + [DataMember(Order = 18)] public TimeSpan ExecutionTime { get; set; } = TimeSpan.Zero; /// /// Gets a value indicating whether the analyzer supports concurrent execution. /// + [DataMember(Order = 19)] public bool Concurrent { get; set; } internal AnalyzerTelemetryInfo(AnalyzerActionCounts actionCounts, int suppressionActionCounts, TimeSpan executionTime) diff --git a/src/Compilers/Core/Portable/Text/TextChange.cs b/src/Compilers/Core/Portable/Text/TextChange.cs index cff2b5041ce61..6da2b595ecbb4 100644 --- a/src/Compilers/Core/Portable/Text/TextChange.cs +++ b/src/Compilers/Core/Portable/Text/TextChange.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -15,16 +16,19 @@ namespace Microsoft.CodeAnalysis.Text /// /// Describes a single change when a particular span is replaced with a new text. /// + [DataContract] public readonly struct TextChange : IEquatable { /// /// The original span of the changed text. /// + [DataMember(Order = 0)] public TextSpan Span { get; } /// /// The new text. /// + [DataMember(Order = 1)] public string? NewText { get; } /// diff --git a/src/Compilers/Core/Portable/Text/TextSpan.cs b/src/Compilers/Core/Portable/Text/TextSpan.cs index fed431a15dad0..f76f2e587b9c2 100644 --- a/src/Compilers/Core/Portable/Text/TextSpan.cs +++ b/src/Compilers/Core/Portable/Text/TextSpan.cs @@ -5,6 +5,7 @@ #nullable enable using System; +using System.Runtime.Serialization; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Text @@ -13,6 +14,7 @@ namespace Microsoft.CodeAnalysis.Text /// Immutable abstract representation of a span of text. For example, in an error diagnostic that reports a /// location, it could come from a parsed string, text from a tool editor buffer, etc. /// + [DataContract] public readonly struct TextSpan : IEquatable, IComparable { /// @@ -38,6 +40,7 @@ public TextSpan(int start, int length) /// /// Start point of the span. /// + [DataMember(Order = 0)] public int Start { get; } /// @@ -48,6 +51,7 @@ public TextSpan(int start, int length) /// /// Length of the span. /// + [DataMember(Order = 1)] public int Length { get; } /// diff --git a/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesLSPService.cs b/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesLSPService.cs index b513b76c16ef0..6e36565be9d8b 100644 --- a/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesLSPService.cs +++ b/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesLSPService.cs @@ -15,8 +15,7 @@ internal class CSharpFindUsagesLSPService : AbstractFindUsagesService { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpFindUsagesLSPService(IThreadingContext threadingContext) - : base(threadingContext) + public CSharpFindUsagesLSPService() { } } diff --git a/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesService.cs b/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesService.cs index f4f1ce49b1936..a56394135ec9d 100644 --- a/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesService.cs +++ b/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesService.cs @@ -17,8 +17,7 @@ internal class CSharpFindUsagesService : AbstractFindUsagesService { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpFindUsagesService(IThreadingContext threadingContext) - : base(threadingContext) + public CSharpFindUsagesService() { } } diff --git a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingNuGetTests.cs b/src/EditorFeatures/CSharpTest/AddUsing/AddUsingNuGetTests.cs index 3f18c3565f2b2..fe8be5b6336f7 100644 --- a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingNuGetTests.cs +++ b/src/EditorFeatures/CSharpTest/AddUsing/AddUsingNuGetTests.cs @@ -62,7 +62,7 @@ public async Task TestSearchPackageSingleName() var packageServiceMock = new Mock(MockBehavior.Strict); packageServiceMock.Setup(s => s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny())) - .Returns(Task.FromResult>(new List())); + .Returns(new ValueTask>(ImmutableArray.Empty)); packageServiceMock.Setup(s => s.FindPackagesWithTypeAsync( NugetOrgSource, "NuGetType", 0, It.IsAny())) .Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NuGetNamespace"))); @@ -93,7 +93,7 @@ public async Task TestSearchPackageMultipleNames() var packageServiceMock = new Mock(MockBehavior.Strict); packageServiceMock.Setup(s => s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny())) - .Returns(Task.FromResult>(new List())); + .Returns(new ValueTask>(ImmutableArray.Empty)); packageServiceMock.Setup(s => s.FindPackagesWithTypeAsync( NugetOrgSource, "NuGetType", 0, It.IsAny())) .Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NS1", "NS2"))); @@ -122,7 +122,7 @@ public async Task TestMissingIfPackageAlreadyInstalled() var packageServiceMock = new Mock(MockBehavior.Strict); packageServiceMock.Setup(s => s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny())) - .Returns(Task.FromResult>(new List())); + .Returns(new ValueTask>(ImmutableArray.Empty)); packageServiceMock.Setup(s => s.FindPackagesWithTypeAsync( NugetOrgSource, "NuGetType", 0, It.IsAny())) .Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NS1", "NS2"))); @@ -148,7 +148,7 @@ public async Task TestOptionsOffered() var packageServiceMock = new Mock(MockBehavior.Strict); packageServiceMock.Setup(s => s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny())) - .Returns(Task.FromResult>(new List())); + .Returns(new ValueTask>(ImmutableArray.Empty)); packageServiceMock.Setup(s => s.FindPackagesWithTypeAsync( NugetOrgSource, "NuGetType", 0, It.IsAny())) .Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NS1", "NS2"))); @@ -192,7 +192,7 @@ public async Task TestInstallGetsCalledNoVersion() var packageServiceMock = new Mock(MockBehavior.Strict); packageServiceMock.Setup(s => s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny())) - .Returns(Task.FromResult>(new List())); + .Returns(new ValueTask>(ImmutableArray.Empty)); packageServiceMock.Setup(s => s.FindPackagesWithTypeAsync( NugetOrgSource, "NuGetType", 0, It.IsAny())) .Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NuGetNamespace"))); @@ -226,7 +226,7 @@ public async Task TestInstallGetsCalledWithVersion() var packageServiceMock = new Mock(MockBehavior.Strict); packageServiceMock.Setup(s => s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny())) - .Returns(Task.FromResult>(new List())); + .Returns(new ValueTask>(ImmutableArray.Empty)); packageServiceMock.Setup(s => s.FindPackagesWithTypeAsync(NugetOrgSource, "NuGetType", 0, It.IsAny())) .Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NuGetNamespace"))); @@ -260,7 +260,7 @@ public async Task TestFailedInstallRollsBackFile() var packageServiceMock = new Mock(MockBehavior.Strict); packageServiceMock.Setup(s => s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny())) - .Returns(Task.FromResult>(new List())); + .Returns(new ValueTask>(ImmutableArray.Empty)); packageServiceMock.Setup(s => s.FindPackagesWithTypeAsync(NugetOrgSource, "NuGetType", 0, It.IsAny())) .Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NuGetNamespace"))); @@ -276,17 +276,18 @@ await TestInRegularAndScriptAsync( installerServiceMock.Verify(); } - private static Task> CreateSearchResult( + private static ValueTask> CreateSearchResult( string packageName, string typeName, ImmutableArray containingNamespaceNames) { return CreateSearchResult(new PackageWithTypeResult( - packageName: packageName, typeName: typeName, version: null, - rank: 0, containingNamespaceNames: containingNamespaceNames)); + packageName: packageName, rank: 0, typeName: typeName, + version: null, containingNamespaceNames: containingNamespaceNames)); } - private static Task> CreateSearchResult(params PackageWithTypeResult[] results) - => Task.FromResult>(ImmutableArray.Create(results)); + private static ValueTask> CreateSearchResult(params PackageWithTypeResult[] results) + => new(ImmutableArray.Create(results)); - private static ImmutableArray CreateNameParts(params string[] parts) => parts.ToImmutableArray(); + private static ImmutableArray CreateNameParts(params string[] parts) + => parts.ToImmutableArray(); } } diff --git a/src/EditorFeatures/CSharpTest/CodeActions/Preview/PreviewExceptionTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/Preview/PreviewExceptionTests.cs index a9c7b412365df..8acf07e2d45a8 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/Preview/PreviewExceptionTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/Preview/PreviewExceptionTests.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Extensions; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Roslyn.Utilities; @@ -25,21 +26,40 @@ public partial class PreviewTests public async Task TestExceptionInComputePreview() { using var workspace = CreateWorkspaceFromOptions("class D {}", new TestParameters()); + + var errorReportingService = (TestErrorReportingService)workspace.Services.GetRequiredService(); + var errorReported = false; + errorReportingService.OnError = message => errorReported = true; + await GetPreview(workspace, new ErrorCases.ExceptionInCodeAction()); + Assert.True(errorReported); } [WpfFact] public void TestExceptionInDisplayText() { using var workspace = CreateWorkspaceFromOptions("class D {}", new TestParameters()); + + var errorReportingService = (TestErrorReportingService)workspace.Services.GetRequiredService(); + var errorReported = false; + errorReportingService.OnError = message => errorReported = true; + DisplayText(workspace, new ErrorCases.ExceptionInCodeAction()); + Assert.True(errorReported); } [WpfFact] public async Task TestExceptionInActionSets() { using var workspace = CreateWorkspaceFromOptions("class D {}", new TestParameters()); + + var errorReportingService = (TestErrorReportingService)workspace.Services.GetRequiredService(); + var errorReported = false; + errorReportingService.OnError = message => errorReported = true; + await ActionSets(workspace, new ErrorCases.ExceptionInCodeAction()); + + Assert.True(errorReported); } private static async Task GetPreview(TestWorkspace workspace, CodeRefactoringProvider provider) diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.DefinitionTrackingContext.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.DefinitionTrackingContext.cs index 11ce232700ed1..d015f0cc1b3fe 100644 --- a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.DefinitionTrackingContext.cs +++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.DefinitionTrackingContext.cs @@ -37,20 +37,20 @@ public CancellationToken CancellationToken public IStreamingProgressTracker ProgressTracker => _underlyingContext.ProgressTracker; - public Task ReportMessageAsync(string message) + public ValueTask ReportMessageAsync(string message) => _underlyingContext.ReportMessageAsync(message); - public Task SetSearchTitleAsync(string title) + public ValueTask SetSearchTitleAsync(string title) => _underlyingContext.SetSearchTitleAsync(title); - public Task OnReferenceFoundAsync(SourceReferenceItem reference) + public ValueTask OnReferenceFoundAsync(SourceReferenceItem reference) => _underlyingContext.OnReferenceFoundAsync(reference); [Obsolete("Use ProgressTracker instead", error: false)] - public Task ReportProgressAsync(int current, int maximum) + public ValueTask ReportProgressAsync(int current, int maximum) => _underlyingContext.ReportProgressAsync(current, maximum); - public Task OnDefinitionFoundAsync(DefinitionItem definition) + public ValueTask OnDefinitionFoundAsync(DefinitionItem definition) { lock (_gate) { diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs index 0ccbc0881c0aa..d3ac21dece7cb 100644 --- a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs +++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs @@ -36,7 +36,7 @@ public FindLiteralsProgressAdapter( _definition = definition; } - public async Task OnReferenceFoundAsync(Document document, TextSpan span) + public async ValueTask OnReferenceFoundAsync(Document document, TextSpan span) { var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync( document, span, _context.CancellationToken).ConfigureAwait(false); @@ -82,16 +82,16 @@ public FindReferencesProgressAdapter( // Do nothing functions. The streaming far service doesn't care about // any of these. - public Task OnStartedAsync() => Task.CompletedTask; - public Task OnCompletedAsync() => Task.CompletedTask; - public Task OnFindInDocumentStartedAsync(Document document) => Task.CompletedTask; - public Task OnFindInDocumentCompletedAsync(Document document) => Task.CompletedTask; + public ValueTask OnStartedAsync() => default; + public ValueTask OnCompletedAsync() => default; + public ValueTask OnFindInDocumentStartedAsync(Document document) => default; + public ValueTask OnFindInDocumentCompletedAsync(Document document) => default; // More complicated forwarding functions. These need to map from the symbols // used by the FAR engine to the INavigableItems used by the streaming FAR // feature. - private async Task GetDefinitionItemAsync(ISymbol definition) + private async ValueTask GetDefinitionItemAsync(ISymbol definition) { var cancellationToken = _context.CancellationToken; using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) @@ -112,13 +112,13 @@ private async Task GetDefinitionItemAsync(ISymbol definition) } } - public async Task OnDefinitionFoundAsync(ISymbol definition) + public async ValueTask OnDefinitionFoundAsync(ISymbol definition) { var definitionItem = await GetDefinitionItemAsync(definition).ConfigureAwait(false); await _context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false); } - public async Task OnReferenceFoundAsync(ISymbol definition, ReferenceLocation location) + public async ValueTask OnReferenceFoundAsync(ISymbol definition, ReferenceLocation location) { var definitionItem = await GetDefinitionItemAsync(definition).ConfigureAwait(false); var referenceItem = await location.TryCreateSourceReferenceItemAsync( diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs index 9c53c2e3bb257..9f011155b934c 100644 --- a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs +++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs @@ -10,9 +10,5 @@ namespace Microsoft.CodeAnalysis.Editor.FindUsages { internal abstract partial class AbstractFindUsagesService : IFindUsagesService, IFindUsagesLSPService { - private readonly IThreadingContext _threadingContext; - - protected AbstractFindUsagesService(IThreadingContext threadingContext) - => _threadingContext = threadingContext; } } diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService_FindImplementations.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService_FindImplementations.cs index cbe6ed362d654..92042144e18a4 100644 --- a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService_FindImplementations.cs +++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService_FindImplementations.cs @@ -51,15 +51,11 @@ public static async Task FindImplementationsAsync( // results as it finds them. When we hear about results we'll forward them to // the 'progress' parameter which will then update the UI. var serverCallback = new FindUsagesServerCallback(solution, context); + var symbolAndProjectId = SerializableSymbolAndProjectId.Create(symbol, project, cancellationToken); - await client.RunRemoteAsync( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteFindUsagesService.FindImplementationsAsync), + await client.TryInvokeAsync( solution, - new object[] - { - SerializableSymbolAndProjectId.Create(symbol, project, cancellationToken), - }, + (service, solutionInfo, cancellationToken) => service.FindImplementationsAsync(solutionInfo, symbolAndProjectId, cancellationToken), serverCallback, cancellationToken).ConfigureAwait(false); } diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService_FindReferences.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService_FindReferences.cs index 8aca444539046..8b84ca2550f64 100644 --- a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService_FindReferences.cs +++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService_FindReferences.cs @@ -114,7 +114,7 @@ public static async Task FindSymbolReferencesAsync( await context.SetSearchTitleAsync(string.Format(EditorFeaturesResources._0_references, FindUsagesHelpers.GetDisplayName(symbol))).ConfigureAwait(false); - var options = FindReferencesSearchOptions.GetFeatureOptionsForStartingSymbol(symbol); + var options = FindSymbols.FindReferencesSearchOptions.GetFeatureOptionsForStartingSymbol(symbol); // Now call into the underlying FAR engine to find reference. The FAR // engine will push results into the 'progress' instance passed into it. @@ -138,16 +138,11 @@ public static async Task FindReferencesAsync( // results as it finds them. When we hear about results we'll forward them to // the 'progress' parameter which will then update the UI. var serverCallback = new FindUsagesServerCallback(solution, context); + var symbolAndProjectId = SerializableSymbolAndProjectId.Create(symbol, project, cancellationToken); - await client.RunRemoteAsync( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteFindUsagesService.FindReferencesAsync), + _ = await client.TryInvokeAsync( solution, - new object[] - { - SerializableSymbolAndProjectId.Create(symbol, project, cancellationToken), - SerializableFindReferencesSearchOptions.Dehydrate(options), - }, + (service, solutionInfo, cancellationToken) => service.FindReferencesAsync(solutionInfo, symbolAndProjectId, options, cancellationToken), serverCallback, cancellationToken).ConfigureAwait(false); } diff --git a/src/EditorFeatures/Core/FindUsages/FindUsagesContext.cs b/src/EditorFeatures/Core/FindUsages/FindUsagesContext.cs index 3b07a1c37dc4b..1133af77c5313 100644 --- a/src/EditorFeatures/Core/FindUsages/FindUsagesContext.cs +++ b/src/EditorFeatures/Core/FindUsages/FindUsagesContext.cs @@ -17,19 +17,19 @@ internal abstract class FindUsagesContext : IFindUsagesContext protected FindUsagesContext() => this.ProgressTracker = new StreamingProgressTracker(this.ReportProgressAsync); - public virtual Task ReportMessageAsync(string message) => Task.CompletedTask; + public virtual ValueTask ReportMessageAsync(string message) => default; - public virtual Task SetSearchTitleAsync(string title) => Task.CompletedTask; + public virtual ValueTask SetSearchTitleAsync(string title) => default; - public virtual Task OnCompletedAsync() => Task.CompletedTask; + public virtual ValueTask OnCompletedAsync() => default; - public virtual Task OnDefinitionFoundAsync(DefinitionItem definition) => Task.CompletedTask; + public virtual ValueTask OnDefinitionFoundAsync(DefinitionItem definition) => default; - public virtual Task OnReferenceFoundAsync(SourceReferenceItem reference) => Task.CompletedTask; + public virtual ValueTask OnReferenceFoundAsync(SourceReferenceItem reference) => default; - protected virtual Task ReportProgressAsync(int current, int maximum) => Task.CompletedTask; + protected virtual ValueTask ReportProgressAsync(int current, int maximum) => default; - Task IFindUsagesContext.ReportProgressAsync(int current, int maximum) + ValueTask IFindUsagesContext.ReportProgressAsync(int current, int maximum) => ReportProgressAsync(current, maximum); } } diff --git a/src/EditorFeatures/Core/FindUsages/IRemoteFindUsagesService.cs b/src/EditorFeatures/Core/FindUsages/IRemoteFindUsagesService.cs deleted file mode 100644 index 01fb1dcb8c309..0000000000000 --- a/src/EditorFeatures/Core/FindUsages/IRemoteFindUsagesService.cs +++ /dev/null @@ -1,190 +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. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.FindUsages; -using Microsoft.CodeAnalysis.Remote; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Editor.FindUsages -{ - internal interface IRemoteFindUsagesService - { - Task FindReferencesAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId symbolAndProjectIdArg, - SerializableFindReferencesSearchOptions options, - CancellationToken cancellationToken); - - Task FindImplementationsAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId symbolAndProjectIdArg, - CancellationToken cancellationToken); - } - - internal class FindUsagesServerCallback - { - private readonly Solution _solution; - private readonly IFindUsagesContext _context; - private readonly Dictionary _idToDefinition = new Dictionary(); - - public FindUsagesServerCallback(Solution solution, IFindUsagesContext context) - { - _solution = solution; - _context = context; - } - - public Task AddItemsAsync(int count) - => _context.ProgressTracker.AddItemsAsync(count); - - public Task ItemCompletedAsync() - => _context.ProgressTracker.ItemCompletedAsync(); - - public Task ReportMessageAsync(string message) - => _context.ReportMessageAsync(message); - - [Obsolete] - public Task ReportProgressAsync(int current, int maximum) - => _context.ReportProgressAsync(current, maximum); - - public Task SetSearchTitleAsync(string title) - => _context.SetSearchTitleAsync(title); - - public Task OnDefinitionFoundAsync(SerializableDefinitionItem definition) - { - var id = definition.Id; - var rehydrated = definition.Rehydrate(_solution); - - lock (_idToDefinition) - { - _idToDefinition.Add(id, rehydrated); - } - - return _context.OnDefinitionFoundAsync(rehydrated); - } - - public Task OnReferenceFoundAsync(SerializableSourceReferenceItem reference) - => _context.OnReferenceFoundAsync(reference.Rehydrate(_solution, GetDefinition(reference.DefinitionId))); - - private DefinitionItem GetDefinition(int definitionId) - { - lock (_idToDefinition) - { - Contract.ThrowIfFalse(_idToDefinition.ContainsKey(definitionId)); - return _idToDefinition[definitionId]; - } - } - } - - internal class SerializableDocumentSpan - { - public DocumentId DocumentId; - public TextSpan SourceSpan; - - public static SerializableDocumentSpan Dehydrate(DocumentSpan documentSpan) - => new SerializableDocumentSpan - { - DocumentId = documentSpan.Document.Id, - SourceSpan = documentSpan.SourceSpan, - }; - - public DocumentSpan Rehydrate(Solution solution) - => new DocumentSpan(solution.GetDocument(DocumentId), SourceSpan); - } - - internal class SerializableTaggedText - { - public string Tag; - public string Text; - public TaggedTextStyle Style; - public string NavigationTarget; - public string NavigationHint; - - public static SerializableTaggedText Dehydrate(TaggedText text) - => new SerializableTaggedText - { - Tag = text.Tag, - Text = text.Text, - Style = text.Style, - NavigationTarget = text.NavigationTarget, - NavigationHint = text.NavigationHint, - }; - - public TaggedText Rehydrate() - => new TaggedText(Tag, Text, Style, NavigationTarget, NavigationHint); - } - - internal class SerializableDefinitionItem - { - public int Id; - public string[] Tags; - public SerializableTaggedText[] DisplayParts; - public SerializableTaggedText[] NameDisplayParts; - public SerializableTaggedText[] OriginationParts; - public SerializableDocumentSpan[] SourceSpans; - public (string key, string value)[] Properties; - public (string key, string value)[] DisplayableProperties; - public bool DisplayIfNoReferences; - - public static SerializableDefinitionItem Dehydrate(int id, DefinitionItem item) - => new SerializableDefinitionItem - { - Id = id, - Tags = item.Tags.ToArray(), - DisplayParts = item.DisplayParts.Select(p => SerializableTaggedText.Dehydrate(p)).ToArray(), - NameDisplayParts = item.NameDisplayParts.Select(p => SerializableTaggedText.Dehydrate(p)).ToArray(), - OriginationParts = item.OriginationParts.Select(p => SerializableTaggedText.Dehydrate(p)).ToArray(), - SourceSpans = item.SourceSpans.Select(ss => SerializableDocumentSpan.Dehydrate(ss)).ToArray(), - Properties = item.Properties.Select(kvp => (kvp.Key, kvp.Value)).ToArray(), - DisplayableProperties = item.DisplayableProperties.Select(kvp => (kvp.Key, kvp.Value)).ToArray(), - DisplayIfNoReferences = item.DisplayIfNoReferences, - }; - - public DefinitionItem Rehydrate(Solution solution) - => new DefinitionItem.DefaultDefinitionItem( - Tags.ToImmutableArray(), - DisplayParts.SelectAsArray(dp => dp.Rehydrate()), - NameDisplayParts.SelectAsArray(dp => dp.Rehydrate()), - OriginationParts.SelectAsArray(dp => dp.Rehydrate()), - SourceSpans.SelectAsArray(ss => ss.Rehydrate(solution)), - Properties.ToImmutableDictionary(t => t.key, t => t.value), - DisplayableProperties.ToImmutableDictionary(t => t.key, t => t.value), - DisplayIfNoReferences); - } - - internal class SerializableSourceReferenceItem - { - public int DefinitionId; - public SerializableDocumentSpan SourceSpan; - public SerializableSymbolUsageInfo SymbolUsageInfo; - public (string Key, string Value)[] AdditionalProperties; - - public static SerializableSourceReferenceItem Dehydrate( - int definitionId, SourceReferenceItem item) - { - return new SerializableSourceReferenceItem - { - DefinitionId = definitionId, - SourceSpan = SerializableDocumentSpan.Dehydrate(item.SourceSpan), - SymbolUsageInfo = SerializableSymbolUsageInfo.Dehydrate(item.SymbolUsageInfo), - AdditionalProperties = item.AdditionalProperties.Select(kvp => (kvp.Key, kvp.Value)).ToArray(), - }; - } - - public SourceReferenceItem Rehydrate(Solution solution, DefinitionItem definition) - { - return new SourceReferenceItem( - definition, - SourceSpan.Rehydrate(solution), - SymbolUsageInfo.Rehydrate(), - AdditionalProperties.ToImmutableDictionary(t => t.Key, t => t.Value)); - } - } -} diff --git a/src/EditorFeatures/Core/FindUsages/SimpleFindUsagesContext.cs b/src/EditorFeatures/Core/FindUsages/SimpleFindUsagesContext.cs index 0420585a96f36..c8cf6b849ad90 100644 --- a/src/EditorFeatures/Core/FindUsages/SimpleFindUsagesContext.cs +++ b/src/EditorFeatures/Core/FindUsages/SimpleFindUsagesContext.cs @@ -31,16 +31,16 @@ public SimpleFindUsagesContext(CancellationToken cancellationToken) public string Message { get; private set; } public string SearchTitle { get; private set; } - public override Task ReportMessageAsync(string message) + public override ValueTask ReportMessageAsync(string message) { Message = message; - return Task.CompletedTask; + return default; } - public override Task SetSearchTitleAsync(string title) + public override ValueTask SetSearchTitleAsync(string title) { SearchTitle = title; - return Task.CompletedTask; + return default; } public ImmutableArray GetDefinitions() @@ -59,24 +59,24 @@ public ImmutableArray GetReferences() } } - public override Task OnDefinitionFoundAsync(DefinitionItem definition) + public override ValueTask OnDefinitionFoundAsync(DefinitionItem definition) { lock (_gate) { _definitionItems.Add(definition); } - return Task.CompletedTask; + return default; } - public override Task OnReferenceFoundAsync(SourceReferenceItem reference) + public override ValueTask OnReferenceFoundAsync(SourceReferenceItem reference) { lock (_gate) { _referenceItems.Add(reference); } - return Task.CompletedTask; + return default; } } } diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs b/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs index 83f82e871d8f3..be9eb36935083 100644 --- a/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs +++ b/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs @@ -12,6 +12,8 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces { internal class EditorErrorReportingService : IErrorReportingService { + public string HostDisplayName => "host"; + public void ShowDetailedErrorInfo(Exception exception) => Logger.Log(FunctionId.Extension_Exception, exception.StackTrace); @@ -23,5 +25,10 @@ public void ShowGlobalErrorInfo(string message, params InfoBarUI[] items) public void ShowRemoteHostCrashedErrorInfo(Exception? exception) => Logger.Log(FunctionId.Extension_Exception, exception?.Message); + + public void ShowFeatureNotAvailableErrorInfo(string message, Exception? exception) + { + // telemetry has already been reported + } } } diff --git a/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs index 97cc1a7fb9219..e7795035e3e58 100644 --- a/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs +++ b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs @@ -13,11 +13,9 @@ using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; -using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Elfie.Model; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.VisualStudio.RemoteControl; -using Roslyn.Utilities; using static System.FormattableString; namespace Microsoft.CodeAnalysis.SymbolSearch @@ -56,19 +54,18 @@ internal partial class SymbolSearchUpdateEngine private readonly IDatabaseFactoryService _databaseFactoryService; private readonly Func _reportAndSwallowException; - private Task LogInfoAsync(string text) => _logService.LogInfoAsync(text); + private ValueTask LogInfoAsync(string text, CancellationToken cancellationToken) + => _logService.LogInfoAsync(text, cancellationToken); - private Task LogExceptionAsync(Exception e, string text) => _logService.LogExceptionAsync(e.ToString(), text); - - public Task UpdateContinuouslyAsync(string source, string localSettingsDirectory) - => UpdateContinuouslyAsync(source, localSettingsDirectory, CancellationToken.None); + private ValueTask LogExceptionAsync(Exception e, string text, CancellationToken cancellationToken) + => _logService.LogExceptionAsync(e.ToString(), text, cancellationToken); /// /// Cancellation support for the task we use to keep the local database up to date. /// Currently used only in tests so we can shutdown gracefully. In normal VS+OOP scenarios /// we don't care about this and we just get torn down when the OOP process goes down. /// - internal Task UpdateContinuouslyAsync(string source, string localSettingsDirectory, CancellationToken cancellationToken) + public ValueTask UpdateContinuouslyAsync(string source, string localSettingsDirectory, CancellationToken cancellationToken) { // Only the first thread to try to update this source should succeed // and cause us to actually begin the update loop. @@ -78,7 +75,7 @@ internal Task UpdateContinuouslyAsync(string source, string localSettingsDirecto if (ourSentinel != currentSentinel) { // We already have an update loop for this source. Nothing for us to do. - return Task.CompletedTask; + return default; } // We were the first ones to try to update this source. Spawn off a task to do @@ -104,7 +101,7 @@ public Updater(SymbolSearchUpdateEngine service, string source, string localSett /// /// Internal for testing purposes. /// - internal async Task UpdateInBackgroundAsync(CancellationToken cancellationToken) + internal async ValueTask UpdateInBackgroundAsync(CancellationToken cancellationToken) { // We only support this single source currently. if (_source != NugetOrgSource) @@ -115,17 +112,17 @@ internal async Task UpdateInBackgroundAsync(CancellationToken cancellationToken) // Keep on looping until we're told to shut down. while (!cancellationToken.IsCancellationRequested) { - await _service.LogInfoAsync("Starting update").ConfigureAwait(false); + await _service.LogInfoAsync("Starting update", cancellationToken).ConfigureAwait(false); try { var delayUntilNextUpdate = await UpdateDatabaseInBackgroundWorkerAsync(cancellationToken).ConfigureAwait(false); - await _service.LogInfoAsync($"Waiting {delayUntilNextUpdate} until next update").ConfigureAwait(false); + await _service.LogInfoAsync($"Waiting {delayUntilNextUpdate} until next update", cancellationToken).ConfigureAwait(false); await Task.Delay(delayUntilNextUpdate, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { - await _service.LogInfoAsync("Update canceled. Ending update loop").ConfigureAwait(false); + await _service.LogInfoAsync("Update canceled. Ending update loop", cancellationToken).ConfigureAwait(false); return; } } @@ -184,12 +181,12 @@ private async Task UpdateDatabaseInBackgroundWorkerAsync(CancellationT if (_service._ioService.Exists(databaseFileInfo)) { - await _service.LogInfoAsync("Local database file exists. Patching local database").ConfigureAwait(false); + await _service.LogInfoAsync("Local database file exists. Patching local database", cancellationToken).ConfigureAwait(false); return await PatchLocalDatabaseAsync(databaseFileInfo, cancellationToken).ConfigureAwait(false); } else { - await _service.LogInfoAsync("Local database file does not exist. Downloading full database").ConfigureAwait(false); + await _service.LogInfoAsync("Local database file does not exist. Downloading full database", cancellationToken).ConfigureAwait(false); return await DownloadFullDatabaseAsync(databaseFileInfo, cancellationToken).ConfigureAwait(false); } } @@ -207,23 +204,23 @@ private async Task UpdateDatabaseInBackgroundWorkerAsync(CancellationT // It's the standard way to indicate that we've been asked to shut // down. var delay = _service._delayService.ExpectedFailureDelay; - await _service.LogExceptionAsync(e, $"Error occurred updating. Retrying update in {delay}").ConfigureAwait(false); + await _service.LogExceptionAsync(e, $"Error occurred updating. Retrying update in {delay}", cancellationToken).ConfigureAwait(false); return delay; } } private async Task CleanCacheDirectoryAsync(CancellationToken cancellationToken) { - await _service.LogInfoAsync("Cleaning cache directory").ConfigureAwait(false); + await _service.LogInfoAsync("Cleaning cache directory", cancellationToken).ConfigureAwait(false); // (intentionally not wrapped in IOUtilities. If this throws we want to restart). if (!_service._ioService.Exists(_cacheDirectoryInfo)) { - await _service.LogInfoAsync("Creating cache directory").ConfigureAwait(false); + await _service.LogInfoAsync("Creating cache directory", cancellationToken).ConfigureAwait(false); // (intentionally not wrapped in IOUtilities. If this throws we want to restart). _service._ioService.Create(_cacheDirectoryInfo); - await _service.LogInfoAsync("Cache directory created").ConfigureAwait(false); + await _service.LogInfoAsync("Cache directory created", cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); @@ -239,22 +236,22 @@ private async Task DownloadFullDatabaseAsync(FileInfo databaseFileInfo { var serverPath = Invariant($"Elfie_V{AddReferenceDatabaseTextFileFormatVersion}/Latest.xml"); - await _service.LogInfoAsync($"Downloading and processing full database: {serverPath}").ConfigureAwait(false); + await _service.LogInfoAsync($"Downloading and processing full database: {serverPath}", cancellationToken).ConfigureAwait(false); var element = await DownloadFileAsync(serverPath, cancellationToken).ConfigureAwait(false); var result = await ProcessFullDatabaseXElementAsync(databaseFileInfo, element, cancellationToken).ConfigureAwait(false); - await _service.LogInfoAsync("Downloading and processing full database completed").ConfigureAwait(false); + await _service.LogInfoAsync("Downloading and processing full database completed", cancellationToken).ConfigureAwait(false); return result; } private async Task<(bool succeeded, TimeSpan delay)> ProcessFullDatabaseXElementAsync( FileInfo databaseFileInfo, XElement element, CancellationToken cancellationToken) { - await _service.LogInfoAsync("Processing full database element").ConfigureAwait(false); + await _service.LogInfoAsync("Processing full database element", cancellationToken).ConfigureAwait(false); // Convert the database contents in the XML to a byte[]. - var (succeeded, contentBytes) = await TryParseDatabaseElementAsync(element).ConfigureAwait(false); + var (succeeded, contentBytes) = await TryParseDatabaseElementAsync(element, cancellationToken).ConfigureAwait(false); if (!succeeded) { @@ -264,7 +261,7 @@ private async Task DownloadFullDatabaseAsync(FileInfo databaseFileInfo // we can retrieve good data the next time around. var failureDelay = _service._delayService.CatastrophicFailureDelay; - await _service.LogInfoAsync($"Unable to parse full database element. Update again in {failureDelay}").ConfigureAwait(false); + await _service.LogInfoAsync($"Unable to parse full database element. Update again in {failureDelay}", cancellationToken).ConfigureAwait(false); return (succeeded: false, failureDelay); } @@ -274,7 +271,7 @@ private async Task DownloadFullDatabaseAsync(FileInfo databaseFileInfo // searching. try { - await CreateAndSetInMemoryDatabaseAsync(bytes).ConfigureAwait(false); + await CreateAndSetInMemoryDatabaseAsync(bytes, cancellationToken).ConfigureAwait(false); } catch (Exception e) when (_service._reportAndSwallowException(e)) { @@ -283,7 +280,7 @@ private async Task DownloadFullDatabaseAsync(FileInfo databaseFileInfo // isn't going to help. We need to wait until there is good data // on the server for us to download. var failureDelay = _service._delayService.CatastrophicFailureDelay; - await _service.LogInfoAsync($"Unable to create database from full database element. Update again in {failureDelay}").ConfigureAwait(false); + await _service.LogInfoAsync($"Unable to create database from full database element. Update again in {failureDelay}", cancellationToken).ConfigureAwait(false); return (succeeded: false, failureDelay); } @@ -293,13 +290,13 @@ private async Task DownloadFullDatabaseAsync(FileInfo databaseFileInfo await WriteDatabaseFileAsync(databaseFileInfo, bytes, cancellationToken).ConfigureAwait(false); var delay = _service._delayService.UpdateSucceededDelay; - await _service.LogInfoAsync($"Processing full database element completed. Update again in {delay}").ConfigureAwait(false); + await _service.LogInfoAsync($"Processing full database element completed. Update again in {delay}", cancellationToken).ConfigureAwait(false); return (succeeded: true, delay); } private async Task WriteDatabaseFileAsync(FileInfo databaseFileInfo, byte[] bytes, CancellationToken cancellationToken) { - await _service.LogInfoAsync("Writing database file").ConfigureAwait(false); + await _service.LogInfoAsync("Writing database file", cancellationToken).ConfigureAwait(false); await RepeatIOAsync( async () => @@ -307,7 +304,7 @@ await RepeatIOAsync( var guidString = Guid.NewGuid().ToString(); var tempFilePath = Path.Combine(_cacheDirectoryInfo.FullName, guidString + ".tmp"); - await _service.LogInfoAsync($"Temp file path: {tempFilePath}").ConfigureAwait(false); + await _service.LogInfoAsync($"Temp file path: {tempFilePath}", cancellationToken).ConfigureAwait(false); try { @@ -316,25 +313,25 @@ await RepeatIOAsync( // file has been completely written to disk (at least as well as the OS can guarantee // things). - await _service.LogInfoAsync("Writing temp file").ConfigureAwait(false); + await _service.LogInfoAsync("Writing temp file", cancellationToken).ConfigureAwait(false); // (intentionally not wrapped in IOUtilities. If this throws we want to retry writing). _service._ioService.WriteAndFlushAllBytes(tempFilePath, bytes); - await _service.LogInfoAsync("Writing temp file completed").ConfigureAwait(false); + await _service.LogInfoAsync("Writing temp file completed", cancellationToken).ConfigureAwait(false); // If we have an existing db file, try to replace it file with the temp file. // Otherwise, just move the temp file into place. if (_service._ioService.Exists(databaseFileInfo)) { - await _service.LogInfoAsync("Replacing database file").ConfigureAwait(false); + await _service.LogInfoAsync("Replacing database file", cancellationToken).ConfigureAwait(false); _service._ioService.Replace(tempFilePath, databaseFileInfo.FullName, destinationBackupFileName: null, ignoreMetadataErrors: true); - await _service.LogInfoAsync("Replace database file completed").ConfigureAwait(false); + await _service.LogInfoAsync("Replace database file completed", cancellationToken).ConfigureAwait(false); } else { - await _service.LogInfoAsync("Moving database file").ConfigureAwait(false); + await _service.LogInfoAsync("Moving database file", cancellationToken).ConfigureAwait(false); _service._ioService.Move(tempFilePath, databaseFileInfo.FullName); - await _service.LogInfoAsync("Moving database file completed").ConfigureAwait(false); + await _service.LogInfoAsync("Moving database file completed", cancellationToken).ConfigureAwait(false); } } finally @@ -345,17 +342,17 @@ await RepeatIOAsync( } }, cancellationToken).ConfigureAwait(false); - await _service.LogInfoAsync("Writing database file completed").ConfigureAwait(false); + await _service.LogInfoAsync("Writing database file completed", cancellationToken).ConfigureAwait(false); } private async Task PatchLocalDatabaseAsync(FileInfo databaseFileInfo, CancellationToken cancellationToken) { - await _service.LogInfoAsync("Patching local database").ConfigureAwait(false); + await _service.LogInfoAsync("Patching local database", cancellationToken).ConfigureAwait(false); - await _service.LogInfoAsync("Reading in local database").ConfigureAwait(false); + await _service.LogInfoAsync("Reading in local database", cancellationToken).ConfigureAwait(false); // (intentionally not wrapped in IOUtilities. If this throws we want to restart). var databaseBytes = _service._ioService.ReadAllBytes(databaseFileInfo.FullName); - await _service.LogInfoAsync($"Reading in local database completed. databaseBytes.Length={databaseBytes.Length}").ConfigureAwait(false); + await _service.LogInfoAsync($"Reading in local database completed. databaseBytes.Length={databaseBytes.Length}", cancellationToken).ConfigureAwait(false); // Make a database instance out of those bytes and set is as the current in memory database // that searches will run against. If we can't make a database instance from these bytes @@ -364,24 +361,24 @@ private async Task PatchLocalDatabaseAsync(FileInfo databaseFileInfo, AddReferenceDatabase database; try { - database = await CreateAndSetInMemoryDatabaseAsync(databaseBytes).ConfigureAwait(false); + database = await CreateAndSetInMemoryDatabaseAsync(databaseBytes, cancellationToken).ConfigureAwait(false); } catch (Exception e) when (_service._reportAndSwallowException(e)) { - await _service.LogExceptionAsync(e, "Error creating database from local copy. Downloading full database").ConfigureAwait(false); + await _service.LogExceptionAsync(e, "Error creating database from local copy. Downloading full database", cancellationToken).ConfigureAwait(false); return await DownloadFullDatabaseAsync(databaseFileInfo, cancellationToken).ConfigureAwait(false); } // Now attempt to download and apply patch file. var serverPath = Invariant($"Elfie_V{AddReferenceDatabaseTextFileFormatVersion}/{database.DatabaseVersion}_Patch.xml"); - await _service.LogInfoAsync("Downloading and processing patch file: " + serverPath).ConfigureAwait(false); + await _service.LogInfoAsync("Downloading and processing patch file: " + serverPath, cancellationToken).ConfigureAwait(false); var element = await DownloadFileAsync(serverPath, cancellationToken).ConfigureAwait(false); var delayUntilUpdate = await ProcessPatchXElementAsync(databaseFileInfo, element, databaseBytes, cancellationToken).ConfigureAwait(false); - await _service.LogInfoAsync("Downloading and processing patch file completed").ConfigureAwait(false); - await _service.LogInfoAsync("Patching local database completed").ConfigureAwait(false); + await _service.LogInfoAsync("Downloading and processing patch file completed", cancellationToken).ConfigureAwait(false); + await _service.LogInfoAsync("Patching local database completed", cancellationToken).ConfigureAwait(false); return delayUntilUpdate; } @@ -392,9 +389,9 @@ private async Task PatchLocalDatabaseAsync(FileInfo databaseFileInfo, /// indicates that our data is corrupt), the exception will bubble up and must be appropriately /// dealt with by the caller. /// - private async Task CreateAndSetInMemoryDatabaseAsync(byte[] bytes) + private async Task CreateAndSetInMemoryDatabaseAsync(byte[] bytes, CancellationToken cancellationToken) { - var database = await CreateDatabaseFromBytesAsync(bytes).ConfigureAwait(false); + var database = await CreateDatabaseFromBytesAsync(bytes, cancellationToken).ConfigureAwait(false); _service._sourceToDatabase[_source] = new AddReferenceDatabaseWrapper(database); return database; } @@ -404,11 +401,11 @@ private async Task ProcessPatchXElementAsync( { try { - await _service.LogInfoAsync("Processing patch element").ConfigureAwait(false); + await _service.LogInfoAsync("Processing patch element", cancellationToken).ConfigureAwait(false); var delayUntilUpdate = await TryProcessPatchXElementAsync(databaseFileInfo, patchElement, databaseBytes, cancellationToken).ConfigureAwait(false); if (delayUntilUpdate != null) { - await _service.LogInfoAsync($"Processing patch element completed. Update again in {delayUntilUpdate.Value}").ConfigureAwait(false); + await _service.LogInfoAsync($"Processing patch element completed. Update again in {delayUntilUpdate.Value}", cancellationToken).ConfigureAwait(false); return delayUntilUpdate.Value; } @@ -416,7 +413,7 @@ private async Task ProcessPatchXElementAsync( } catch (Exception e) when (_service._reportAndSwallowException(e)) { - await _service.LogExceptionAsync(e, "Error occurred while processing patch element. Downloading full database").ConfigureAwait(false); + await _service.LogExceptionAsync(e, "Error occurred while processing patch element. Downloading full database", cancellationToken).ConfigureAwait(false); // Fall through and download full database. } @@ -430,25 +427,25 @@ private async Task ProcessPatchXElementAsync( if (upToDate) { - await _service.LogInfoAsync("Local version is up to date").ConfigureAwait(false); + await _service.LogInfoAsync("Local version is up to date", cancellationToken).ConfigureAwait(false); return _service._delayService.UpdateSucceededDelay; } if (tooOld) { - await _service.LogInfoAsync("Local version too old").ConfigureAwait(false); + await _service.LogInfoAsync("Local version too old", cancellationToken).ConfigureAwait(false); return null; } - await _service.LogInfoAsync($"Got patch. databaseBytes.Length={databaseBytes.Length} patchBytes.Length={patchBytes.Length}.").ConfigureAwait(false); + await _service.LogInfoAsync($"Got patch. databaseBytes.Length={databaseBytes.Length} patchBytes.Length={patchBytes.Length}.", cancellationToken).ConfigureAwait(false); // We have patch data. Apply it to our current database bytes to produce the new // database. - await _service.LogInfoAsync("Applying patch").ConfigureAwait(false); + await _service.LogInfoAsync("Applying patch", cancellationToken).ConfigureAwait(false); var finalBytes = _service._patchService.ApplyPatch(databaseBytes, patchBytes); - await _service.LogInfoAsync($"Applying patch completed. finalBytes.Length={finalBytes.Length}").ConfigureAwait(false); + await _service.LogInfoAsync($"Applying patch completed. finalBytes.Length={finalBytes.Length}", cancellationToken).ConfigureAwait(false); - await CreateAndSetInMemoryDatabaseAsync(finalBytes).ConfigureAwait(false); + await CreateAndSetInMemoryDatabaseAsync(finalBytes, cancellationToken).ConfigureAwait(false); await WriteDatabaseFileAsync(databaseFileInfo, finalBytes, cancellationToken).ConfigureAwait(false); @@ -483,17 +480,17 @@ private static void ParsePatchElement(XElement patchElement, out bool upToDate, } } - private async Task CreateDatabaseFromBytesAsync(byte[] bytes) + private async Task CreateDatabaseFromBytesAsync(byte[] bytes, CancellationToken cancellationToken) { - await _service.LogInfoAsync("Creating database from bytes").ConfigureAwait(false); + await _service.LogInfoAsync("Creating database from bytes", cancellationToken).ConfigureAwait(false); var result = _service._databaseFactoryService.CreateDatabaseFromBytes(bytes); - await _service.LogInfoAsync("Creating database from bytes completed").ConfigureAwait(false); + await _service.LogInfoAsync("Creating database from bytes completed", cancellationToken).ConfigureAwait(false); return result; } private async Task DownloadFileAsync(string serverPath, CancellationToken cancellationToken) { - await _service.LogInfoAsync("Creating download client: " + serverPath).ConfigureAwait(false); + await _service.LogInfoAsync("Creating download client: " + serverPath, cancellationToken).ConfigureAwait(false); // Create a client that will attempt to download the specified file. The client works // in the following manner: @@ -509,18 +506,18 @@ private async Task DownloadFileAsync(string serverPath, CancellationTo var pollingMinutes = (int)TimeSpan.FromDays(1).TotalMinutes; using var client = _service._remoteControlService.CreateClient(HostId, serverPath, pollingMinutes); - await _service.LogInfoAsync("Creating download client completed").ConfigureAwait(false); + await _service.LogInfoAsync("Creating download client completed", cancellationToken).ConfigureAwait(false); // Poll the client every minute until we get the file. while (true) { cancellationToken.ThrowIfCancellationRequested(); - var resultOpt = await TryDownloadFileAsync(client).ConfigureAwait(false); + var resultOpt = await TryDownloadFileAsync(client, cancellationToken).ConfigureAwait(false); if (resultOpt == null) { var delay = _service._delayService.CachePollDelay; - await _service.LogInfoAsync($"File not downloaded. Trying again in {delay}").ConfigureAwait(false); + await _service.LogInfoAsync($"File not downloaded. Trying again in {delay}", cancellationToken).ConfigureAwait(false); await Task.Delay(delay, cancellationToken).ConfigureAwait(false); } else @@ -532,9 +529,9 @@ private async Task DownloadFileAsync(string serverPath, CancellationTo } /// Returns 'null' if download is not available and caller should keep polling. - private async Task TryDownloadFileAsync(IRemoteControlClient client) + private async Task TryDownloadFileAsync(IRemoteControlClient client, CancellationToken cancellationToken) { - await _service.LogInfoAsync("Read file from client").ConfigureAwait(false); + await _service.LogInfoAsync("Read file from client", cancellationToken).ConfigureAwait(false); // "ReturnsNull": Only return a file if we have it locally *and* it's not older than our polling time (1 day). @@ -542,12 +539,12 @@ private async Task TryDownloadFileAsync(IRemoteControlClient client) if (stream == null) { - await _service.LogInfoAsync("Read file completed. Client returned no data").ConfigureAwait(false); + await _service.LogInfoAsync("Read file completed. Client returned no data", cancellationToken).ConfigureAwait(false); return null; } - await _service.LogInfoAsync("Read file completed. Client returned data").ConfigureAwait(false); - await _service.LogInfoAsync("Converting data to XElement").ConfigureAwait(false); + await _service.LogInfoAsync("Read file completed. Client returned data", cancellationToken).ConfigureAwait(false); + await _service.LogInfoAsync("Converting data to XElement", cancellationToken).ConfigureAwait(false); // We're reading in our own XML file, but even so, use conservative settings // just to be on the safe side. First, disallow DTDs entirely (we will never @@ -562,7 +559,7 @@ private async Task TryDownloadFileAsync(IRemoteControlClient client) using var reader = XmlReader.Create(stream, settings); var result = XElement.Load(reader); - await _service.LogInfoAsync("Converting data to XElement completed").ConfigureAwait(false); + await _service.LogInfoAsync("Converting data to XElement completed", cancellationToken).ConfigureAwait(false); return result; } @@ -588,15 +585,15 @@ private async Task RepeatIOAsync(Func action, CancellationToken cancellati // around the reporting in this case. var delay = _service._delayService.FileWriteDelay; - await _service.LogExceptionAsync(e, $"Operation failed. Trying again after {delay}").ConfigureAwait(false); + await _service.LogExceptionAsync(e, $"Operation failed. Trying again after {delay}", cancellationToken).ConfigureAwait(false); await Task.Delay(delay, cancellationToken).ConfigureAwait(false); } } } - private async Task<(bool succeeded, byte[] contentBytes)> TryParseDatabaseElementAsync(XElement element) + private async Task<(bool succeeded, byte[] contentBytes)> TryParseDatabaseElementAsync(XElement element, CancellationToken cancellationToken) { - await _service.LogInfoAsync("Parsing database element").ConfigureAwait(false); + await _service.LogInfoAsync("Parsing database element", cancellationToken).ConfigureAwait(false); var contentsAttribute = element.Attribute(ContentAttributeName); if (contentsAttribute == null) { @@ -605,7 +602,7 @@ private async Task RepeatIOAsync(Func action, CancellationToken cancellati return (succeeded: false, (byte[])null); } - var contentBytes = await ConvertContentAttributeAsync(contentsAttribute).ConfigureAwait(false); + var contentBytes = await ConvertContentAttributeAsync(contentsAttribute, cancellationToken).ConfigureAwait(false); var checksumAttribute = element.Attribute(ChecksumAttributeName); if (checksumAttribute != null) @@ -628,7 +625,7 @@ private async Task RepeatIOAsync(Func action, CancellationToken cancellati return (succeeded: true, contentBytes); } - private async Task ConvertContentAttributeAsync(XAttribute contentsAttribute) + private async Task ConvertContentAttributeAsync(XAttribute contentsAttribute, CancellationToken cancellationToken) { var text = contentsAttribute.Value; var compressedBytes = Convert.FromBase64String(text); @@ -643,7 +640,7 @@ private async Task ConvertContentAttributeAsync(XAttribute contentsAttri var bytes = outStream.ToArray(); - await _service.LogInfoAsync($"Parsing complete. bytes.length={bytes.Length}").ConfigureAwait(false); + await _service.LogInfoAsync($"Parsing complete. bytes.length={bytes.Length}", cancellationToken).ConfigureAwait(false); return bytes; } } diff --git a/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.cs index 1cb312506eacd..077bf39342f07 100644 --- a/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.cs +++ b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.cs @@ -66,20 +66,20 @@ internal SymbolSearchUpdateEngine( _reportAndSwallowException = reportAndSwallowException; } - public Task> FindPackagesWithTypeAsync( + public ValueTask> FindPackagesWithTypeAsync( string source, string name, int arity, CancellationToken cancellationToken) { if (!_sourceToDatabase.TryGetValue(source, out var databaseWrapper)) { // Don't have a database to search. - return SpecializedTasks.EmptyImmutableArray(); + return new(ImmutableArray.Empty); } var database = databaseWrapper.Database; if (name == "var") { // never find anything named 'var'. - return SpecializedTasks.EmptyImmutableArray(); + return new(ImmutableArray.Empty); } var query = new MemberQuery(name, isFullSuffix: true, isFullNamespace: false); @@ -102,16 +102,16 @@ public Task> FindPackagesWithTypeAsync( } } - return Task.FromResult(result.ToImmutableAndFree()); + return new(result.ToImmutableAndFree()); } - public Task> FindPackagesWithAssemblyAsync( + public ValueTask> FindPackagesWithAssemblyAsync( string source, string assemblyName, CancellationToken cancellationToken) { if (!_sourceToDatabase.TryGetValue(source, out var databaseWrapper)) { // Don't have a database to search. - return SpecializedTasks.EmptyImmutableArray(); + return new(ImmutableArray.Empty); } var result = ArrayBuilder.GetInstance(); @@ -132,33 +132,35 @@ public Task> FindPackagesWithAssemblyA // Ignore any reference assembly results. if (symbol.PackageName.ToString() != MicrosoftAssemblyReferencesName) { + var version = database.GetPackageVersion(symbol.Index).ToString(); + result.Add(new PackageWithAssemblyResult( symbol.PackageName.ToString(), - database.GetPackageVersion(symbol.Index).ToString(), - GetRank(symbol))); + GetRank(symbol), + string.IsNullOrWhiteSpace(version) ? null : version)); } } } } - return Task.FromResult(result.ToImmutableAndFree()); + return new(result.ToImmutableAndFree()); } - public Task> FindReferenceAssembliesWithTypeAsync( + public ValueTask> FindReferenceAssembliesWithTypeAsync( string name, int arity, CancellationToken cancellationToken) { // Our reference assembly data is stored in the nuget.org DB. if (!_sourceToDatabase.TryGetValue(NugetOrgSource, out var databaseWrapper)) { // Don't have a database to search. - return SpecializedTasks.EmptyImmutableArray(); + return new(ImmutableArray.Empty); } var database = databaseWrapper.Database; if (name == "var") { // never find anything named 'var'. - return SpecializedTasks.EmptyImmutableArray(); + return new(ImmutableArray.Empty); } var query = new MemberQuery(name, isFullSuffix: true, isFullNamespace: false); @@ -186,7 +188,7 @@ public Task> FindReferenceAssemb } } - return Task.FromResult(results.ToImmutableAndFree()); + return new(results.ToImmutableAndFree()); } private static List FilterToViableTypes(PartialArray symbols) @@ -211,9 +213,9 @@ private PackageWithTypeResult CreateResult(AddReferenceDatabase database, Symbol return new PackageWithTypeResult( packageName: packageName, - typeName: type.Name.ToString(), - version: version, rank: GetRank(type), + typeName: type.Name.ToString(), + version: string.IsNullOrWhiteSpace(version) ? null : version, containingNamespaceNames: nameParts.ToImmutableAndFree()); } diff --git a/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngineFactory.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngineFactory.cs index 1224ad7545023..dbf4d896394ec 100644 --- a/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngineFactory.cs +++ b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngineFactory.cs @@ -4,13 +4,11 @@ #nullable enable -using System.Collections.Generic; using System.Collections.Immutable; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Remote; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SymbolSearch { @@ -32,9 +30,8 @@ public static async Task CreateEngineAsync( var client = await RemoteHostClient.TryGetClientAsync(workspace, cancellationToken).ConfigureAwait(false); if (client != null) { - var callbackObject = new CallbackObject(logService); - var session = await client.CreateConnectionAsync(WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine, callbackObject, cancellationToken).ConfigureAwait(false); - return new RemoteUpdateEngine(workspace, session); + var connection = await client.CreateConnectionAsync(logService, cancellationToken).ConfigureAwait(false); + return new RemoteUpdateEngine(connection); } // Couldn't go out of proc. Just do everything inside the current process. @@ -53,97 +50,64 @@ public static ISymbolSearchUpdateEngine CreateEngineInProcess(ISymbolSearchLogSe private sealed class NoOpUpdateEngine : ISymbolSearchUpdateEngine { - public Task> FindPackagesWithAssemblyAsync(string source, string assemblyName, CancellationToken cancellationToken) - => SpecializedTasks.EmptyImmutableArray(); + public ValueTask> FindPackagesWithAssemblyAsync(string source, string assemblyName, CancellationToken cancellationToken) + => new(ImmutableArray.Empty); - public Task> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken) - => SpecializedTasks.EmptyImmutableArray(); + public ValueTask> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken) + => new(ImmutableArray.Empty); - public Task> FindReferenceAssembliesWithTypeAsync(string name, int arity, CancellationToken cancellationToken) - => SpecializedTasks.EmptyImmutableArray(); + public ValueTask> FindReferenceAssembliesWithTypeAsync(string name, int arity, CancellationToken cancellationToken) + => new(ImmutableArray.Empty); - public Task UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory) - => Task.CompletedTask; + public ValueTask UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory, CancellationToken cancellationToken) + => default; } private sealed class RemoteUpdateEngine : ISymbolSearchUpdateEngine { - private readonly SemaphoreSlim _gate = new SemaphoreSlim(initialCount: 1); + private readonly RemoteServiceConnection _connection; - private readonly Workspace _workspace; - private readonly RemoteServiceConnection _session; - - public RemoteUpdateEngine( - Workspace workspace, - RemoteServiceConnection session) - { - _workspace = workspace; - _session = session; - } + public RemoteUpdateEngine(RemoteServiceConnection connection) + => _connection = connection; public void Dispose() - { - _session.Dispose(); - } + => _connection.Dispose(); - public async Task> FindPackagesWithTypeAsync( - string source, string name, int arity, CancellationToken cancellationToken) + public async ValueTask> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken) { - var results = await _session.RunRemoteAsync>( - nameof(IRemoteSymbolSearchUpdateEngine.FindPackagesWithTypeAsync), - solution: null, - new object[] { source, name, arity }, + var result = await _connection.TryInvokeAsync>( + (service, cancellationToken) => service.FindPackagesWithTypeAsync(source, name, arity, cancellationToken), cancellationToken).ConfigureAwait(false); - return results.ToImmutableArray(); + return result.HasValue ? result.Value : ImmutableArray.Empty; } - public async Task> FindPackagesWithAssemblyAsync( + public async ValueTask> FindPackagesWithAssemblyAsync( string source, string assemblyName, CancellationToken cancellationToken) { - var results = await _session.RunRemoteAsync>( - nameof(IRemoteSymbolSearchUpdateEngine.FindPackagesWithAssemblyAsync), - solution: null, - new object[] { source, assemblyName }, + var result = await _connection.TryInvokeAsync>( + (service, cancellationToken) => service.FindPackagesWithAssemblyAsync(source, assemblyName, cancellationToken), cancellationToken).ConfigureAwait(false); - return results.ToImmutableArray(); + return result.HasValue ? result.Value : ImmutableArray.Empty; } - public async Task> FindReferenceAssembliesWithTypeAsync( + public async ValueTask> FindReferenceAssembliesWithTypeAsync( string name, int arity, CancellationToken cancellationToken) { - var results = await _session.RunRemoteAsync>( - nameof(IRemoteSymbolSearchUpdateEngine.FindReferenceAssembliesWithTypeAsync), - solution: null, - new object[] { name, arity }, + var result = await _connection.TryInvokeAsync>( + (service, cancellationToken) => service.FindReferenceAssembliesWithTypeAsync(name, arity, cancellationToken), cancellationToken).ConfigureAwait(false); - return results.ToImmutableArray(); + return result.HasValue ? result.Value : ImmutableArray.Empty; } - public Task UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory) - => _session.RunRemoteAsync( - nameof(IRemoteSymbolSearchUpdateEngine.UpdateContinuouslyAsync), - solution: null, - new object[] { sourceName, localSettingsDirectory }, - CancellationToken.None); - } - - private class CallbackObject : ISymbolSearchLogService - { - private readonly ISymbolSearchLogService _logService; - - public CallbackObject(ISymbolSearchLogService logService) + public async ValueTask UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory, CancellationToken cancellationToken) { - _logService = logService; + _ = await _connection.TryInvokeAsync( + (service, cancellationToken) => service.UpdateContinuouslyAsync(sourceName, localSettingsDirectory, cancellationToken), + cancellationToken).ConfigureAwait(false); } - - public Task LogExceptionAsync(string exception, string text) - => _logService.LogExceptionAsync(exception, text); - - public Task LogInfoAsync(string text) - => _logService.LogInfoAsync(text); } } } diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index d9ea28b05df5a..572b99a7ec0e0 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -129,30 +129,50 @@ public async Task TestGetFixesAsyncHasNoDuplicateConfigurationActions() } [Fact] - public async Task TestGetCodeFixWithExceptionInRegisterMethod() + public async Task TestGetCodeFixWithExceptionInRegisterMethod_Diagnostic() + { + await GetFirstDiagnosticWithFixWithExceptionValidationAsync(new ErrorCases.ExceptionInRegisterMethod()); + } + + [Fact] + public async Task TestGetCodeFixWithExceptionInRegisterMethod_Fixes() { - await GetFirstDiagnosticWithFixAsync(new ErrorCases.ExceptionInRegisterMethod()); await GetAddedFixesWithExceptionValidationAsync(new ErrorCases.ExceptionInRegisterMethod()); } [Fact] - public async Task TestGetCodeFixWithExceptionInRegisterMethodAsync() + public async Task TestGetCodeFixWithExceptionInRegisterMethodAsync_Diagnostic() + { + await GetFirstDiagnosticWithFixWithExceptionValidationAsync(new ErrorCases.ExceptionInRegisterMethodAsync()); + } + + [Fact] + public async Task TestGetCodeFixWithExceptionInRegisterMethodAsync_Fixes() { - await GetFirstDiagnosticWithFixAsync(new ErrorCases.ExceptionInRegisterMethodAsync()); await GetAddedFixesWithExceptionValidationAsync(new ErrorCases.ExceptionInRegisterMethodAsync()); } [Fact] - public async Task TestGetCodeFixWithExceptionInFixableDiagnosticIds() + public async Task TestGetCodeFixWithExceptionInFixableDiagnosticIds_Diagnostic() + { + await GetFirstDiagnosticWithFixWithExceptionValidationAsync(new ErrorCases.ExceptionInFixableDiagnosticIds()); + } + + [Fact] + public async Task TestGetCodeFixWithExceptionInFixableDiagnosticIds_Fixes() { - await GetFirstDiagnosticWithFixAsync(new ErrorCases.ExceptionInFixableDiagnosticIds()); await GetAddedFixesWithExceptionValidationAsync(new ErrorCases.ExceptionInFixableDiagnosticIds()); } [Fact(Skip = "https://github.com/dotnet/roslyn/issues/21533")] - public async Task TestGetCodeFixWithExceptionInFixableDiagnosticIds2() + public async Task TestGetCodeFixWithExceptionInFixableDiagnosticIds_Diagnostic2() + { + await GetFirstDiagnosticWithFixWithExceptionValidationAsync(new ErrorCases.ExceptionInFixableDiagnosticIds2()); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/21533")] + public async Task TestGetCodeFixWithExceptionInFixableDiagnosticIds_Fixes2() { - await GetFirstDiagnosticWithFixAsync(new ErrorCases.ExceptionInFixableDiagnosticIds2()); await GetAddedFixesWithExceptionValidationAsync(new ErrorCases.ExceptionInFixableDiagnosticIds2()); } @@ -176,6 +196,11 @@ private static async Task> GetAddedFixesAsync( using var workspace = tuple.workspace; + var errorReportingService = (TestErrorReportingService)workspace.Services.GetRequiredService(); + + var errorReported = false; + errorReportingService.OnError = message => errorReported = true; + GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager); var incrementalAnalyzer = (IIncrementalAnalyzerProvider)tuple.analyzerService; var analyzer = incrementalAnalyzer.CreateIncrementalAnalyzer(workspace); @@ -190,17 +215,26 @@ private static async Task> GetAddedFixesAsync( Assert.False(extensionManager.IsIgnored(codefix)); } + Assert.Equal(exception || throwExceptionInFixerCreation, errorReported); + return fixes; } - private static async Task GetFirstDiagnosticWithFixAsync(CodeFixProvider codefix) + private static async Task GetFirstDiagnosticWithFixWithExceptionValidationAsync(CodeFixProvider codefix) { var tuple = ServiceSetup(codefix); using var workspace = tuple.workspace; + + var errorReportingService = (TestErrorReportingService)workspace.Services.GetRequiredService(); + + var errorReported = false; + errorReportingService.OnError = message => errorReported = true; + GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager); var unused = await tuple.codeFixService.GetMostSevereFixableDiagnosticAsync(document, TextSpan.FromBounds(0, 0), cancellationToken: CancellationToken.None); Assert.True(extensionManager.IsDisabled(codefix)); Assert.False(extensionManager.IsIgnored(codefix)); + Assert.True(errorReported); } private static (TestWorkspace workspace, DiagnosticAnalyzerService analyzerService, CodeFixService codeFixService, IErrorLoggerService errorLogger) ServiceSetup( diff --git a/src/EditorFeatures/Test/CodeRefactorings/CodeRefactoringServiceTest.cs b/src/EditorFeatures/Test/CodeRefactorings/CodeRefactoringServiceTest.cs index 75741bd1b3542..80423f046fbbc 100644 --- a/src/EditorFeatures/Test/CodeRefactorings/CodeRefactoringServiceTest.cs +++ b/src/EditorFeatures/Test/CodeRefactorings/CodeRefactoringServiceTest.cs @@ -50,7 +50,14 @@ public async Task TestProjectRefactoringAsync() private static async Task VerifyRefactoringDisabledAsync() where T : CodeRefactoringProvider { - using var workspace = TestWorkspace.CreateCSharp(@"class Program {}", composition: EditorTestCompositions.EditorFeatures.AddParts(typeof(T))); + using var workspace = TestWorkspace.CreateCSharp(@"class Program {}", + composition: EditorTestCompositions.EditorFeatures.AddParts(typeof(T))); + + var errorReportingService = (TestErrorReportingService)workspace.Services.GetRequiredService(); + + var errorReported = false; + errorReportingService.OnError = message => errorReported = true; + var refactoringService = workspace.GetService(); var codeRefactoring = workspace.ExportProvider.GetExportedValues().OfType().Single(); @@ -60,6 +67,8 @@ private static async Task VerifyRefactoringDisabledAsync() var result = await refactoringService.GetRefactoringsAsync(document, TextSpan.FromBounds(0, 0), CancellationToken.None); Assert.True(extensionManager.IsDisabled(codeRefactoring)); Assert.False(extensionManager.IsIgnored(codeRefactoring)); + + Assert.True(errorReported); } internal class StubRefactoring : CodeRefactoringProvider diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 06e6fcc240250..b8d11886594af 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -978,8 +978,8 @@ void M() // Then invoke analysis without cancellation token, and verify non-cancelled diagnostic. var diagnosticsMap = await diagnosticComputer.GetDiagnosticsAsync(analyzerIds, reportSuppressedDiagnostics: false, logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: CancellationToken.None); - var builder = diagnosticsMap.AnalysisResult.Values.Single(); - var diagnostic = kind == AnalysisKind.Syntax ? builder.SyntaxLocals.Values.Single().Single() : builder.SemanticLocals.Values.Single().Single(); + var builder = diagnosticsMap.Diagnostics.Single().diagnosticMap; + var diagnostic = kind == AnalysisKind.Syntax ? builder.Syntax.Single().Item2.Single() : builder.Semantic.Single().Item2.Single(); Assert.Equal(CancellationTestAnalyzer.NonCanceledDiagnosticId, diagnostic.Id); } diff --git a/src/EditorFeatures/Test/FindReferences/FindReferencesCommandHandlerTests.cs b/src/EditorFeatures/Test/FindReferences/FindReferencesCommandHandlerTests.cs index 0ffeb83a92b89..900a7f886f901 100644 --- a/src/EditorFeatures/Test/FindReferences/FindReferencesCommandHandlerTests.cs +++ b/src/EditorFeatures/Test/FindReferences/FindReferencesCommandHandlerTests.cs @@ -28,14 +28,14 @@ private class MockFindUsagesContext : FindUsagesContext { public readonly List Result = new List(); - public override Task OnDefinitionFoundAsync(DefinitionItem definition) + public override ValueTask OnDefinitionFoundAsync(DefinitionItem definition) { lock (Result) { Result.Add(definition); } - return Task.CompletedTask; + return default; } } diff --git a/src/EditorFeatures/Test/Microsoft.CodeAnalysis.EditorFeatures.UnitTests.csproj b/src/EditorFeatures/Test/Microsoft.CodeAnalysis.EditorFeatures.UnitTests.csproj index 8298048cd39a5..ca47481e9b233 100644 --- a/src/EditorFeatures/Test/Microsoft.CodeAnalysis.EditorFeatures.UnitTests.csproj +++ b/src/EditorFeatures/Test/Microsoft.CodeAnalysis.EditorFeatures.UnitTests.csproj @@ -9,8 +9,8 @@ true - - + + diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb index 2df3f304ac0e2..5587b2b994306 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb @@ -229,20 +229,20 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences Return definition.DisplayIfNoReferences End Function - Public Overrides Function OnDefinitionFoundAsync(definition As DefinitionItem) As Task + Public Overrides Function OnDefinitionFoundAsync(definition As DefinitionItem) As ValueTask SyncLock gate Me.Definitions.Add(definition) End SyncLock - Return Task.CompletedTask + Return Nothing End Function - Public Overrides Function OnReferenceFoundAsync(reference As SourceReferenceItem) As Task + Public Overrides Function OnReferenceFoundAsync(reference As SourceReferenceItem) As ValueTask SyncLock gate References.Add(reference) End SyncLock - Return Task.CompletedTask + Return Nothing End Function End Class diff --git a/src/EditorFeatures/Test2/ReferenceHighlighting/DocumentHighlightsServiceTests.vb b/src/EditorFeatures/Test2/ReferenceHighlighting/DocumentHighlightsServiceTests.vb index 5bca6ac79ba74..8ae5343fc9e51 100644 --- a/src/EditorFeatures/Test2/ReferenceHighlighting/DocumentHighlightsServiceTests.vb +++ b/src/EditorFeatures/Test2/ReferenceHighlighting/DocumentHighlightsServiceTests.vb @@ -4,16 +4,16 @@ Imports System.Collections.Immutable Imports System.Threading -Imports System.Threading.Tasks Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces +Imports Microsoft.CodeAnalysis.Remote.Testing Namespace Microsoft.CodeAnalysis.Editor.UnitTests.ReferenceHighlighting - <[UseExportProvider]> + Public Class DocumentHighlightsServiceTests - - Public Async Function TestMultipleLanguagesPassedToAPI() As Task + + Public Async Function TestMultipleLanguagesPassedToAPI(testHost As TestHost) As Task Dim workspaceElement = @@ -34,16 +34,20 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.ReferenceHighlighting - Using workspace = TestWorkspace.Create(workspaceElement) + Using workspace = TestWorkspace.Create(workspaceElement, composition:=EditorTestCompositions.EditorFeatures.WithTestHostParts(testHost)) Dim position = workspace.DocumentWithCursor.CursorPosition.Value Dim solution = workspace.CurrentSolution Dim csharpDocument = solution.Projects.Single(Function(p) p.Language = LanguageNames.CSharp).Documents.Single() Dim vbDocument = solution.Projects.Single(Function(p) p.Language = LanguageNames.VisualBasic).Documents.Single() - Dim service = csharpDocument.GetLanguageService(Of Microsoft.CodeAnalysis.DocumentHighlighting.IDocumentHighlightsService) - Await service.GetDocumentHighlightsAsync( + Dim service = csharpDocument.GetLanguageService(Of DocumentHighlighting.IDocumentHighlightsService) + Dim highlights = Await service.GetDocumentHighlightsAsync( csharpDocument, position, ImmutableHashSet.Create(csharpDocument, vbDocument), CancellationToken.None) + + AssertEx.Equal( + {"Test1.cs: Reference [102..108)"}, + highlights.Select(Function(h) $"{h.Document.Name}: {String.Join(",", h.HighlightSpans.Select(Function(span) $"{span.Kind} {span.TextSpan}"))}")) End Using End Function End Class diff --git a/src/EditorFeatures/TestUtilities/CodeLens/AbstractCodeLensTest.cs b/src/EditorFeatures/TestUtilities/CodeLens/AbstractCodeLensTest.cs index 6109f4dd130ba..ee3f833955d8e 100644 --- a/src/EditorFeatures/TestUtilities/CodeLens/AbstractCodeLensTest.cs +++ b/src/EditorFeatures/TestUtilities/CodeLens/AbstractCodeLensTest.cs @@ -35,8 +35,8 @@ protected static async Task RunCountTest(XElement input, int cap = 0) var result = await new CodeLensReferencesService().GetReferenceCountAsync(workspace.CurrentSolution, annotatedDocument.Id, declarationSyntaxNode, cap, CancellationToken.None); Assert.NotNull(result); - Assert.Equal(expected, result.Count); - Assert.Equal(isCapped, result.IsCapped); + Assert.Equal(expected, result.Value.Count); + Assert.Equal(isCapped, result.Value.IsCapped); } } } @@ -63,8 +63,8 @@ protected static async Task RunReferenceTest(XElement input) var declarationSyntaxNode = syntaxNode.FindNode(span); var result = await new CodeLensReferencesService().FindReferenceLocationsAsync(workspace.CurrentSolution, annotatedDocument.Id, declarationSyntaxNode, CancellationToken.None); - var count = result.Count(); - Assert.Equal(expected, count); + Assert.True(result.HasValue); + Assert.Equal(expected, result.Value.Length); } } } @@ -91,8 +91,8 @@ protected static async Task RunMethodReferenceTest(XElement input) var declarationSyntaxNode = syntaxNode.FindNode(span); var result = await new CodeLensReferencesService().FindReferenceMethodsAsync(workspace.CurrentSolution, annotatedDocument.Id, declarationSyntaxNode, CancellationToken.None); - var count = result.Count(); - Assert.Equal(expected, count); + Assert.True(result.HasValue); + Assert.Equal(expected, result.Value.Length); } } } diff --git a/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesLSPService.vb b/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesLSPService.vb index 73fd13b6d4a32..bd1bfaf985bae 100644 --- a/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesLSPService.vb +++ b/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesLSPService.vb @@ -14,8 +14,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.FindUsages - Public Sub New(threadingContext As IThreadingContext) - MyBase.New(threadingContext) + Public Sub New() End Sub End Class End Namespace diff --git a/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesService.vb b/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesService.vb index e37ca58cde30e..c2e7413f2a293 100644 --- a/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesService.vb +++ b/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesService.vb @@ -14,8 +14,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.FindUsages - Public Sub New(threadingContext As IThreadingContext) - MyBase.New(threadingContext) + Public Sub New() End Sub End Class End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests_NuGet.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests_NuGet.vb index 348e34cb163f5..6adcea71f3438 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests_NuGet.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests_NuGet.vb @@ -57,7 +57,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeActions.AddImp Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict) packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))). - Returns(Task.FromResult(Of IList(Of ReferenceAssemblyWithTypeResult))(New List(Of ReferenceAssemblyWithTypeResult))) + Returns(New ValueTask(Of ImmutableArray(Of ReferenceAssemblyWithTypeResult))(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty)) packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(NugetOrgSource, "NuGetType", 0, It.IsAny(Of CancellationToken)())). Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NuGetNamespace"))) @@ -86,7 +86,7 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict) packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))). - Returns(Task.FromResult(Of IList(Of ReferenceAssemblyWithTypeResult))(New List(Of ReferenceAssemblyWithTypeResult))) + Returns(New ValueTask(Of ImmutableArray(Of ReferenceAssemblyWithTypeResult))(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty)) packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(NugetOrgSource, "NuGetType", 0, It.IsAny(Of CancellationToken)())). Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NS1", "NS2"))) @@ -115,7 +115,7 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict) packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))). - Returns(Task.FromResult(Of IList(Of ReferenceAssemblyWithTypeResult))(New List(Of ReferenceAssemblyWithTypeResult))) + Returns(New ValueTask(Of ImmutableArray(Of ReferenceAssemblyWithTypeResult))(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty)) packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(NugetOrgSource, "NuGetType", 0, It.IsAny(Of CancellationToken)())). Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NS1", "NS2"))) @@ -140,7 +140,7 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict) packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))). - Returns(Task.FromResult(Of IList(Of ReferenceAssemblyWithTypeResult))(New List(Of ReferenceAssemblyWithTypeResult))) + Returns(New ValueTask(Of ImmutableArray(Of ReferenceAssemblyWithTypeResult))(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty)) packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(NugetOrgSource, "NuGetType", 0, It.IsAny(Of CancellationToken)())). Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NS1", "NS2"))) @@ -165,7 +165,7 @@ New TestParameters(fixProviderData:=New ProviderData(installerServiceMock.Object Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict) packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))). - Returns(Task.FromResult(Of IList(Of ReferenceAssemblyWithTypeResult))(New List(Of ReferenceAssemblyWithTypeResult))) + Returns(New ValueTask(Of ImmutableArray(Of ReferenceAssemblyWithTypeResult))(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty)) packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(NugetOrgSource, "NuGetType", 0, It.IsAny(Of CancellationToken)())). Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NS1", "NS2"))) @@ -207,7 +207,7 @@ parameters:=New TestParameters(index:=2, fixProviderData:=data)) Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict) packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))). - Returns(Task.FromResult(Of IList(Of ReferenceAssemblyWithTypeResult))(New List(Of ReferenceAssemblyWithTypeResult))) + Returns(New ValueTask(Of ImmutableArray(Of ReferenceAssemblyWithTypeResult))(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty)) packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(NugetOrgSource, "NuGetType", 0, It.IsAny(Of CancellationToken)())). Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NuGetNamespace"))) @@ -239,7 +239,7 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict) packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))). - Returns(Task.FromResult(Of IList(Of ReferenceAssemblyWithTypeResult))(New List(Of ReferenceAssemblyWithTypeResult))) + Returns(New ValueTask(Of ImmutableArray(Of ReferenceAssemblyWithTypeResult))(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty)) packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(NugetOrgSource, "NuGetType", 0, It.IsAny(Of CancellationToken)())). Returns(CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NuGetNamespace"))) @@ -257,17 +257,17 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa installerServiceMock.Verify() End Function - Private Shared Function CreateSearchResult(packageName As String, typeName As String, nameParts As ImmutableArray(Of String)) As Task(Of IList(Of PackageWithTypeResult)) + Private Shared Function CreateSearchResult(packageName As String, typeName As String, nameParts As ImmutableArray(Of String)) As ValueTask(Of ImmutableArray(Of PackageWithTypeResult)) Return CreateSearchResult(New PackageWithTypeResult( packageName:=packageName, + rank:=0, typeName:=typeName, version:=Nothing, - rank:=0, containingNamespaceNames:=nameParts)) End Function - Private Shared Function CreateSearchResult(ParamArray results As PackageWithTypeResult()) As Task(Of IList(Of PackageWithTypeResult)) - Return Task.FromResult(Of IList(Of PackageWithTypeResult))(ImmutableArray.Create(results)) + Private Shared Function CreateSearchResult(ParamArray results As PackageWithTypeResult()) As ValueTask(Of ImmutableArray(Of PackageWithTypeResult)) + Return New ValueTask(Of ImmutableArray(Of PackageWithTypeResult))(ImmutableArray.Create(results)) End Function Private Shared Function CreateNameParts(ParamArray parts As String()) As ImmutableArray(Of String) diff --git a/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs b/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs index 1dd0a88aa85c3..850960da63803 100644 --- a/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs +++ b/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs @@ -59,26 +59,15 @@ public async Task> GetFixesAsync( var client = await RemoteHostClient.TryGetClientAsync(document.Project, cancellationToken).ConfigureAwait(false); if (client != null) { - var callbackTarget = new RemoteSymbolSearchService(symbolSearchService); + var callbackTarget = new RemoteAddImportServiceCallback(symbolSearchService); - var result = await client.RunRemoteAsync>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteAddImportFeatureService.GetFixesAsync), + var result = await client.TryInvokeAsync>( document.Project.Solution, - new object[] - { - document.Id, - span, - diagnosticId, - maxResults, - placeSystemNamespaceFirst, - searchReferenceAssemblies, - packageSources - }, + (service, solutionInfo, cancellationToken) => service.GetFixesAsync(solutionInfo, document.Id, span, diagnosticId, maxResults, placeSystemNamespaceFirst, searchReferenceAssemblies, packageSources, cancellationToken), callbackTarget, cancellationToken).ConfigureAwait(false); - return result.ToImmutableArray(); + return result.HasValue ? result.Value : ImmutableArray.Empty; } return await GetFixesInCurrentProcessAsync( diff --git a/src/Features/Core/Portable/AddImport/AddImportFixData.cs b/src/Features/Core/Portable/AddImport/AddImportFixData.cs index 7d7ce3086fcfb..bf74a8c8dfa2a 100644 --- a/src/Features/Core/Portable/AddImport/AddImportFixData.cs +++ b/src/Features/Core/Portable/AddImport/AddImportFixData.cs @@ -4,14 +4,17 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Runtime.Serialization; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Tags; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.AddImport { - internal class AddImportFixData + [DataContract] + internal sealed class AddImportFixData { + [DataMember(Order = 0)] public AddImportFixKind Kind { get; } /// @@ -20,29 +23,34 @@ internal class AddImportFixData /// May be empty for fixes that don't need to add an import and only do something like /// add a project/metadata reference. /// - public IList TextChanges { get; } + [DataMember(Order = 1)] + public readonly ImmutableArray TextChanges; /// /// String to display in the lightbulb menu. /// - public string Title { get; private set; } + [DataMember(Order = 2)] + public readonly string Title; /// /// Tags that control what glyph is displayed in the lightbulb menu. /// - public IList Tags { get; private set; } + [DataMember(Order = 3)] + public readonly ImmutableArray Tags; /// /// The priority this item should have in the lightbulb list. /// - public CodeActionPriority Priority { get; private set; } + [DataMember(Order = 4)] + public readonly CodeActionPriority Priority; #region When adding P2P references. /// /// The optional id for a we'd like to add a reference to. /// - public ProjectId ProjectReferenceToAdd { get; private set; } + [DataMember(Order = 5)] + public readonly ProjectId ProjectReferenceToAdd; #endregion @@ -53,84 +61,104 @@ internal class AddImportFixData /// is the id for the we can find that /// referenced from. /// - public ProjectId PortableExecutableReferenceProjectId { get; private set; } + [DataMember(Order = 6)] + public readonly ProjectId PortableExecutableReferenceProjectId; /// /// If we want to add a metadata reference, this /// is the for it. /// - public string PortableExecutableReferenceFilePathToAdd { get; private set; } + [DataMember(Order = 7)] + public readonly string PortableExecutableReferenceFilePathToAdd; #endregion #region When adding an assembly reference - public string AssemblyReferenceAssemblyName { get; private set; } - public string AssemblyReferenceFullyQualifiedTypeName { get; private set; } + [DataMember(Order = 8)] + public readonly string AssemblyReferenceAssemblyName; + + [DataMember(Order = 9)] + public readonly string AssemblyReferenceFullyQualifiedTypeName; #endregion #region When adding a package reference - public string PackageSource { get; private set; } - public string PackageName { get; private set; } - public string PackageVersionOpt { get; private set; } + [DataMember(Order = 10)] + public readonly string PackageSource; + + [DataMember(Order = 11)] + public readonly string PackageName; + + [DataMember(Order = 12)] + public readonly string PackageVersionOpt; #endregion - private AddImportFixData( + // Must be public since it's used for deserialization. + public AddImportFixData( AddImportFixKind kind, - ImmutableArray textChanges) + ImmutableArray textChanges, + string title = null, + ImmutableArray tags = default, + CodeActionPriority priority = default, + ProjectId projectReferenceToAdd = null, + ProjectId portableExecutableReferenceProjectId = null, + string portableExecutableReferenceFilePathToAdd = null, + string assemblyReferenceAssemblyName = null, + string assemblyReferenceFullyQualifiedTypeName = null, + string packageSource = null, + string packageName = null, + string packageVersionOpt = null) { Kind = kind; TextChanges = textChanges; - Tags = ImmutableArray.Empty; + Title = title; + Tags = tags; + Priority = priority; + ProjectReferenceToAdd = projectReferenceToAdd; + PortableExecutableReferenceProjectId = portableExecutableReferenceProjectId; + PortableExecutableReferenceFilePathToAdd = portableExecutableReferenceFilePathToAdd; + AssemblyReferenceAssemblyName = assemblyReferenceAssemblyName; + AssemblyReferenceFullyQualifiedTypeName = assemblyReferenceFullyQualifiedTypeName; + PackageSource = packageSource; + PackageName = packageName; + PackageVersionOpt = packageVersionOpt; } public static AddImportFixData CreateForProjectSymbol(ImmutableArray textChanges, string title, ImmutableArray tags, CodeActionPriority priority, ProjectId projectReferenceToAdd) - { - return new AddImportFixData(AddImportFixKind.ProjectSymbol, textChanges) - { - Title = title, - Tags = tags, - Priority = priority, - ProjectReferenceToAdd = projectReferenceToAdd - }; - } + => new(AddImportFixKind.ProjectSymbol, + textChanges, + title: title, + tags: tags, + priority: priority, + projectReferenceToAdd: projectReferenceToAdd); public static AddImportFixData CreateForMetadataSymbol(ImmutableArray textChanges, string title, ImmutableArray tags, CodeActionPriority priority, ProjectId portableExecutableReferenceProjectId, string portableExecutableReferenceFilePathToAdd) - { - return new AddImportFixData(AddImportFixKind.MetadataSymbol, textChanges) - { - Title = title, - Tags = tags, - Priority = priority, - PortableExecutableReferenceProjectId = portableExecutableReferenceProjectId, - PortableExecutableReferenceFilePathToAdd = portableExecutableReferenceFilePathToAdd - }; - } + => new(AddImportFixKind.MetadataSymbol, + textChanges, + title: title, + tags: tags, + priority: priority, + portableExecutableReferenceProjectId: portableExecutableReferenceProjectId, + portableExecutableReferenceFilePathToAdd: portableExecutableReferenceFilePathToAdd); public static AddImportFixData CreateForReferenceAssemblySymbol(ImmutableArray textChanges, string title, string assemblyReferenceAssemblyName, string assemblyReferenceFullyQualifiedTypeName) - { - return new AddImportFixData(AddImportFixKind.ReferenceAssemblySymbol, textChanges) - { - Title = title, - Tags = WellKnownTagArrays.AddReference, - Priority = CodeActionPriority.Low, - AssemblyReferenceAssemblyName = assemblyReferenceAssemblyName, - AssemblyReferenceFullyQualifiedTypeName = assemblyReferenceFullyQualifiedTypeName - }; - } + => new(AddImportFixKind.ReferenceAssemblySymbol, + textChanges, + title: title, + tags: WellKnownTagArrays.AddReference, + priority: CodeActionPriority.Low, + assemblyReferenceAssemblyName: assemblyReferenceAssemblyName, + assemblyReferenceFullyQualifiedTypeName: assemblyReferenceFullyQualifiedTypeName); public static AddImportFixData CreateForPackageSymbol(ImmutableArray textChanges, string packageSource, string packageName, string packageVersionOpt) - { - return new AddImportFixData(AddImportFixKind.PackageSymbol, textChanges) - { - PackageSource = packageSource, - Priority = CodeActionPriority.Low, - PackageName = packageName, - PackageVersionOpt = packageVersionOpt, - }; - } + => new(AddImportFixKind.PackageSymbol, + textChanges, + packageSource: packageSource, + priority: CodeActionPriority.Low, + packageName: packageName, + packageVersionOpt: packageVersionOpt); } } diff --git a/src/Features/Core/Portable/AddImport/Remote/AbstractAddImportFeatureService_Remote.cs b/src/Features/Core/Portable/AddImport/Remote/AbstractAddImportFeatureService_Remote.cs index ce7827e4e1849..26cc554d43b2d 100644 --- a/src/Features/Core/Portable/AddImport/Remote/AbstractAddImportFeatureService_Remote.cs +++ b/src/Features/Core/Portable/AddImport/Remote/AbstractAddImportFeatureService_Remote.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.SymbolSearch; @@ -24,39 +25,21 @@ internal abstract partial class AbstractAddImportFeatureService - private sealed class RemoteSymbolSearchService : IRemoteSymbolSearchUpdateEngine + private sealed class RemoteAddImportServiceCallback : IRemoteMissingImportDiscoveryService.ICallback { private readonly ISymbolSearchService _symbolSearchService; - public RemoteSymbolSearchService(ISymbolSearchService symbolSearchService) + public RemoteAddImportServiceCallback(ISymbolSearchService symbolSearchService) => _symbolSearchService = symbolSearchService; - public Task UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory) - { - // Remote side should never call this. - throw new NotImplementedException(); - } + public ValueTask> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken) + => _symbolSearchService.FindPackagesWithTypeAsync(source, name, arity, cancellationToken); - public Task> FindPackagesWithTypeAsync( - string source, string name, int arity, CancellationToken cancellationToken) - { - return _symbolSearchService.FindPackagesWithTypeAsync( - source, name, arity, cancellationToken); - } + public ValueTask> FindPackagesWithAssemblyAsync(string source, string name, CancellationToken cancellationToken) + => _symbolSearchService.FindPackagesWithAssemblyAsync(source, name, cancellationToken); - public Task> FindPackagesWithAssemblyAsync( - string source, string name, CancellationToken cancellationToken) - { - return _symbolSearchService.FindPackagesWithAssemblyAsync( - source, name, cancellationToken); - } - - public Task> FindReferenceAssembliesWithTypeAsync( - string name, int arity, CancellationToken cancellationToken) - { - return _symbolSearchService.FindReferenceAssembliesWithTypeAsync( - name, arity, cancellationToken); - } + public ValueTask> FindReferenceAssembliesWithTypeAsync(string name, int arity, CancellationToken cancellationToken) + => _symbolSearchService.FindReferenceAssembliesWithTypeAsync(name, arity, cancellationToken); } } } diff --git a/src/Features/Core/Portable/AddImport/Remote/IRemoteAddImportFeatureService.cs b/src/Features/Core/Portable/AddImport/Remote/IRemoteAddImportFeatureService.cs deleted file mode 100644 index acdcf598092d3..0000000000000 --- a/src/Features/Core/Portable/AddImport/Remote/IRemoteAddImportFeatureService.cs +++ /dev/null @@ -1,20 +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. - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Packaging; -using Microsoft.CodeAnalysis.Remote; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.AddImport -{ - internal interface IRemoteAddImportFeatureService - { - Task> GetFixesAsync( - PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan span, string diagnosticId, int maxResults, bool placeSystemNamespaceFirst, - bool searchReferenceAssemblies, IList packageSources, CancellationToken cancellationToken); - } -} diff --git a/src/Features/Core/Portable/AddImport/Remote/IRemoteMissingImportDiscoveryService.cs b/src/Features/Core/Portable/AddImport/Remote/IRemoteMissingImportDiscoveryService.cs new file mode 100644 index 0000000000000..d9b57e4966f20 --- /dev/null +++ b/src/Features/Core/Portable/AddImport/Remote/IRemoteMissingImportDiscoveryService.cs @@ -0,0 +1,28 @@ +// 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.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Packaging; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.SymbolSearch; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.AddImport +{ + internal interface IRemoteMissingImportDiscoveryService + { + internal interface ICallback + { + ValueTask> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken); + ValueTask> FindPackagesWithAssemblyAsync(string source, string name, CancellationToken cancellationToken); + ValueTask> FindReferenceAssembliesWithTypeAsync(string name, int arity, CancellationToken cancellationToken); + } + + ValueTask> GetFixesAsync( + PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan span, string diagnosticId, int maxResults, bool placeSystemNamespaceFirst, + bool searchReferenceAssemblies, ImmutableArray packageSources, CancellationToken cancellationToken); + } +} diff --git a/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs b/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs index f0419302bfa0e..fe28b0551299a 100644 --- a/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs +++ b/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Globalization; using System.Linq; using System.Threading; @@ -23,9 +24,9 @@ internal sealed class CodeLensReferencesService : ICodeLensReferencesService typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, memberOptions: SymbolDisplayMemberOptions.IncludeContainingType); - private static async Task FindAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, + private static async Task FindAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, Func> onResults, Func> onCapped, - int searchCap, CancellationToken cancellationToken) where T : class + int searchCap, CancellationToken cancellationToken) where T : struct { var document = solution.GetDocument(documentId); if (document == null) @@ -65,7 +66,7 @@ await SymbolFinder.FindReferencesAsync(symbol, solution, progress, null, } } - public Task GetReferenceCountAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, int maxSearchResults, CancellationToken cancellationToken) + public Task GetReferenceCountAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, int maxSearchResults, CancellationToken cancellationToken) { return FindAsync(solution, documentId, syntaxNode, progress => Task.FromResult(new ReferenceCount( @@ -180,7 +181,7 @@ private static SyntaxNode GetEnclosingCodeElementNode(Document document, SyntaxT return langServices.GetDisplayNode(node); } - public async Task> FindReferenceLocationsAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, CancellationToken cancellationToken) + public async Task?> FindReferenceLocationsAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, CancellationToken cancellationToken) { return await FindAsync(solution, documentId, syntaxNode, async progress => @@ -191,7 +192,7 @@ public async Task> FindReferenceLocatio var result = await Task.WhenAll(referenceTasks).ConfigureAwait(false); - return (IEnumerable)result; + return result.ToImmutableArray(); }, onCapped: null, searchCap: 0, cancellationToken: cancellationToken).ConfigureAwait(false); } @@ -238,7 +239,7 @@ private static async Task TryGetMethodDescriptorAsync return !string.IsNullOrEmpty(fullName) ? new ReferenceMethodDescriptor(fullName, document.FilePath, document.Project.OutputFilePath) : null; } - public Task> FindReferenceMethodsAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, CancellationToken cancellationToken) + public Task?> FindReferenceMethodsAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, CancellationToken cancellationToken) { return FindAsync(solution, documentId, syntaxNode, async progress => @@ -250,7 +251,7 @@ public Task> FindReferenceMethodsAsync(So var result = await Task.WhenAll(descriptorTasks).ConfigureAwait(false); - return result.OfType(); + return result.OfType().ToImmutableArray(); }, onCapped: null, searchCap: 0, cancellationToken: cancellationToken); } diff --git a/src/Features/Core/Portable/CodeLens/ICodeLensReferencesService.cs b/src/Features/Core/Portable/CodeLens/ICodeLensReferencesService.cs index 4fa7d213438e8..d96dce7f3cefe 100644 --- a/src/Features/Core/Portable/CodeLens/ICodeLensReferencesService.cs +++ b/src/Features/Core/Portable/CodeLens/ICodeLensReferencesService.cs @@ -2,7 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -18,23 +21,21 @@ internal interface ICodeLensReferencesService : IWorkspaceService /// if is greater than 0. /// /// - Task GetReferenceCountAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, int maxSearchResults, CancellationToken cancellationToken); + Task GetReferenceCountAsync(Solution solution, DocumentId documentId, SyntaxNode? syntaxNode, int maxSearchResults, CancellationToken cancellationToken); /// /// Given a document and syntax node, returns a collection of locations where the located node is referenced. /// - Task> FindReferenceLocationsAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, CancellationToken cancellationToken); + Task?> FindReferenceLocationsAsync(Solution solution, DocumentId documentId, SyntaxNode? syntaxNode, CancellationToken cancellationToken); /// /// Given a document and syntax node, returns a collection of locations of methods that refer to the located node. /// - Task> FindReferenceMethodsAsync(Solution solution, - DocumentId documentId, SyntaxNode syntaxNode, CancellationToken cancellationToken); + Task?> FindReferenceMethodsAsync(Solution solution, DocumentId documentId, SyntaxNode? syntaxNode, CancellationToken cancellationToken); /// /// Given a document and syntax node, returns the fully qualified name of the located node's declaration. /// - Task GetFullyQualifiedNameAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, - CancellationToken cancellationToken); + Task GetFullyQualifiedNameAsync(Solution solution, DocumentId documentId, SyntaxNode? syntaxNode, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/CodeLens/IRemoteCodeLensReferencesService.cs b/src/Features/Core/Portable/CodeLens/IRemoteCodeLensReferencesService.cs index 2b4bce0def2a7..f4d8259ef4e9c 100644 --- a/src/Features/Core/Portable/CodeLens/IRemoteCodeLensReferencesService.cs +++ b/src/Features/Core/Portable/CodeLens/IRemoteCodeLensReferencesService.cs @@ -2,7 +2,7 @@ // 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.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Remote; @@ -12,9 +12,9 @@ namespace Microsoft.CodeAnalysis.CodeLens { internal interface IRemoteCodeLensReferencesService { - Task GetReferenceCountAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, int maxResultCount, CancellationToken cancellationToken); - Task> FindReferenceLocationsAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken); - Task> FindReferenceMethodsAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken); - Task GetFullyQualifiedNameAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken); + ValueTask GetReferenceCountAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, int maxResultCount, CancellationToken cancellationToken); + ValueTask?> FindReferenceLocationsAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken); + ValueTask?> FindReferenceMethodsAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken); + ValueTask GetFullyQualifiedNameAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/CodeLens/ReferenceCount.cs b/src/Features/Core/Portable/CodeLens/ReferenceCount.cs index 619acedfefc87..d6f73c23cb68b 100644 --- a/src/Features/Core/Portable/CodeLens/ReferenceCount.cs +++ b/src/Features/Core/Portable/CodeLens/ReferenceCount.cs @@ -2,21 +2,26 @@ // 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.Runtime.Serialization; + namespace Microsoft.CodeAnalysis.CodeLens { /// /// Represents the result of a FindReferences Count operation. /// - internal class ReferenceCount + [DataContract] + internal readonly struct ReferenceCount { /// /// Represents the number of references to a given symbol. /// + [DataMember(Order = 0)] public int Count { get; } /// /// Represents if the count is capped by a certain maximum. /// + [DataMember(Order = 1)] public bool IsCapped { get; } public ReferenceCount(int count, bool isCapped) diff --git a/src/Features/Core/Portable/CodeLens/ReferenceLocationDescriptor.cs b/src/Features/Core/Portable/CodeLens/ReferenceLocationDescriptor.cs index bf11aaba08586..2e3767f4277a3 100644 --- a/src/Features/Core/Portable/CodeLens/ReferenceLocationDescriptor.cs +++ b/src/Features/Core/Portable/CodeLens/ReferenceLocationDescriptor.cs @@ -3,91 +3,110 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.Serialization; namespace Microsoft.CodeAnalysis.CodeLens { /// /// Holds information required to display and navigate to individual references /// + [DataContract] internal sealed class ReferenceLocationDescriptor { - public Guid ProjectGuid { get; } + /// + /// Fully qualified name of the symbol containing the reference location + /// + [DataMember(Order = 0)] + public string LongDescription { get; } - public Guid DocumentGuid { get; } + /// + /// Language of the reference location + /// + [DataMember(Order = 1)] + public string Language { get; } /// - /// Document's file path + /// The kind of symbol containing the reference location (such as type, method, property, etc.) /// - public string FilePath { get; } + [DataMember(Order = 2)] + public Glyph? Glyph { get; } /// /// Reference's span start based on the document content /// + [DataMember(Order = 3)] public int SpanStart { get; } /// /// Reference's span length based on the document content /// + [DataMember(Order = 4)] public int SpanLength { get; } /// /// Reference's line based on the document content /// + [DataMember(Order = 5)] public int LineNumber { get; } /// /// Reference's character based on the document content /// + [DataMember(Order = 6)] public int ColumnNumber { get; } - /// - /// Language of the reference location - /// - public string Language { get; } + [DataMember(Order = 7)] + public Guid ProjectGuid { get; } - /// - /// Fully qualified name of the symbol containing the reference location - /// - public string LongDescription { get; } + [DataMember(Order = 8)] + public Guid DocumentGuid { get; } /// - /// The kind of symbol containing the reference location (such as type, method, property, etc.) + /// Document's file path /// - public Glyph? Glyph { get; } + [DataMember(Order = 9)] + public string FilePath { get; } /// /// the full line of source that contained the reference /// + [DataMember(Order = 10)] public string ReferenceLineText { get; } /// /// the beginning of the span within reference text that was the use of the reference /// + [DataMember(Order = 11)] public int ReferenceStart { get; } /// /// the length of the span of the reference /// + [DataMember(Order = 12)] public int ReferenceLength { get; } /// /// Text above the line with the reference /// + [DataMember(Order = 13)] public string BeforeReferenceText1 { get; } /// /// Text above the line with the reference /// + [DataMember(Order = 14)] public string BeforeReferenceText2 { get; } /// /// Text below the line with the reference /// + [DataMember(Order = 15)] public string AfterReferenceText1 { get; } /// /// Text below the line with the reference /// + [DataMember(Order = 16)] public string AfterReferenceText2 { get; } public ReferenceLocationDescriptor( diff --git a/src/Features/Core/Portable/CodeLens/ReferenceMethodDescriptor.cs b/src/Features/Core/Portable/CodeLens/ReferenceMethodDescriptor.cs index 1d686c6a876dd..482056798494d 100644 --- a/src/Features/Core/Portable/CodeLens/ReferenceMethodDescriptor.cs +++ b/src/Features/Core/Portable/CodeLens/ReferenceMethodDescriptor.cs @@ -2,13 +2,34 @@ // 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.Runtime.Serialization; + namespace Microsoft.CodeAnalysis.CodeLens { /// /// A caller method of a callee /// + [DataContract] internal sealed class ReferenceMethodDescriptor { + /// + /// Returns method's fully quilified name without parameters + /// + [DataMember(Order = 0)] + public string FullName { get; private set; } + + /// + /// Returns method's file path. + /// + [DataMember(Order = 1)] + public string FilePath { get; private set; } + + /// + /// Returns output file path for the project containing the method. + /// + [DataMember(Order = 2)] + public string OutputFilePath { get; private set; } + /// /// Describe a caller method of a callee /// @@ -24,20 +45,5 @@ public ReferenceMethodDescriptor(string fullName, string filePath, string output FilePath = filePath; OutputFilePath = outputFilePath; } - - /// - /// Returns method's fully quilified name without parameters - /// - public string FullName { get; private set; } - - /// - /// Returns method's file path. - /// - public string FilePath { get; private set; } - - /// - /// Returns output file path for the project containing the method. - /// - public string OutputFilePath { get; private set; } } } diff --git a/src/Features/Core/Portable/Common/TaggedText.cs b/src/Features/Core/Portable/Common/TaggedText.cs index 4ea3b8d125e7b..65405868ae02f 100644 --- a/src/Features/Core/Portable/Common/TaggedText.cs +++ b/src/Features/Core/Portable/Common/TaggedText.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -16,33 +17,39 @@ namespace Microsoft.CodeAnalysis /// /// A piece of text with a descriptive tag. /// + [DataContract] public readonly struct TaggedText { /// /// A descriptive tag from . /// + [DataMember(Order = 0)] public string Tag { get; } /// /// The actual text to be displayed. /// + [DataMember(Order = 1)] public string Text { get; } /// /// Gets the style(s) to apply to the text. /// + [DataMember(Order = 2)] internal TaggedTextStyle Style { get; } /// /// Gets the navigation target for the text, or if the text does not have a navigation /// target. /// + [DataMember(Order = 3)] internal string NavigationTarget { get; } /// /// Gets the navigation hint for the text, or if the text does not have a navigation /// hint. /// + [DataMember(Order = 4)] internal string NavigationHint { get; } /// diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.cs index 1e920cb0e1b34..4368a910cce6d 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion.Log; @@ -35,39 +34,52 @@ public static async Task> GetUn bool forceIndexCreation, CancellationToken cancellationToken) { - async Task<(ImmutableArray, StatisticCounter)> GetItemsAsync() + SerializableUnimportedExtensionMethods items; + + var ticks = Environment.TickCount; + + var project = document.Project; + var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); + if (client != null) { - var project = document.Project; + var receiverTypeSymbolKeyData = SymbolKey.CreateString(receiverTypeSymbol, cancellationToken); - var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); - if (client != null) + var result = await client.TryInvokeAsync( + project.Solution, + (service, solutionInfo, cancellationToken) => service.GetUnimportedExtensionMethodsAsync( + solutionInfo, document.Id, position, receiverTypeSymbolKeyData, namespaceInScope.ToImmutableArray(), forceIndexCreation, cancellationToken), + callbackTarget: null, + cancellationToken).ConfigureAwait(false); + + if (!result.HasValue) { - var result = await client.RunRemoteAsync<(IList items, StatisticCounter counter)>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteExtensionMethodImportCompletionService.GetUnimportedExtensionMethodsAsync), - project.Solution, - new object[] { document.Id, position, SymbolKey.CreateString(receiverTypeSymbol, cancellationToken), namespaceInScope.ToArray(), forceIndexCreation }, - callbackTarget: null, - cancellationToken).ConfigureAwait(false); - - return (result.items.ToImmutableArray(), result.counter); + return ImmutableArray.Empty; } - return await GetUnimportedExtensionMethodsInCurrentProcessAsync(document, position, receiverTypeSymbol, namespaceInScope, forceIndexCreation, cancellationToken).ConfigureAwait(false); + items = result.Value; + } + else + { + items = await GetUnimportedExtensionMethodsInCurrentProcessAsync(document, position, receiverTypeSymbol, namespaceInScope, forceIndexCreation, cancellationToken).ConfigureAwait(false); } - var ticks = Environment.TickCount; + // report telemetry: + var totalTicks = Environment.TickCount - ticks; - var (items, counter) = await GetItemsAsync().ConfigureAwait(false); + CompletionProvidersLogger.LogExtensionMethodCompletionTicksDataPoint(totalTicks); + CompletionProvidersLogger.LogExtensionMethodCompletionMethodsProvidedDataPoint(items.CompletionItems.Length); + CompletionProvidersLogger.LogExtensionMethodCompletionGetSymbolsTicksDataPoint(items.GetSymbolsTicks); + CompletionProvidersLogger.LogExtensionMethodCompletionCreateItemsTicksDataPoint(items.CreateItemsTicks); - counter.TotalTicks = Environment.TickCount - ticks; - counter.TotalExtensionMethodsProvided = items.Length; - counter.Report(); + if (items.IsPartialResult) + { + CompletionProvidersLogger.LogExtensionMethodCompletionPartialResultCount(); + } - return items; + return items.CompletionItems; } - public static async Task<(ImmutableArray, StatisticCounter)> GetUnimportedExtensionMethodsInCurrentProcessAsync( + public static async Task GetUnimportedExtensionMethodsInCurrentProcessAsync( Document document, int position, ITypeSymbol receiverTypeSymbol, @@ -75,7 +87,6 @@ public static async Task> GetUn bool forceIndexCreation, CancellationToken cancellationToken) { - var counter = new StatisticCounter(); var ticks = Environment.TickCount; // First find symbols of all applicable extension methods. @@ -84,7 +95,7 @@ public static async Task> GetUn document, position, receiverTypeSymbol, namespaceInScope, cancellationToken).ConfigureAwait(false); var (extentsionMethodSymbols, isPartialResult) = await symbolComputer.GetExtensionMethodSymbolsAsync(forceIndexCreation, cancellationToken).ConfigureAwait(false); - counter.GetSymbolsTicks = Environment.TickCount - ticks; + var getSymbolsTicks = Environment.TickCount - ticks; ticks = Environment.TickCount; var items = ConvertSymbolsToCompletionItems(extentsionMethodSymbols, cancellationToken); @@ -103,13 +114,11 @@ public static async Task> GetUn s_indexingTask = symbolComputer.PopulateIndicesAsync(CancellationToken.None); } } - - counter.PartialResult = true; } - counter.CreateItemsTicks = Environment.TickCount - ticks; + var createItemsTicks = Environment.TickCount - ticks; - return (items, counter); + return new SerializableUnimportedExtensionMethods(items, isPartialResult, getSymbolsTicks, createItemsTicks); } @@ -179,26 +188,4 @@ private static string GetFullyQualifiedNamespaceName(INamespaceSymbol symbol, Di return name; } } - - internal sealed class StatisticCounter - { - public bool PartialResult { get; set; } - public int TotalTicks { get; set; } - public int TotalExtensionMethodsProvided { get; set; } - public int GetSymbolsTicks { get; set; } - public int CreateItemsTicks { get; set; } - - public void Report() - { - CompletionProvidersLogger.LogExtensionMethodCompletionTicksDataPoint(TotalTicks); - CompletionProvidersLogger.LogExtensionMethodCompletionMethodsProvidedDataPoint(TotalExtensionMethodsProvided); - CompletionProvidersLogger.LogExtensionMethodCompletionGetSymbolsTicksDataPoint(GetSymbolsTicks); - CompletionProvidersLogger.LogExtensionMethodCompletionCreateItemsTicksDataPoint(CreateItemsTicks); - - if (PartialResult) - { - CompletionProvidersLogger.LogExtensionMethodCompletionPartialResultCount(); - } - } - } } diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/IRemoteExtensionMethodImportCompletionService.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/IRemoteExtensionMethodImportCompletionService.cs index f80674e78bfea..6beb28a16c334 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/IRemoteExtensionMethodImportCompletionService.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/IRemoteExtensionMethodImportCompletionService.cs @@ -4,7 +4,7 @@ #nullable enable -using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Remote; @@ -13,12 +13,12 @@ namespace Microsoft.CodeAnalysis.Completion.Providers { internal interface IRemoteExtensionMethodImportCompletionService { - Task<(IList, StatisticCounter)> GetUnimportedExtensionMethodsAsync( + public ValueTask GetUnimportedExtensionMethodsAsync( PinnedSolutionInfo solutionInfo, DocumentId documentId, int position, string receiverTypeSymbolKeyData, - string[] namespaceInScope, + ImmutableArray namespaceInScope, bool forceIndexCreation, CancellationToken cancellationToken); } diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/SerializableImportCompletionItem.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/SerializableImportCompletionItem.cs index a6586b3ad1fc3..405e8e950b5d7 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/SerializableImportCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/SerializableImportCompletionItem.cs @@ -4,15 +4,29 @@ #nullable enable +using System.Runtime.Serialization; + namespace Microsoft.CodeAnalysis.Completion.Providers { + [DataContract] internal readonly struct SerializableImportCompletionItem { + [DataMember(Order = 0)] public readonly string SymbolKeyData; - public readonly int Arity; + + [DataMember(Order = 1)] public readonly string Name; + + [DataMember(Order = 2)] + public readonly int Arity; + + [DataMember(Order = 3)] public readonly Glyph Glyph; + + [DataMember(Order = 4)] public readonly string ContainingNamespace; + + [DataMember(Order = 5)] public readonly int AdditionalOverloadCount; public SerializableImportCompletionItem(string symbolKeyData, string name, int arity, Glyph glyph, string containingNamespace, int additionalOverloadCount) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/SerializableUnimportedExtensionMethods.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/SerializableUnimportedExtensionMethods.cs new file mode 100644 index 0000000000000..f6c574acb8acc --- /dev/null +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/SerializableUnimportedExtensionMethods.cs @@ -0,0 +1,39 @@ +// 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 enable + +using System.Collections.Immutable; +using System.Runtime.Serialization; + +namespace Microsoft.CodeAnalysis.Completion.Providers +{ + [DataContract] + internal readonly struct SerializableUnimportedExtensionMethods + { + [DataMember(Order = 0)] + public readonly ImmutableArray CompletionItems; + + [DataMember(Order = 1)] + public readonly bool IsPartialResult; + + [DataMember(Order = 2)] + public readonly int GetSymbolsTicks; + + [DataMember(Order = 3)] + public readonly int CreateItemsTicks; + + public SerializableUnimportedExtensionMethods( + ImmutableArray completionItems, + bool isPartialResult, + int getSymbolsTicks, + int createItemsTicks) + { + CompletionItems = completionItems; + IsPartialResult = isPartialResult; + GetSymbolsTicks = getSymbolsTicks; + CreateItemsTicks = createItemsTicks; + } + } +} diff --git a/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs index 2508ba8595e3a..94599f1a20489 100644 --- a/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs @@ -168,24 +168,22 @@ public async Task ConvertToStructAsync( var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); if (client != null) { - var result = await client.RunRemoteAsync( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteConvertTupleToStructCodeRefactoringProvider.ConvertToStructAsync), + var result = await client.TryInvokeAsync( solution, - new object[] - { - document.Id, - span, - scope, - }, + (service, solutionInfo, cancellationToken) => service.ConvertToStructAsync(solutionInfo, document.Id, span, scope, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); + if (!result.HasValue) + { + return solution; + } + var resultSolution = await RemoteUtilities.UpdateSolutionAsync( - solution, result.DocumentTextChanges, cancellationToken).ConfigureAwait(false); + solution, result.Value.DocumentTextChanges, cancellationToken).ConfigureAwait(false); return await AddRenameTokenAsync( - resultSolution, result.RenamedToken, cancellationToken).ConfigureAwait(false); + resultSolution, result.Value.RenamedToken, cancellationToken).ConfigureAwait(false); } } diff --git a/src/Features/Core/Portable/ConvertTupleToStruct/IRemoteConvertTupleToStructCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertTupleToStruct/IRemoteConvertTupleToStructCodeRefactoringProvider.cs deleted file mode 100644 index ddeaaab4772ae..0000000000000 --- a/src/Features/Core/Portable/ConvertTupleToStruct/IRemoteConvertTupleToStructCodeRefactoringProvider.cs +++ /dev/null @@ -1,27 +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. - -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Remote; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.ConvertTupleToStruct -{ - internal interface IRemoteConvertTupleToStructCodeRefactoringProvider - { - Task ConvertToStructAsync( - PinnedSolutionInfo solutionInfo, - DocumentId documentId, - TextSpan span, - Scope scope, - CancellationToken cancellationToken); - } - - internal class SerializableConvertTupleToStructResult - { - public (DocumentId, TextChange[])[] DocumentTextChanges; - public (DocumentId, TextSpan) RenamedToken; - } -} diff --git a/src/Features/Core/Portable/ConvertTupleToStruct/IRemoteConvertTupleToStructCodeRefactoringService.cs b/src/Features/Core/Portable/ConvertTupleToStruct/IRemoteConvertTupleToStructCodeRefactoringService.cs new file mode 100644 index 0000000000000..c5c290b8826ea --- /dev/null +++ b/src/Features/Core/Portable/ConvertTupleToStruct/IRemoteConvertTupleToStructCodeRefactoringService.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.Collections.Immutable; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ConvertTupleToStruct +{ + internal interface IRemoteConvertTupleToStructCodeRefactoringService + { + ValueTask ConvertToStructAsync( + PinnedSolutionInfo solutionInfo, + DocumentId documentId, + TextSpan span, + Scope scope, + CancellationToken cancellationToken); + } + + [DataContract] + internal readonly struct SerializableConvertTupleToStructResult + { + [DataMember(Order = 0)] + public readonly ImmutableArray<(DocumentId, ImmutableArray)> DocumentTextChanges; + + [DataMember(Order = 1)] + public readonly (DocumentId, TextSpan) RenamedToken; + + public SerializableConvertTupleToStructResult( + ImmutableArray<(DocumentId, ImmutableArray)> documentTextChanges, + (DocumentId, TextSpan) renamedToken) + { + DocumentTextChanges = documentTextChanges; + RenamedToken = renamedToken; + } + } +} diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticArguments.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticArguments.cs index 935d6da138210..f301b126f4b97 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticArguments.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticArguments.cs @@ -5,6 +5,7 @@ #nullable enable using System.Diagnostics; +using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Diagnostics @@ -12,6 +13,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics /// /// helper type to package diagnostic arguments to pass around between remote hosts /// + [DataContract] internal class DiagnosticArguments { /// @@ -20,35 +22,41 @@ internal class DiagnosticArguments /// Additionally, diagnostic computation for explicit user commands, such as FixAll, /// are marked high priority. /// + [DataMember(Order = 0)] public bool IsHighPriority; /// /// Flag indicating if suppressed diagnostics should be returned. /// + [DataMember(Order = 1)] public bool ReportSuppressedDiagnostics; /// /// Flag indicating if analyzer performance info, such as analyzer execution times, /// should be logged as performance telemetry. /// + [DataMember(Order = 2)] public bool LogPerformanceInfo; /// /// Flag indicating if the analyzer telemety info, such as registered analyzer action counts /// and analyzer execution times, should be included in the computed result. /// + [DataMember(Order = 3)] public bool GetTelemetryInfo; /// /// Optional document ID, if computing diagnostics for a specific document. /// For example, diagnostic computation for open file analysis. /// + [DataMember(Order = 4)] public DocumentId? DocumentId; /// /// Optional document text span, if computing diagnostics for a specific span for a document. /// For example, diagnostic computation for light bulb invocation for a specific line in active document. /// + [DataMember(Order = 5)] public TextSpan? DocumentSpan; /// @@ -56,16 +64,19 @@ internal class DiagnosticArguments /// i.e. must be non-null for a non-null analysis kind. /// Only supported non-null values are and . /// + [DataMember(Order = 6)] public AnalysisKind? DocumentAnalysisKind; /// /// Project ID for the document or project for which diagnostics need to be computed. /// + [DataMember(Order = 7)] public ProjectId ProjectId; /// /// Array of analyzer IDs for analyzers that need to be executed for computing diagnostics. /// + [DataMember(Order = 8)] public string[] AnalyzerIds; public DiagnosticArguments( diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticResultSerializer.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticResultSerializer.cs deleted file mode 100644 index 5e5cd7f37eec5..0000000000000 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticResultSerializer.cs +++ /dev/null @@ -1,308 +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 enable - -using System; -using System.Diagnostics; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics.Telemetry; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Workspaces.Diagnostics; -using Roslyn.Utilities; -using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.ErrorReporting; - -namespace Microsoft.CodeAnalysis.Diagnostics -{ - internal static class DiagnosticResultSerializer - { - public static (int diagnostics, int telemetry) WriteDiagnosticAnalysisResults( - ObjectWriter writer, - AnalysisKind? analysisKind, - DiagnosticAnalysisResultMap result, - CancellationToken cancellationToken) - { - var diagnosticCount = 0; - var diagnosticSerializer = new DiagnosticDataSerializer(VersionStamp.Default, VersionStamp.Default); - - writer.WriteInt32(result.AnalysisResult.Count); - foreach (var (analyzerId, analyzerResults) in result.AnalysisResult) - { - writer.WriteString(analyzerId); - - switch (analysisKind) - { - case AnalysisKind.Syntax: - diagnosticCount += WriteDiagnosticDataMap(writer, diagnosticSerializer, analyzerResults.SyntaxLocals, cancellationToken); - break; - - case AnalysisKind.Semantic: - diagnosticCount += WriteDiagnosticDataMap(writer, diagnosticSerializer, analyzerResults.SemanticLocals, cancellationToken); - break; - - case null: - diagnosticCount += WriteDiagnosticDataMap(writer, diagnosticSerializer, analyzerResults.SyntaxLocals, cancellationToken); - diagnosticCount += WriteDiagnosticDataMap(writer, diagnosticSerializer, analyzerResults.SemanticLocals, cancellationToken); - diagnosticCount += WriteDiagnosticDataMap(writer, diagnosticSerializer, analyzerResults.NonLocals, cancellationToken); - - diagnosticSerializer.WriteDiagnosticData(writer, analyzerResults.Others, cancellationToken); - diagnosticCount += analyzerResults.Others.Length; - break; - - default: - throw ExceptionUtilities.UnexpectedValue(analysisKind.Value); - } - } - - writer.WriteInt32(result.TelemetryInfo.Count); - foreach (var (analyzerId, analyzerTelemetry) in result.TelemetryInfo) - { - writer.WriteString(analyzerId); - WriteTelemetry(writer, analyzerTelemetry, cancellationToken); - } - - // report how many data has been sent - return (diagnosticCount, result.TelemetryInfo.Count); - } - - public static bool TryReadDiagnosticAnalysisResults( - ObjectReader reader, - IDictionary analyzerMap, - DocumentAnalysisScope? documentAnalysisScope, - Project project, - VersionStamp version, - CancellationToken cancellationToken, - [NotNullWhen(returnValue: true)] out DiagnosticAnalysisResultMap? result) - { - result = null; - - try - { - var diagnosticDataSerializer = new DiagnosticDataSerializer(VersionStamp.Default, VersionStamp.Default); - - var analysisMap = ImmutableDictionary.CreateBuilder(); - var documentIds = documentAnalysisScope != null ? ImmutableHashSet.Create(documentAnalysisScope.TextDocument.Id) : null; - - var analysisCount = reader.ReadInt32(); - for (var i = 0; i < analysisCount; i++) - { - var analyzer = analyzerMap[reader.ReadString()]; - - DiagnosticAnalysisResult analysisResult; - if (documentAnalysisScope != null) - { - ImmutableDictionary>? syntaxLocalMap, semanticLocalMap; - if (documentAnalysisScope.Kind == AnalysisKind.Syntax) - { - if (!TryReadDiagnosticDataMap(reader, diagnosticDataSerializer, project, cancellationToken, out syntaxLocalMap)) - { - return false; - } - - semanticLocalMap = ImmutableDictionary>.Empty; - } - else - { - Debug.Assert(documentAnalysisScope.Kind == AnalysisKind.Semantic); - if (!TryReadDiagnosticDataMap(reader, diagnosticDataSerializer, project, cancellationToken, out semanticLocalMap)) - { - return false; - } - - syntaxLocalMap = ImmutableDictionary>.Empty; - } - - analysisResult = DiagnosticAnalysisResult.Create( - project, - version, - syntaxLocalMap, - semanticLocalMap, - nonLocalMap: ImmutableDictionary>.Empty, - others: ImmutableArray.Empty, - documentIds); - } - else - { - if (!TryReadDiagnosticDataMap(reader, diagnosticDataSerializer, project, cancellationToken, out var syntaxLocalMap) || - !TryReadDiagnosticDataMap(reader, diagnosticDataSerializer, project, cancellationToken, out var semanticLocalMap) || - !TryReadDiagnosticDataMap(reader, diagnosticDataSerializer, project, cancellationToken, out var nonLocalMap) || - !diagnosticDataSerializer.TryReadDiagnosticData(reader, project, document: null, cancellationToken, out var others)) - { - return false; - } - - analysisResult = DiagnosticAnalysisResult.Create( - project, - version, - syntaxLocalMap, - semanticLocalMap, - nonLocalMap, - others.NullToEmpty(), - documentIds: null); - } - - analysisMap.Add(analyzer, analysisResult); - } - - var telemetryMap = ImmutableDictionary.CreateBuilder(); - - var telemetryCount = reader.ReadInt32(); - for (var i = 0; i < telemetryCount; i++) - { - var analyzer = analyzerMap[reader.ReadString()]; - var telemetryInfo = ReadTelemetry(reader, cancellationToken); - - telemetryMap.Add(analyzer, telemetryInfo); - } - - result = DiagnosticAnalysisResultMap.Create(analysisMap.ToImmutable(), telemetryMap.ToImmutable()); - return true; - } - catch (Exception ex) when (FatalError.ReportWithoutCrashUnlessCanceled(ex)) - { - return false; - } - } - - private static int WriteDiagnosticDataMap( - ObjectWriter writer, - DiagnosticDataSerializer serializer, - ImmutableDictionary> diagnostics, - CancellationToken cancellationToken) - { - var count = 0; - - writer.WriteInt32(diagnostics.Count); - foreach (var (documentId, data) in diagnostics) - { - documentId.WriteTo(writer); - serializer.WriteDiagnosticData(writer, data, cancellationToken); - - count += data.Length; - } - - return count; - } - - private static bool TryReadDiagnosticDataMap( - ObjectReader reader, - DiagnosticDataSerializer serializer, - Project project, - CancellationToken cancellationToken, - [NotNullWhen(returnValue: true)] out ImmutableDictionary>? dataMap) - { - var count = reader.ReadInt32(); - - var map = ImmutableDictionary.CreateBuilder>(); - for (var i = 0; i < count; i++) - { - var documentId = DocumentId.ReadFrom(reader); - var document = project.GetDocument(documentId); - - if (!serializer.TryReadDiagnosticData(reader, project, document, cancellationToken, out var diagnostics)) - { - dataMap = null; - return false; - } - - // drop diagnostics for non-null document that doesn't support diagnostics - if (diagnostics.IsDefault || document?.SupportsDiagnostics() == false) - { - continue; - } - - map.Add(documentId, diagnostics); - } - - dataMap = map.ToImmutable(); - return true; - } - - private static void WriteTelemetry(ObjectWriter writer, AnalyzerTelemetryInfo telemetryInfo, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - writer.WriteInt32(telemetryInfo.CompilationStartActionsCount); - writer.WriteInt32(telemetryInfo.CompilationEndActionsCount); - writer.WriteInt32(telemetryInfo.CompilationActionsCount); - writer.WriteInt32(telemetryInfo.SyntaxTreeActionsCount); - writer.WriteInt32(telemetryInfo.AdditionalFileActionsCount); - writer.WriteInt32(telemetryInfo.SemanticModelActionsCount); - writer.WriteInt32(telemetryInfo.SymbolActionsCount); - writer.WriteInt32(telemetryInfo.SymbolStartActionsCount); - writer.WriteInt32(telemetryInfo.SymbolEndActionsCount); - writer.WriteInt32(telemetryInfo.SyntaxNodeActionsCount); - writer.WriteInt32(telemetryInfo.CodeBlockStartActionsCount); - writer.WriteInt32(telemetryInfo.CodeBlockEndActionsCount); - writer.WriteInt32(telemetryInfo.CodeBlockActionsCount); - writer.WriteInt32(telemetryInfo.OperationActionsCount); - writer.WriteInt32(telemetryInfo.OperationBlockActionsCount); - writer.WriteInt32(telemetryInfo.OperationBlockStartActionsCount); - writer.WriteInt32(telemetryInfo.OperationBlockEndActionsCount); - writer.WriteInt32(telemetryInfo.SuppressionActionsCount); - writer.WriteInt64(telemetryInfo.ExecutionTime.Ticks); - writer.WriteBoolean(telemetryInfo.Concurrent); - } - - private static AnalyzerTelemetryInfo ReadTelemetry(ObjectReader reader, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var compilationStartActionsCount = reader.ReadInt32(); - var compilationEndActionsCount = reader.ReadInt32(); - var compilationActionsCount = reader.ReadInt32(); - var syntaxTreeActionsCount = reader.ReadInt32(); - var additionalFileActionsCount = reader.ReadInt32(); - var semanticModelActionsCount = reader.ReadInt32(); - var symbolActionsCount = reader.ReadInt32(); - var symbolStartActionsCount = reader.ReadInt32(); - var symbolEndActionsCount = reader.ReadInt32(); - var syntaxNodeActionsCount = reader.ReadInt32(); - var codeBlockStartActionsCount = reader.ReadInt32(); - var codeBlockEndActionsCount = reader.ReadInt32(); - var codeBlockActionsCount = reader.ReadInt32(); - var operationActionsCount = reader.ReadInt32(); - var operationBlockActionsCount = reader.ReadInt32(); - var operationBlockStartActionsCount = reader.ReadInt32(); - var operationBlockEndActionsCount = reader.ReadInt32(); - var suppressionActionsCount = reader.ReadInt32(); - var executionTime = new TimeSpan(reader.ReadInt64()); - var concurrent = reader.ReadBoolean(); - - return new AnalyzerTelemetryInfo() - { - CompilationStartActionsCount = compilationStartActionsCount, - CompilationEndActionsCount = compilationEndActionsCount, - CompilationActionsCount = compilationActionsCount, - - SyntaxTreeActionsCount = syntaxTreeActionsCount, - AdditionalFileActionsCount = additionalFileActionsCount, - SemanticModelActionsCount = semanticModelActionsCount, - SymbolActionsCount = symbolActionsCount, - SymbolStartActionsCount = symbolStartActionsCount, - SymbolEndActionsCount = symbolEndActionsCount, - SyntaxNodeActionsCount = syntaxNodeActionsCount, - - CodeBlockStartActionsCount = codeBlockStartActionsCount, - CodeBlockEndActionsCount = codeBlockEndActionsCount, - CodeBlockActionsCount = codeBlockActionsCount, - - OperationActionsCount = operationActionsCount, - OperationBlockStartActionsCount = operationBlockStartActionsCount, - OperationBlockEndActionsCount = operationBlockEndActionsCount, - OperationBlockActionsCount = operationBlockActionsCount, - - SuppressionActionsCount = suppressionActionsCount, - - ExecutionTime = executionTime, - - Concurrent = concurrent - }; - } - } -} diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs index 1c75b8df462b4..60642c068f25f 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs @@ -15,7 +15,9 @@ using Microsoft.CodeAnalysis.Diagnostics.EngineV2; using Microsoft.CodeAnalysis.Diagnostics.Telemetry; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; @@ -192,39 +194,40 @@ private async Task>( + var result = await client.TryInvokeAsync( solution, - invocation: (service, solutionInfo, stream, cancellationToken) => service.CalculateDiagnosticsAsync(solutionInfo, argument, stream, cancellationToken), - reader: (stream, cancellationToken) => ReadCompilerAnalysisResultAsync(stream, analyzerMap, documentAnalysisScope, project, cancellationToken), + invocation: (service, solutionInfo, cancellationToken) => service.CalculateDiagnosticsAsync(solutionInfo, argument, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); - return result.HasValue ? result.Value : DiagnosticAnalysisResultMap.Empty; - } - - private static async ValueTask> ReadCompilerAnalysisResultAsync( - Stream stream, - Dictionary analyzerMap, - DocumentAnalysisScope? documentAnalysisScope, - Project project, - CancellationToken cancellationToken) - { - // handling of cancellation and exception - var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - - using var reader = ObjectReader.TryGetReader(stream, leaveOpen: false, cancellationToken); - - // We only get a reader for data transmitted between live processes. - // This data should always be correct as we're never persisting the data between sessions. - Contract.ThrowIfNull(reader); - - if (!DiagnosticResultSerializer.TryReadDiagnosticAnalysisResults(reader, analyzerMap, - documentAnalysisScope, project, version, cancellationToken, out var result)) + if (!result.HasValue) { return DiagnosticAnalysisResultMap.Empty; } - return result.Value; + // handling of cancellation and exception + var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); + + var documentIds = (documentAnalysisScope != null) ? ImmutableHashSet.Create(documentAnalysisScope.TextDocument.Id) : null; + + return new DiagnosticAnalysisResultMap( + result.Value.Diagnostics.ToImmutableDictionary( + entry => analyzerMap[entry.analyzerId], + entry => DiagnosticAnalysisResult.Create( + project, + version, + syntaxLocalMap: Hydrate(entry.diagnosticMap.Syntax, project), + semanticLocalMap: Hydrate(entry.diagnosticMap.Semantic, project), + nonLocalMap: Hydrate(entry.diagnosticMap.NonLocal, project), + others: entry.diagnosticMap.Other, + documentIds)), + result.Value.Telemetry.ToImmutableDictionary(entry => analyzerMap[entry.analyzerId], entry => entry.telemetry)); } + + // TODO: filter in OOP https://github.com/dotnet/roslyn/issues/47859 + private static ImmutableDictionary> Hydrate(ImmutableArray<(DocumentId documentId, ImmutableArray diagnostics)> diagnosticByDocument, Project project) + => diagnosticByDocument + .Where(entry => project.GetTextDocument(entry.documentId)?.SupportsDiagnostics() == true) + .ToImmutableDictionary(entry => entry.documentId, entry => entry.diagnostics); } } diff --git a/src/Features/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs index aba822012e040..2bfd5bf7fe1ec 100644 --- a/src/Features/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.IO; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Remote; @@ -14,19 +15,25 @@ namespace Microsoft.CodeAnalysis.Diagnostics { internal interface IRemoteDiagnosticAnalyzerService { - ValueTask CalculateDiagnosticsAsync(PinnedSolutionInfo solutionInfo, DiagnosticArguments arguments, Stream outputStream, CancellationToken cancellationToken); + ValueTask CalculateDiagnosticsAsync(PinnedSolutionInfo solutionInfo, DiagnosticArguments arguments, CancellationToken cancellationToken); ValueTask ReportAnalyzerPerformanceAsync(ImmutableArray snapshot, int unitCount, CancellationToken cancellationToken); } + [DataContract] internal readonly struct AnalyzerPerformanceInfo { + [DataMember(Order = 0)] public readonly string AnalyzerId; + + [DataMember(Order = 1)] public readonly bool BuiltIn; + + [DataMember(Order = 2)] public readonly TimeSpan TimeSpan; - public AnalyzerPerformanceInfo(string analyzerid, bool builtIn, TimeSpan timeSpan) + public AnalyzerPerformanceInfo(string analyzerId, bool builtIn, TimeSpan timeSpan) { - AnalyzerId = analyzerid; + AnalyzerId = analyzerId; BuiltIn = builtIn; TimeSpan = timeSpan; } diff --git a/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService.cs b/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService.cs index 434cdc710fd55..a82808dd0be57 100644 --- a/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService.cs +++ b/src/Features/Core/Portable/DocumentHighlighting/AbstractDocumentHighlightsService.cs @@ -30,20 +30,18 @@ public async Task> GetDocumentHighlightsAsync var client = await RemoteHostClient.TryGetClientAsync(document.Project, cancellationToken).ConfigureAwait(false); if (client != null) { - var result = await client.RunRemoteAsync>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteDocumentHighlights.GetDocumentHighlightsAsync), + var result = await client.TryInvokeAsync>( solution, - new object[] - { - document.Id, - position, - documentsToSearch.Select(d => d.Id).ToArray() - }, + (service, solutionInfo, cancellationToken) => service.GetDocumentHighlightsAsync(solutionInfo, document.Id, position, documentsToSearch.SelectAsArray(d => d.Id), cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); - return result.SelectAsArray(h => h.Rehydrate(solution)); + if (!result.HasValue) + { + return ImmutableArray.Empty; + } + + return result.Value.SelectAsArray(h => h.Rehydrate(solution)); } return await GetDocumentHighlightsInCurrentProcessAsync( @@ -100,19 +98,6 @@ private static async Task> TryGetEmbeddedLang return default; } - private static async Task GetSymbolToSearchAsync(Document document, int position, SemanticModel semanticModel, ISymbol symbol, CancellationToken cancellationToken) - { - // see whether we can use the symbol as it is - var currentSemanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - if (currentSemanticModel == semanticModel) - { - return symbol; - } - - // get symbols from current document again - return await SymbolFinder.FindSymbolAtPositionAsync(currentSemanticModel, position, document.Project.Solution.Workspace, cancellationToken).ConfigureAwait(false); - } - private async Task> GetTagsForReferencedSymbolAsync( ISymbol symbol, Document document, @@ -124,7 +109,7 @@ private async Task> GetTagsForReferencedSymbo { var progress = new StreamingProgressCollector(); - var options = FindReferencesSearchOptions.GetFeatureOptionsForStartingSymbol(symbol); + var options = FindSymbols.FindReferencesSearchOptions.GetFeatureOptionsForStartingSymbol(symbol); await SymbolFinder.FindReferencesAsync( symbol, document.Project.Solution, progress, documentsToSearch, options, cancellationToken).ConfigureAwait(false); @@ -164,7 +149,7 @@ private static bool ShouldConsiderSymbol(ISymbol symbol) private async Task> FilterAndCreateSpansAsync( ImmutableArray references, Document startingDocument, IImmutableSet documentsToSearch, ISymbol symbol, - FindReferencesSearchOptions options, CancellationToken cancellationToken) + FindSymbols.FindReferencesSearchOptions options, CancellationToken cancellationToken) { var solution = startingDocument.Project.Solution; references = references.FilterToItemsToShow(options); diff --git a/src/Features/Core/Portable/DocumentHighlighting/IDocumentHighlightsService.cs b/src/Features/Core/Portable/DocumentHighlighting/IDocumentHighlightsService.cs index ff50706ebc97c..9d49416dcbca8 100644 --- a/src/Features/Core/Portable/DocumentHighlighting/IDocumentHighlightsService.cs +++ b/src/Features/Core/Portable/DocumentHighlighting/IDocumentHighlightsService.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -18,9 +19,13 @@ internal enum HighlightSpanKind WrittenReference, } + [DataContract] internal readonly struct HighlightSpan { + [DataMember(Order = 0)] public TextSpan TextSpan { get; } + + [DataMember(Order = 1)] public HighlightSpanKind Kind { get; } public HighlightSpan(TextSpan textSpan, HighlightSpanKind kind) : this() diff --git a/src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlights.cs b/src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlights.cs deleted file mode 100644 index 279a0b6359e35..0000000000000 --- a/src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlights.cs +++ /dev/null @@ -1,34 +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. - -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Remote; - -namespace Microsoft.CodeAnalysis.DocumentHighlighting -{ - internal interface IRemoteDocumentHighlights - { - Task> GetDocumentHighlightsAsync( - PinnedSolutionInfo solutionInfo, DocumentId documentId, int position, DocumentId[] documentIdsToSearch, CancellationToken cancellationToken); - } - - internal struct SerializableDocumentHighlights - { - public DocumentId DocumentId; - public IList HighlightSpans; - - public DocumentHighlights Rehydrate(Solution solution) - => new DocumentHighlights(solution.GetDocument(DocumentId), HighlightSpans.ToImmutableArray()); - - public static SerializableDocumentHighlights Dehydrate(DocumentHighlights highlights) - => new SerializableDocumentHighlights - { - DocumentId = highlights.Document.Id, - HighlightSpans = highlights.HighlightSpans - }; - } -} diff --git a/src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlightsService.cs b/src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlightsService.cs new file mode 100644 index 0000000000000..1a22573e933ce --- /dev/null +++ b/src/Features/Core/Portable/DocumentHighlighting/IRemoteDocumentHighlightsService.cs @@ -0,0 +1,42 @@ +// 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 enable + +using System.Collections.Immutable; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Remote; + +namespace Microsoft.CodeAnalysis.DocumentHighlighting +{ + internal interface IRemoteDocumentHighlightsService + { + ValueTask> GetDocumentHighlightsAsync( + PinnedSolutionInfo solutionInfo, DocumentId documentId, int position, ImmutableArray documentIdsToSearch, CancellationToken cancellationToken); + } + + [DataContract] + internal readonly struct SerializableDocumentHighlights + { + [DataMember(Order = 0)] + public readonly DocumentId DocumentId; + + [DataMember(Order = 1)] + public readonly ImmutableArray HighlightSpans; + + public SerializableDocumentHighlights(DocumentId documentId, ImmutableArray highlightSpans) + { + DocumentId = documentId; + HighlightSpans = highlightSpans; + } + + public DocumentHighlights Rehydrate(Solution solution) + => new DocumentHighlights(solution.GetDocument(DocumentId), HighlightSpans); + + public static SerializableDocumentHighlights Dehydrate(DocumentHighlights highlights) + => new(highlights.Document.Id, highlights.HighlightSpans); + } +} diff --git a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs index 6ebb8f0c51112..2fa6a0702b07d 100644 --- a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs +++ b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs @@ -104,21 +104,21 @@ public async Task EncapsulateFieldsAsync( var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); if (client != null) { - var result = await client.RunRemoteAsync<(DocumentId, TextChange[])[]>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteEncapsulateFieldService.EncapsulateFieldsAsync), + var fieldSymbolKeys = fields.SelectAsArray(f => SymbolKey.CreateString(f, cancellationToken)); + + var result = await client.TryInvokeAsync)>>( solution, - new object[] - { - document.Id, - fields.Select(f => SymbolKey.CreateString(f, cancellationToken)).ToArray(), - updateReferences, - }, + (service, solutionInfo, cancellationToken) => service.EncapsulateFieldsAsync(solutionInfo, document.Id, fieldSymbolKeys, updateReferences, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); + if (!result.HasValue) + { + return solution; + } + return await RemoteUtilities.UpdateSolutionAsync( - solution, result, cancellationToken).ConfigureAwait(false); + solution, result.Value, cancellationToken).ConfigureAwait(false); } } diff --git a/src/Features/Core/Portable/EncapsulateField/IRemoteEncapsulateFieldService.cs b/src/Features/Core/Portable/EncapsulateField/IRemoteEncapsulateFieldService.cs index e1edfef47f965..8b7429f51a135 100644 --- a/src/Features/Core/Portable/EncapsulateField/IRemoteEncapsulateFieldService.cs +++ b/src/Features/Core/Portable/EncapsulateField/IRemoteEncapsulateFieldService.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.EncapsulateField { internal interface IRemoteEncapsulateFieldService { - Task<(DocumentId, TextChange[])[]> EncapsulateFieldsAsync( + ValueTask)>> EncapsulateFieldsAsync( PinnedSolutionInfo solutionInfo, DocumentId documentId, ImmutableArray fieldSymbolKeys, diff --git a/src/EditorFeatures/Core/FindUsages/IFindUsagesContext.cs b/src/Features/Core/Portable/FindUsages/IFindUsagesContext.cs similarity index 76% rename from src/EditorFeatures/Core/FindUsages/IFindUsagesContext.cs rename to src/Features/Core/Portable/FindUsages/IFindUsagesContext.cs index fe9d1f09a936e..c57d9b9ed4356 100644 --- a/src/EditorFeatures/Core/FindUsages/IFindUsagesContext.cs +++ b/src/Features/Core/Portable/FindUsages/IFindUsagesContext.cs @@ -22,17 +22,17 @@ internal interface IFindUsagesContext /// /// Report a message to be displayed to the user. /// - Task ReportMessageAsync(string message); + ValueTask ReportMessageAsync(string message); /// /// Set the title of the window that results are displayed in. /// - Task SetSearchTitleAsync(string title); + ValueTask SetSearchTitleAsync(string title); - Task OnDefinitionFoundAsync(DefinitionItem definition); - Task OnReferenceFoundAsync(SourceReferenceItem reference); + ValueTask OnDefinitionFoundAsync(DefinitionItem definition); + ValueTask OnReferenceFoundAsync(SourceReferenceItem reference); [Obsolete("Use ProgressTracker instead", error: false)] - Task ReportProgressAsync(int current, int maximum); + ValueTask ReportProgressAsync(int current, int maximum); } } diff --git a/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs b/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs new file mode 100644 index 0000000000000..e155e0ca31437 --- /dev/null +++ b/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs @@ -0,0 +1,234 @@ +// 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.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.FindUsages +{ + internal interface IRemoteFindUsagesService + { + internal interface ICallback + { + ValueTask AddItemsAsync(int count); + ValueTask ItemCompletedAsync(); + ValueTask ReportMessageAsync(string message); + ValueTask ReportProgressAsync(int current, int maximum); + ValueTask SetSearchTitleAsync(string title); + ValueTask OnDefinitionFoundAsync(SerializableDefinitionItem definition); + ValueTask OnReferenceFoundAsync(SerializableSourceReferenceItem reference); + } + + ValueTask FindReferencesAsync( + PinnedSolutionInfo solutionInfo, + SerializableSymbolAndProjectId symbolAndProjectId, + FindReferencesSearchOptions options, + CancellationToken cancellationToken); + + ValueTask FindImplementationsAsync( + PinnedSolutionInfo solutionInfo, + SerializableSymbolAndProjectId symbolAndProjectId, + CancellationToken cancellationToken); + } + + internal sealed class FindUsagesServerCallback : IRemoteFindUsagesService.ICallback + { + private readonly Solution _solution; + private readonly IFindUsagesContext _context; + private readonly Dictionary _idToDefinition = new Dictionary(); + + public FindUsagesServerCallback(Solution solution, IFindUsagesContext context) + { + _solution = solution; + _context = context; + } + + public ValueTask AddItemsAsync(int count) + => _context.ProgressTracker.AddItemsAsync(count); + + public ValueTask ItemCompletedAsync() + => _context.ProgressTracker.ItemCompletedAsync(); + + public ValueTask ReportMessageAsync(string message) + => _context.ReportMessageAsync(message); + + [Obsolete] + public ValueTask ReportProgressAsync(int current, int maximum) + => _context.ReportProgressAsync(current, maximum); + + public ValueTask SetSearchTitleAsync(string title) + => _context.SetSearchTitleAsync(title); + + public ValueTask OnDefinitionFoundAsync(SerializableDefinitionItem definition) + { + var id = definition.Id; + var rehydrated = definition.Rehydrate(_solution); + + lock (_idToDefinition) + { + _idToDefinition.Add(id, rehydrated); + } + + return _context.OnDefinitionFoundAsync(rehydrated); + } + + public ValueTask OnReferenceFoundAsync(SerializableSourceReferenceItem reference) + => _context.OnReferenceFoundAsync(reference.Rehydrate(_solution, GetDefinition(reference.DefinitionId))); + + private DefinitionItem GetDefinition(int definitionId) + { + lock (_idToDefinition) + { + Contract.ThrowIfFalse(_idToDefinition.ContainsKey(definitionId)); + return _idToDefinition[definitionId]; + } + } + } + + [DataContract] + internal readonly struct SerializableDocumentSpan + { + [DataMember(Order = 0)] + public readonly DocumentId DocumentId; + + [DataMember(Order = 1)] + public readonly TextSpan SourceSpan; + + public SerializableDocumentSpan(DocumentId documentId, TextSpan sourceSpan) + { + DocumentId = documentId; + SourceSpan = sourceSpan; + } + + public static SerializableDocumentSpan Dehydrate(DocumentSpan documentSpan) + => new(documentSpan.Document.Id, documentSpan.SourceSpan); + + public DocumentSpan Rehydrate(Solution solution) + => new(solution.GetDocument(DocumentId), SourceSpan); + } + + [DataContract] + internal readonly struct SerializableDefinitionItem + { + [DataMember(Order = 0)] + public readonly int Id; + + [DataMember(Order = 1)] + public readonly ImmutableArray Tags; + + [DataMember(Order = 2)] + public readonly ImmutableArray DisplayParts; + + [DataMember(Order = 3)] + public readonly ImmutableArray NameDisplayParts; + + [DataMember(Order = 4)] + public readonly ImmutableArray OriginationParts; + + [DataMember(Order = 5)] + public readonly ImmutableArray SourceSpans; + + [DataMember(Order = 6)] + public readonly ImmutableDictionary Properties; + + [DataMember(Order = 7)] + public readonly ImmutableDictionary DisplayableProperties; + + [DataMember(Order = 8)] + public readonly bool DisplayIfNoReferences; + + public SerializableDefinitionItem( + int id, + ImmutableArray tags, + ImmutableArray displayParts, + ImmutableArray nameDisplayParts, + ImmutableArray originationParts, + ImmutableArray sourceSpans, + ImmutableDictionary properties, + ImmutableDictionary displayableProperties, + bool displayIfNoReferences) + { + Id = id; + Tags = tags; + DisplayParts = displayParts; + NameDisplayParts = nameDisplayParts; + OriginationParts = originationParts; + SourceSpans = sourceSpans; + Properties = properties; + DisplayableProperties = displayableProperties; + DisplayIfNoReferences = displayIfNoReferences; + } + + public static SerializableDefinitionItem Dehydrate(int id, DefinitionItem item) + => new(id, + item.Tags, + item.DisplayParts, + item.NameDisplayParts, + item.OriginationParts, + item.SourceSpans.SelectAsArray(ss => SerializableDocumentSpan.Dehydrate(ss)), + item.Properties, + item.DisplayableProperties, + item.DisplayIfNoReferences); + + public DefinitionItem Rehydrate(Solution solution) + => new DefinitionItem.DefaultDefinitionItem( + Tags, + DisplayParts, + NameDisplayParts, + OriginationParts, + SourceSpans.SelectAsArray(ss => ss.Rehydrate(solution)), + Properties, + DisplayableProperties, + DisplayIfNoReferences); + } + + [DataContract] + internal readonly struct SerializableSourceReferenceItem + { + [DataMember(Order = 0)] + public readonly int DefinitionId; + + [DataMember(Order = 1)] + public readonly SerializableDocumentSpan SourceSpan; + + [DataMember(Order = 2)] + public readonly SymbolUsageInfo SymbolUsageInfo; + + [DataMember(Order = 3)] + public readonly ImmutableDictionary AdditionalProperties; + + public SerializableSourceReferenceItem( + int definitionId, + SerializableDocumentSpan sourceSpan, + SymbolUsageInfo symbolUsageInfo, + ImmutableDictionary additionalProperties) + { + DefinitionId = definitionId; + SourceSpan = sourceSpan; + SymbolUsageInfo = symbolUsageInfo; + AdditionalProperties = additionalProperties; + } + + public static SerializableSourceReferenceItem Dehydrate(int definitionId, SourceReferenceItem item) + => new(definitionId, + SerializableDocumentSpan.Dehydrate(item.SourceSpan), + item.SymbolUsageInfo, + item.AdditionalProperties); + + public SourceReferenceItem Rehydrate(Solution solution, DefinitionItem definition) + => new(definition, + SourceSpan.Rehydrate(solution), + SymbolUsageInfo, + AdditionalProperties.ToImmutableDictionary(t => t.Key, t => t.Value)); + } +} diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs index 1e375b2f07cf2..fc93cb930df8b 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs @@ -38,15 +38,13 @@ public async Task> SearchDocumentAsync( { var solution = document.Project.Solution; - var result = await client.RunRemoteAsync>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteNavigateToSearchService.SearchDocumentAsync), + var result = await client.TryInvokeAsync>( solution, - new object[] { document.Id, searchPattern, kinds.ToArray() }, + (service, solutionInfo, cancellationToken) => service.SearchDocumentAsync(solutionInfo, document.Id, searchPattern, kinds.ToImmutableArray(), cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); - return result.SelectAsArray(r => r.Rehydrate(solution)); + return result.HasValue ? result.Value.SelectAsArray(r => r.Rehydrate(solution)) : ImmutableArray.Empty; } return await SearchDocumentInCurrentProcessAsync( @@ -60,16 +58,14 @@ public async Task> SearchProjectAsync( if (client != null) { var solution = project.Solution; - - var result = await client.RunRemoteAsync>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteNavigateToSearchService.SearchProjectAsync), + var priorityDocumentIds = priorityDocuments.SelectAsArray(d => d.Id); + var result = await client.TryInvokeAsync>( solution, - new object[] { project.Id, priorityDocuments.Select(d => d.Id).ToArray(), searchPattern, kinds.ToArray() }, + (service, solutionInfo, cancellationToken) => service.SearchProjectAsync(solutionInfo, project.Id, priorityDocumentIds, searchPattern, kinds.ToImmutableArray(), cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); - return result.SelectAsArray(r => r.Rehydrate(solution)); + return result.HasValue ? result.Value.SelectAsArray(r => r.Rehydrate(solution)) : ImmutableArray.Empty; } return await SearchProjectInCurrentProcessAsync( diff --git a/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs b/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs index 7949a1bb3439c..677c502aa2e95 100644 --- a/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs +++ b/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs @@ -2,7 +2,9 @@ // 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.Collections.Generic; +#nullable enable + +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Remote; @@ -11,10 +13,10 @@ namespace Microsoft.CodeAnalysis.NavigateTo { internal interface IRemoteNavigateToSearchService { - Task> SearchDocumentAsync( - PinnedSolutionInfo solutionInfo, DocumentId documentId, string searchPattern, string[] kinds, CancellationToken cancellationToken); + ValueTask> SearchDocumentAsync( + PinnedSolutionInfo solutionInfo, DocumentId documentId, string searchPattern, ImmutableArray kinds, CancellationToken cancellationToken); - Task> SearchProjectAsync( - PinnedSolutionInfo solutionInfo, ProjectId projectId, DocumentId[] priorityDocumentIds, string searchPattern, string[] kinds, CancellationToken cancellationToken); + ValueTask> SearchProjectAsync( + PinnedSolutionInfo solutionInfo, ProjectId projectId, ImmutableArray priorityDocumentIds, string searchPattern, ImmutableArray kinds, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/Remote/RemoteArguments.cs b/src/Features/Core/Portable/Remote/RemoteArguments.cs index 75d2aaf12ac58..0cb7df7d1b282 100644 --- a/src/Features/Core/Portable/Remote/RemoteArguments.cs +++ b/src/Features/Core/Portable/Remote/RemoteArguments.cs @@ -4,50 +4,83 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Runtime.Serialization; using Microsoft.CodeAnalysis.NavigateTo; using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote { #region NavigateTo - internal class SerializableNavigateToSearchResult + [DataContract] + internal readonly struct SerializableNavigateToSearchResult { - public string AdditionalInformation; + [DataMember(Order = 0)] + public readonly string AdditionalInformation; - public string Kind; - public NavigateToMatchKind MatchKind; - public bool IsCaseSensitive; - public string Name; - public IList NameMatchSpans; - public string SecondarySort; - public string Summary; + [DataMember(Order = 1)] + public readonly string Kind; - public SerializableNavigableItem NavigableItem; + [DataMember(Order = 2)] + public readonly NavigateToMatchKind MatchKind; - internal static SerializableNavigateToSearchResult Dehydrate(INavigateToSearchResult result) + [DataMember(Order = 3)] + public readonly bool IsCaseSensitive; + + [DataMember(Order = 4)] + public readonly string Name; + + [DataMember(Order = 5)] + public readonly ImmutableArray NameMatchSpans; + + [DataMember(Order = 6)] + public readonly string SecondarySort; + + [DataMember(Order = 7)] + public readonly string Summary; + + [DataMember(Order = 8)] + public readonly SerializableNavigableItem NavigableItem; + + public SerializableNavigateToSearchResult( + string additionalInformation, + string kind, + NavigateToMatchKind matchKind, + bool isCaseSensitive, + string name, + ImmutableArray nameMatchSpans, + string secondarySort, + string summary, + SerializableNavigableItem navigableItem) { - return new SerializableNavigateToSearchResult - { - AdditionalInformation = result.AdditionalInformation, - Kind = result.Kind, - MatchKind = result.MatchKind, - IsCaseSensitive = result.IsCaseSensitive, - Name = result.Name, - NameMatchSpans = result.NameMatchSpans, - SecondarySort = result.SecondarySort, - Summary = result.Summary, - NavigableItem = SerializableNavigableItem.Dehydrate(result.NavigableItem) - }; + AdditionalInformation = additionalInformation; + Kind = kind; + MatchKind = matchKind; + IsCaseSensitive = isCaseSensitive; + Name = name; + NameMatchSpans = nameMatchSpans; + SecondarySort = secondarySort; + Summary = summary; + NavigableItem = navigableItem; } + internal static SerializableNavigateToSearchResult Dehydrate(INavigateToSearchResult result) + => new(result.AdditionalInformation, + result.Kind, + result.MatchKind, + result.IsCaseSensitive, + result.Name, + result.NameMatchSpans, + result.SecondarySort, + result.Summary, + SerializableNavigableItem.Dehydrate(result.NavigableItem)); + internal INavigateToSearchResult Rehydrate(Solution solution) { return new NavigateToSearchResult( AdditionalInformation, Kind, MatchKind, IsCaseSensitive, - Name, NameMatchSpans.ToImmutableArrayOrEmpty(), + Name, NameMatchSpans, SecondarySort, Summary, NavigableItem.Rehydrate(solution)); } @@ -82,42 +115,68 @@ public NavigateToSearchResult( } } - internal class SerializableNavigableItem + /// + /// Note: this is intentionally a class, not struct, to avoid hitting .NET Framework loader bug + /// that fails to load a struct S declaring a field of type ImmutableArray of S. + /// + [DataContract] + internal sealed class SerializableNavigableItem { - public Glyph Glyph; + [DataMember(Order = 0)] + public readonly Glyph Glyph; - public IList DisplayTaggedParts; + [DataMember(Order = 1)] + public readonly ImmutableArray DisplayTaggedParts; - public bool DisplayFileLocation; + [DataMember(Order = 2)] + public readonly bool DisplayFileLocation; - public bool IsImplicitlyDeclared; + [DataMember(Order = 3)] + public readonly bool IsImplicitlyDeclared; - public DocumentId Document; - public TextSpan SourceSpan; + [DataMember(Order = 4)] + public readonly DocumentId Document; - public IList ChildItems; + [DataMember(Order = 5)] + public readonly TextSpan SourceSpan; - public static SerializableNavigableItem Dehydrate(INavigableItem item) + [DataMember(Order = 6)] + public readonly ImmutableArray ChildItems; + + public SerializableNavigableItem( + Glyph glyph, + ImmutableArray displayTaggedParts, + bool displayFileLocation, + bool isImplicitlyDeclared, + DocumentId document, + TextSpan sourceSpan, + ImmutableArray childItems) { - return new SerializableNavigableItem - { - Glyph = item.Glyph, - DisplayTaggedParts = item.DisplayTaggedParts, - DisplayFileLocation = item.DisplayFileLocation, - IsImplicitlyDeclared = item.IsImplicitlyDeclared, - Document = item.Document.Id, - SourceSpan = item.SourceSpan, - ChildItems = item.ChildItems.SelectAsArray(Dehydrate) - }; + Glyph = glyph; + DisplayTaggedParts = displayTaggedParts; + DisplayFileLocation = displayFileLocation; + IsImplicitlyDeclared = isImplicitlyDeclared; + Document = document; + SourceSpan = sourceSpan; + ChildItems = childItems; } + public static SerializableNavigableItem Dehydrate(INavigableItem item) + => new(item.Glyph, + item.DisplayTaggedParts, + item.DisplayFileLocation, + item.IsImplicitlyDeclared, + item.Document.Id, + item.SourceSpan, + item.ChildItems.SelectAsArray(Dehydrate)); + public INavigableItem Rehydrate(Solution solution) { var childItems = ChildItems == null ? ImmutableArray.Empty : ChildItems.SelectAsArray(c => c.Rehydrate(solution)); return new NavigableItem( - Glyph, DisplayTaggedParts.ToImmutableArrayOrEmpty(), + Glyph, DisplayTaggedParts, DisplayFileLocation, IsImplicitlyDeclared, solution.GetDocument(Document), SourceSpan, diff --git a/src/Features/Core/Portable/TodoComments/ITodoCommentService.cs b/src/Features/Core/Portable/TodoComments/ITodoCommentService.cs index 029b47ac1cced..d552edcc199f0 100644 --- a/src/Features/Core/Portable/TodoComments/ITodoCommentService.cs +++ b/src/Features/Core/Portable/TodoComments/ITodoCommentService.cs @@ -43,18 +43,16 @@ private TodoCommentData CreateSerializableData( var originalLineInfo = location.GetLineSpan(); var mappedLineInfo = location.GetMappedLineSpan(); - return new TodoCommentData - { - Priority = Descriptor.Priority, - Message = Message, - DocumentId = document.Id, - OriginalLine = originalLineInfo.StartLinePosition.Line, - OriginalColumn = originalLineInfo.StartLinePosition.Character, - OriginalFilePath = document.FilePath, - MappedLine = mappedLineInfo.StartLinePosition.Line, - MappedColumn = mappedLineInfo.StartLinePosition.Character, - MappedFilePath = mappedLineInfo.GetMappedFilePathIfExist(), - }; + return new( + priority: Descriptor.Priority, + message: Message, + documentId: document.Id, + originalLine: originalLineInfo.StartLinePosition.Line, + originalColumn: originalLineInfo.StartLinePosition.Character, + originalFilePath: document.FilePath, + mappedLine: mappedLineInfo.StartLinePosition.Line, + mappedColumn: mappedLineInfo.StartLinePosition.Character, + mappedFilePath: mappedLineInfo.GetMappedFilePathIfExist()); } public static async Task ConvertAsync( diff --git a/src/Features/LanguageServer/Protocol/CustomProtocol/FindUsagesLSPContext.cs b/src/Features/LanguageServer/Protocol/CustomProtocol/FindUsagesLSPContext.cs index 81d91ee02a4b0..22885baf0515c 100644 --- a/src/Features/LanguageServer/Protocol/CustomProtocol/FindUsagesLSPContext.cs +++ b/src/Features/LanguageServer/Protocol/CustomProtocol/FindUsagesLSPContext.cs @@ -75,9 +75,10 @@ public FindUsagesLSPContext( } // After all definitions/references have been found, wait here until all results have been reported. - public override Task OnCompletedAsync() => _workQueue.WaitUntilCurrentBatchCompletesAsync(); + public override async ValueTask OnCompletedAsync() + => await _workQueue.WaitUntilCurrentBatchCompletesAsync().ConfigureAwait(false); - public override async Task OnDefinitionFoundAsync(DefinitionItem definition) + public override async ValueTask OnDefinitionFoundAsync(DefinitionItem definition) { using (await _semaphore.DisposableWaitAsync(CancellationToken).ConfigureAwait(false)) { @@ -112,7 +113,7 @@ public override async Task OnDefinitionFoundAsync(DefinitionItem definition) } } - public override async Task OnReferenceFoundAsync(SourceReferenceItem reference) + public override async ValueTask OnReferenceFoundAsync(SourceReferenceItem reference) { using (await _semaphore.DisposableWaitAsync(CancellationToken).ConfigureAwait(false)) { diff --git a/src/Tools/ExternalAccess/FSharp/Internal/Editor/FindUsages/FSharpFindUsagesContext.cs b/src/Tools/ExternalAccess/FSharp/Internal/Editor/FindUsages/FSharpFindUsagesContext.cs index de035c19b9e82..319091d2d981d 100644 --- a/src/Tools/ExternalAccess/FSharp/Internal/Editor/FindUsages/FSharpFindUsagesContext.cs +++ b/src/Tools/ExternalAccess/FSharp/Internal/Editor/FindUsages/FSharpFindUsagesContext.cs @@ -22,29 +22,29 @@ public FSharpFindUsagesContext(IFindUsagesContext context) public Task OnDefinitionFoundAsync(FSharp.FindUsages.FSharpDefinitionItem definition) { - return _context.OnDefinitionFoundAsync(definition.RoslynDefinitionItem); + return _context.OnDefinitionFoundAsync(definition.RoslynDefinitionItem).AsTask(); } public Task OnReferenceFoundAsync(FSharp.FindUsages.FSharpSourceReferenceItem reference) { - return _context.OnReferenceFoundAsync(reference.RoslynSourceReferenceItem); + return _context.OnReferenceFoundAsync(reference.RoslynSourceReferenceItem).AsTask(); } public Task ReportMessageAsync(string message) { - return _context.ReportMessageAsync(message); + return _context.ReportMessageAsync(message).AsTask(); } public Task ReportProgressAsync(int current, int maximum) { #pragma warning disable CS0618 // Type or member is obsolete - return _context.ReportProgressAsync(current, maximum); + return _context.ReportProgressAsync(current, maximum).AsTask(); #pragma warning restore CS0618 // Type or member is obsolete } public Task SetSearchTitleAsync(string title) { - return _context.SetSearchTitleAsync(title); + return _context.SetSearchTitleAsync(title).AsTask(); } } } diff --git a/src/VisualStudio/CodeLens/ReferenceCodeLensProvider.cs b/src/VisualStudio/CodeLens/ReferenceCodeLensProvider.cs index 00b358711f65c..dfa0c1a116312 100644 --- a/src/VisualStudio/CodeLens/ReferenceCodeLensProvider.cs +++ b/src/VisualStudio/CodeLens/ReferenceCodeLensProvider.cs @@ -199,16 +199,19 @@ public void Dispose() // we always get data through VS rather than Roslyn OOP directly since we want final data rather than // raw data from Roslyn OOP such as razor find all reference results - var referenceCount = await _callbackService.InvokeAsync( + var referenceCountOpt = await _callbackService.InvokeAsync( _owner, nameof(ICodeLensContext.GetReferenceCountAsync), new object[] { Descriptor, descriptorContext }, cancellationToken).ConfigureAwait(false); - if (referenceCount == null) + + if (!referenceCountOpt.HasValue) { return null; } + var referenceCount = referenceCountOpt.Value; + var referenceCountString = $"{referenceCount.Count}{(referenceCount.IsCapped ? "+" : string.Empty)}"; return new CodeLensDataPointDescriptor() { @@ -220,7 +223,7 @@ public void Dispose() ImageId = null }; - string GetCodeElementKindsString(CodeElementKinds kind) + static string GetCodeElementKindsString(CodeElementKinds kind) { switch (kind) { @@ -242,53 +245,53 @@ public async Task GetDetailsAsync(CodeLensDescriptorC { // we always get data through VS rather than Roslyn OOP directly since we want final data rather than // raw data from Roslyn OOP such as razor find all reference results - var referenceLocationDescriptors = await _callbackService.InvokeAsync>( + var referenceLocationDescriptors = await _callbackService.InvokeAsync?>( _owner, nameof(ICodeLensContext.FindReferenceLocationsAsync), new object[] { Descriptor, descriptorContext }, cancellationToken).ConfigureAwait(false); - var details = new CodeLensDetailsDescriptor + var entries = referenceLocationDescriptors?.Select(referenceLocationDescriptor => { - Headers = s_header, - Entries = referenceLocationDescriptors.Select(referenceLocationDescriptor => + ImageId imageId = default; + if (referenceLocationDescriptor.Glyph.HasValue) { - ImageId imageId = default; - if (referenceLocationDescriptor.Glyph.HasValue) - { - var moniker = referenceLocationDescriptor.Glyph.Value.GetImageMoniker(); - imageId = new ImageId(moniker.Guid, moniker.Id); - } + var moniker = referenceLocationDescriptor.Glyph.Value.GetImageMoniker(); + imageId = new ImageId(moniker.Guid, moniker.Id); + } - return new CodeLensDetailEntryDescriptor() + return new CodeLensDetailEntryDescriptor() + { + // use default since reference codelens don't require special behaviors + NavigationCommand = null, + NavigationCommandArgs = null, + Tooltip = null, + Fields = new List() { - // use default since reference codelens don't require special behaviors - NavigationCommand = null, - NavigationCommandArgs = null, - Tooltip = null, - Fields = new List() - { - new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.FilePath }, - new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.LineNumber.ToString() }, - new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.ColumnNumber.ToString() }, - new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.ReferenceLineText }, - new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.ReferenceStart.ToString() }, - new CodeLensDetailEntryField() { Text = (referenceLocationDescriptor.ReferenceStart + referenceLocationDescriptor.ReferenceLength).ToString() }, - new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.LongDescription }, - new CodeLensDetailEntryField() { ImageId = imageId }, - new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.BeforeReferenceText2 }, - new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.BeforeReferenceText1 }, - new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.AfterReferenceText1 }, - new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.AfterReferenceText2 } - }, - }; - }).ToList(), + new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.FilePath }, + new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.LineNumber.ToString() }, + new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.ColumnNumber.ToString() }, + new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.ReferenceLineText }, + new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.ReferenceStart.ToString() }, + new CodeLensDetailEntryField() { Text = (referenceLocationDescriptor.ReferenceStart + referenceLocationDescriptor.ReferenceLength).ToString() }, + new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.LongDescription }, + new CodeLensDetailEntryField() { ImageId = imageId }, + new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.BeforeReferenceText2 }, + new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.BeforeReferenceText1 }, + new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.AfterReferenceText1 }, + new CodeLensDetailEntryField() { Text = referenceLocationDescriptor.AfterReferenceText2 } + }, + }; + }).ToList(); + + return new CodeLensDetailsDescriptor + { + Headers = s_header, + Entries = entries ?? SpecializedCollections.EmptyList(), // use default behavior PaneNavigationCommands = null }; - - return details; } internal void Invalidate() diff --git a/src/VisualStudio/Core/Def/External/UnitTesting/UnitTestingReferencesService.cs b/src/VisualStudio/Core/Def/External/UnitTesting/UnitTestingReferencesService.cs index f0c322dd8f3ba..4c94b7606eaa6 100644 --- a/src/VisualStudio/Core/Def/External/UnitTesting/UnitTestingReferencesService.cs +++ b/src/VisualStudio/Core/Def/External/UnitTesting/UnitTestingReferencesService.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -25,18 +26,18 @@ internal class UnitTestingReferencesService CodeLensDescriptorContext descriptorContext, CancellationToken cancellationToken) { - var callerMethods = await callbackService.InvokeAsync>( + var callerMethods = await callbackService.InvokeAsync?>( provider, nameof(ICodeLensContext.FindReferenceMethodsAsync), new object[] { descriptor, descriptorContext }, cancellationToken).ConfigureAwait(false); - if (callerMethods == null || !callerMethods.Any()) + if (!callerMethods.HasValue || callerMethods.Value.IsEmpty) { return Empty; } - return callerMethods.Select(m => ( + return callerMethods.Value.SelectAsArray(m => ( MethodFullyQualifiedName: m.FullName, MethodFilePath: m.FilePath, MethodOutputFilePath: m.OutputFilePath)); diff --git a/src/VisualStudio/Core/Def/Implementation/CodeLens/CodeLensCallbackListener.cs b/src/VisualStudio/Core/Def/Implementation/CodeLens/CodeLensCallbackListener.cs index f1fac962d790f..b7eb9660005be 100644 --- a/src/VisualStudio/Core/Def/Implementation/CodeLens/CodeLensCallbackListener.cs +++ b/src/VisualStudio/Core/Def/Implementation/CodeLens/CodeLensCallbackListener.cs @@ -93,7 +93,7 @@ public async Task> GetProjectVersionsAsync(Imm return await service.GetReferenceCountAsync(solution, documentId, node, maxSearchResults, cancellationToken).ConfigureAwait(false); } - public async Task?> FindReferenceLocationsAsync( + public async Task?> FindReferenceLocationsAsync( CodeLensDescriptor descriptor, CodeLensDescriptorContext descriptorContext, CancellationToken cancellationToken) { var solution = _workspace.CurrentSolution; @@ -108,7 +108,7 @@ public async Task> GetProjectVersionsAsync(Imm return await service.FindReferenceLocationsAsync(solution, documentId, node, cancellationToken).ConfigureAwait(false); } - public async Task?> FindReferenceMethodsAsync( + public async Task?> FindReferenceMethodsAsync( CodeLensDescriptor descriptor, CodeLensDescriptorContext descriptorContext, CancellationToken cancellationToken) { var solution = _workspace.CurrentSolution; diff --git a/src/VisualStudio/Core/Def/Implementation/CodeLens/ICodeLensContext.cs b/src/VisualStudio/Core/Def/Implementation/CodeLens/ICodeLensContext.cs index afd05c09fc019..553d7beceed41 100644 --- a/src/VisualStudio/Core/Def/Implementation/CodeLens/ICodeLensContext.cs +++ b/src/VisualStudio/Core/Def/Implementation/CodeLens/ICodeLensContext.cs @@ -31,13 +31,13 @@ internal interface ICodeLensContext /// /// get reference location descriptor of the given descriptor /// - Task?> FindReferenceLocationsAsync( + Task?> FindReferenceLocationsAsync( CodeLensDescriptor descriptor, CodeLensDescriptorContext descriptorContext, CancellationToken cancellationToken); /// /// Given a document and syntax node, returns a collection of locations of methods that refer to the located node. /// - Task?> FindReferenceMethodsAsync( + Task?> FindReferenceMethodsAsync( CodeLensDescriptor descriptor, CodeLensDescriptorContext descriptorContext, CancellationToken cancellationToken); } } diff --git a/src/VisualStudio/Core/Def/Implementation/CodeLens/RemoteCodeLensReferencesService.cs b/src/VisualStudio/Core/Def/Implementation/CodeLens/RemoteCodeLensReferencesService.cs index 87558a9ea8021..faa2c4bb96bf1 100644 --- a/src/VisualStudio/Core/Def/Implementation/CodeLens/RemoteCodeLensReferencesService.cs +++ b/src/VisualStudio/Core/Def/Implementation/CodeLens/RemoteCodeLensReferencesService.cs @@ -2,7 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.Collections.Generic; +using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; using System.Threading; @@ -12,6 +15,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -27,7 +31,7 @@ public RemoteCodeLensReferencesService() { } - public async Task GetReferenceCountAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, int maxSearchResults, + public async Task GetReferenceCountAsync(Solution solution, DocumentId documentId, SyntaxNode? syntaxNode, int maxSearchResults, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.CodeLens_GetReferenceCountAsync, cancellationToken)) @@ -40,36 +44,41 @@ public async Task GetReferenceCountAsync(Solution solution, Docu var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); if (client != null) { - return await client.RunRemoteAsync( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteCodeLensReferencesService.GetReferenceCountAsync), + var result = await client.TryInvokeAsync( solution, - new object[] { documentId, syntaxNode.Span, maxSearchResults }, + (service, solutionInfo, cancellationToken) => service.GetReferenceCountAsync(solutionInfo, documentId, syntaxNode.Span, maxSearchResults, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); + + return result.HasValue ? result.Value : null; } return await CodeLensReferencesServiceFactory.Instance.GetReferenceCountAsync(solution, documentId, syntaxNode, maxSearchResults, cancellationToken).ConfigureAwait(false); } } - public async Task> FindReferenceLocationsAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, + public async Task?> FindReferenceLocationsAsync(Solution solution, DocumentId documentId, SyntaxNode? syntaxNode, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.CodeLens_FindReferenceLocationsAsync, cancellationToken)) { + if (syntaxNode == null) + { + return null; + } + var descriptors = await FindReferenceLocationsWorkerAsync(solution, documentId, syntaxNode, cancellationToken).ConfigureAwait(false); - if (descriptors == null) + if (!descriptors.HasValue) { return null; } // map spans to right locations using SpanMapper for documents such as cshtml and etc - return await FixUpDescriptorsAsync(solution, descriptors, cancellationToken).ConfigureAwait(false); + return await FixUpDescriptorsAsync(solution, descriptors.Value, cancellationToken).ConfigureAwait(false); } } - public async Task> FindReferenceMethodsAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, + public async Task?> FindReferenceMethodsAsync(Solution solution, DocumentId documentId, SyntaxNode? syntaxNode, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.CodeLens_FindReferenceMethodsAsync, cancellationToken)) @@ -82,20 +91,20 @@ public async Task> FindReferenceMethodsAs var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); if (client != null) { - return await client.RunRemoteAsync>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteCodeLensReferencesService.FindReferenceMethodsAsync), + var result = await client.TryInvokeAsync?>( solution, - new object[] { documentId, syntaxNode.Span }, + (service, solutionInfo, cancellationToken) => service.FindReferenceMethodsAsync(solutionInfo, documentId, syntaxNode.Span, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); + + return result.HasValue ? result.Value : null; } return await CodeLensReferencesServiceFactory.Instance.FindReferenceMethodsAsync(solution, documentId, syntaxNode, cancellationToken).ConfigureAwait(false); } } - public async Task GetFullyQualifiedNameAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, + public async Task GetFullyQualifiedNameAsync(Solution solution, DocumentId documentId, SyntaxNode? syntaxNode, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.CodeLens_GetFullyQualifiedName, cancellationToken)) @@ -108,31 +117,35 @@ public async Task GetFullyQualifiedNameAsync(Solution solution, Document var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); if (client != null) { - return await client.RunRemoteAsync( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteCodeLensReferencesService.GetFullyQualifiedNameAsync), + var result = await client.TryInvokeAsync( solution, - new object[] { documentId, syntaxNode.Span }, + (service, solutionInfo, cancellationToken) => service.GetFullyQualifiedNameAsync(solutionInfo, documentId, syntaxNode.Span, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); + + return result.HasValue ? result.Value : null; } return await CodeLensReferencesServiceFactory.Instance.GetFullyQualifiedNameAsync(solution, documentId, syntaxNode, cancellationToken).ConfigureAwait(false); } } - private static async Task> FixUpDescriptorsAsync( - Solution solution, IEnumerable descriptors, CancellationToken cancellationToken) + private static async Task> FixUpDescriptorsAsync( + Solution solution, ImmutableArray descriptors, CancellationToken cancellationToken) { - var list = new List(); + using var _ = ArrayBuilder.GetInstance(out var list); foreach (var descriptor in descriptors) { var referencedDocumentId = DocumentId.CreateFromSerialized( ProjectId.CreateFromSerialized(descriptor.ProjectGuid), descriptor.DocumentGuid); var document = solution.GetDocument(referencedDocumentId); + if (document == null) + { + continue; + } - var spanMapper = document?.Services.GetService(); + var spanMapper = document.Services.GetService(); if (spanMapper == null) { // for normal document, just add one as they are @@ -182,7 +195,7 @@ private static async Task> FixUpDescrip after2)); } - return list; + return list.ToImmutable(); } private static (string text, int start, int length) GetReferenceInfo(ExcerptResult? reference, ReferenceLocationDescriptor descriptor) @@ -226,24 +239,24 @@ private static string GetLineTextOrEmpty(TextLineCollection lines, int index) return lines[index].ToString().TrimEnd(); } - private async Task> FindReferenceLocationsWorkerAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, + private static async Task?> FindReferenceLocationsWorkerAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, CancellationToken cancellationToken) { if (syntaxNode == null) { - return null; + return ImmutableArray.Empty; } var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); if (client != null) { - return await client.RunRemoteAsync>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteCodeLensReferencesService.FindReferenceLocationsAsync), + var result = await client.TryInvokeAsync?>( solution, - new object[] { documentId, syntaxNode.Span }, + (service, solutionInfo, cancellationToken) => service.FindReferenceLocationsAsync(solutionInfo, documentId, syntaxNode.Span, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); + + return result.HasValue ? result.Value : null; } // remote host is not running. this can happen if remote host is disabled. diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/VisualStudioDesignerAttributeService.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/VisualStudioDesignerAttributeService.cs index d3b36d99eb96c..b00ce35c3a9a5 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/VisualStudioDesignerAttributeService.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/VisualStudioDesignerAttributeService.cs @@ -44,7 +44,7 @@ internal class VisualStudioDesignerAttributeService /// Our connection to the remote OOP server. Created on demand when we startup and then /// kept around for the lifetime of this service. /// - private RemoteServiceConnection? _lazyConnection; + private RemoteServiceConnection? _lazyConnection; /// /// Cache from project to the CPS designer service for it. Computed on demand (which @@ -123,7 +123,7 @@ private async Task StartWorkerAsync() // Pass ourselves in as the callback target for the OOP service. As it discovers // designer attributes it will call back into us to notify VS about it. - _lazyConnection = await client.CreateConnectionAsync(callbackTarget: this, cancellationToken).ConfigureAwait(false); + _lazyConnection = await client.CreateConnectionAsync(callbackTarget: this, cancellationToken).ConfigureAwait(false); // Now kick off scanning in the OOP process. // If the call fails an error has already been reported and there is nothing more to do. diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs index 1929d1521c11c..e0fc7532e2b41 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs @@ -276,14 +276,14 @@ public IDisposable Subscribe(ITableDataSink sink) #region FindUsagesContext overrides. - public sealed override Task SetSearchTitleAsync(string title) + public sealed override ValueTask SetSearchTitleAsync(string title) { // Note: IFindAllReferenceWindow.Title is safe to set from any thread. _findReferencesWindow.Title = title; - return Task.CompletedTask; + return default; } - public sealed override async Task OnCompletedAsync() + public sealed override async ValueTask OnCompletedAsync() { await OnCompletedAsyncWorkerAsync().ConfigureAwait(false); @@ -292,7 +292,7 @@ public sealed override async Task OnCompletedAsync() protected abstract Task OnCompletedAsyncWorkerAsync(); - public sealed override Task OnDefinitionFoundAsync(DefinitionItem definition) + public sealed override ValueTask OnDefinitionFoundAsync(DefinitionItem definition) { lock (Gate) { @@ -302,7 +302,7 @@ public sealed override Task OnDefinitionFoundAsync(DefinitionItem definition) return OnDefinitionFoundWorkerAsync(definition); } - protected abstract Task OnDefinitionFoundWorkerAsync(DefinitionItem definition); + protected abstract ValueTask OnDefinitionFoundWorkerAsync(DefinitionItem definition); protected async Task<(Guid, string projectName, SourceText)> GetGuidAndProjectNameAndSourceTextAsync(Document document) { @@ -369,10 +369,10 @@ protected async Task TryCreateDocumentSpanEntryAsync( return (excerptResult, AbstractDocumentSpanEntry.GetLineContainingPosition(sourceText, documentSpan.SourceSpan.Start)); } - public sealed override Task OnReferenceFoundAsync(SourceReferenceItem reference) + public sealed override ValueTask OnReferenceFoundAsync(SourceReferenceItem reference) => OnReferenceFoundWorkerAsync(reference); - protected abstract Task OnReferenceFoundWorkerAsync(SourceReferenceItem reference); + protected abstract ValueTask OnReferenceFoundWorkerAsync(SourceReferenceItem reference); protected RoslynDefinitionBucket GetOrCreateDefinitionBucket(DefinitionItem definition) { @@ -388,13 +388,13 @@ protected RoslynDefinitionBucket GetOrCreateDefinitionBucket(DefinitionItem defi } } - public sealed override Task ReportMessageAsync(string message) + public sealed override ValueTask ReportMessageAsync(string message) => throw new InvalidOperationException("This should never be called in the streaming case."); - protected sealed override Task ReportProgressAsync(int current, int maximum) + protected sealed override ValueTask ReportProgressAsync(int current, int maximum) { _progressQueue.AddWork((current, maximum)); - return Task.CompletedTask; + return default; } private Task UpdateTableProgressAsync(ImmutableArray<(int current, int maximum)> nextBatch, CancellationToken cancellationToken) diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/WithReferencesFindUsagesContext.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/WithReferencesFindUsagesContext.cs index a0725532785e3..afee03db1277b 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/WithReferencesFindUsagesContext.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/WithReferencesFindUsagesContext.cs @@ -37,7 +37,7 @@ public WithReferencesFindUsagesContext( { } - protected override async Task OnDefinitionFoundWorkerAsync(DefinitionItem definition) + protected override async ValueTask OnDefinitionFoundWorkerAsync(DefinitionItem definition) { // If this is a definition we always want to show, then create entries // for all the declaration locations immediately. Otherwise, we'll @@ -103,7 +103,7 @@ private bool HasDeclarationEntries(DefinitionItem definition) } } - protected override Task OnReferenceFoundWorkerAsync(SourceReferenceItem reference) + protected override ValueTask OnReferenceFoundWorkerAsync(SourceReferenceItem reference) { // Normal references go into both sets of entries. We ensure an entry for the definition, and an entry // for the reference itself. @@ -118,7 +118,7 @@ protected override Task OnReferenceFoundWorkerAsync(SourceReferenceItem referenc addToEntriesWhenNotGroupingByDefinition: true); } - protected async Task OnEntryFoundAsync( + protected async ValueTask OnEntryFoundAsync( DefinitionItem definition, Func> createEntryAsync, bool addToEntriesWhenGroupingByDefinition, diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs index 0918cc8b6fab5..00689589c8480 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs @@ -34,14 +34,14 @@ public WithoutReferencesFindUsagesContext( } // We should never be called in a context where we get references. - protected override Task OnReferenceFoundWorkerAsync(SourceReferenceItem reference) + protected override ValueTask OnReferenceFoundWorkerAsync(SourceReferenceItem reference) => throw new InvalidOperationException(); // Nothing to do on completion. protected override Task OnCompletedAsyncWorkerAsync() => Task.CompletedTask; - protected override async Task OnDefinitionFoundWorkerAsync(DefinitionItem definition) + protected override async ValueTask OnDefinitionFoundWorkerAsync(DefinitionItem definition) { var definitionBucket = GetOrCreateDefinitionBucket(definition); diff --git a/src/VisualStudio/Core/Def/Implementation/TodoComments/VisualStudioTodoCommentsService.cs b/src/VisualStudio/Core/Def/Implementation/TodoComments/VisualStudioTodoCommentsService.cs index cfec1aaf6663d..895aeec7bc1c8 100644 --- a/src/VisualStudio/Core/Def/Implementation/TodoComments/VisualStudioTodoCommentsService.cs +++ b/src/VisualStudio/Core/Def/Implementation/TodoComments/VisualStudioTodoCommentsService.cs @@ -49,7 +49,7 @@ private readonly ConcurrentDictionary - private RemoteServiceConnection? _lazyConnection; + private RemoteServiceConnection? _lazyConnection; /// /// Queue where we enqueue the information we get from OOP to process in batch in the future. @@ -120,7 +120,7 @@ private async Task StartWorkerAsync() // Pass ourselves in as the callback target for the OOP service. As it discovers // todo comments it will call back into us to notify VS about it. - _lazyConnection = await client.CreateConnectionAsync(callbackTarget: this, cancellationToken).ConfigureAwait(false); + _lazyConnection = await client.CreateConnectionAsync(callbackTarget: this, cancellationToken).ConfigureAwait(false); // Now that we've started, let the VS todo list know to start listening to us _eventListenerTracker.EnsureEventListener(_workspace, this); diff --git a/src/VisualStudio/Core/Def/Implementation/Watson/WatsonReporter.cs b/src/VisualStudio/Core/Def/Implementation/Watson/WatsonReporter.cs index d6c7e06cbd236..e51c5e7179a6c 100644 --- a/src/VisualStudio/Core/Def/Implementation/Watson/WatsonReporter.cs +++ b/src/VisualStudio/Core/Def/Implementation/Watson/WatsonReporter.cs @@ -208,7 +208,8 @@ private static List CollectServiceHubLogFilePaths() // name our services more consistently to simplify filtering // filter logs that are not relevant to Roslyn investigation - if (!name.Contains("-" + RemoteServiceName.Prefix) && + if (!name.Contains("-" + ServiceDescriptors.ServiceNamePrefix) && + !name.Contains("-" + RemoteServiceName.Prefix) && !name.Contains("-" + RemoteServiceName.IntelliCodeServiceName) && !name.Contains("-" + RemoteServiceName.RazorServiceName) && !name.Contains("-" + RemoteServiceName.UnitTestingAnalysisServiceName) && diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.ExceptionFormatting.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.ExceptionFormatting.cs index 1b7ca6d514888..ce225d3fb2b73 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.ExceptionFormatting.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.ExceptionFormatting.cs @@ -21,26 +21,14 @@ private static string GetFormattedExceptionStack(Exception exception) return GetStackForAggregateException(exception, aggregate); } - if (exception is RemoteInvocationException remoteException) + if (exception is RemoteInvocationException) { - return GetStackForRemoteException(remoteException); + return exception.ToString(); } return GetStackForException(exception, includeMessageOnly: false); } - private static string GetStackForRemoteException(RemoteInvocationException remoteException) - { - var text = GetStackForException(remoteException, includeMessageOnly: true); - if (remoteException.ErrorData == null) - { - return text; - } - - text = $"{text}{Environment.NewLine}---> (Remote Exception) {remoteException.ErrorData.ToString()} <--- {Environment.NewLine}"; - return text; - } - private static string GetStackForAggregateException(Exception exception, AggregateException aggregate) { var text = GetStackForException(exception, includeMessageOnly: true); @@ -95,7 +83,7 @@ private static string GetAsyncStackTrace(Exception exception) where ShouldShowFrame(declaringType) select FormatFrame(method, declaringType); var stringBuilder = new StringBuilder(); - return String.Join(Environment.NewLine, stackFrameLines); + return string.Join(Environment.NewLine, stackFrameLines); } private static bool ShouldShowFrame(Type declaringType) => @@ -120,7 +108,7 @@ private static string FormatFrame(MethodBase method, Type declaringType) FormatGenericArguments(stringBuilder, declaringType.GetGenericArguments()); } - stringBuilder.Append("("); + stringBuilder.Append('('); if (isAsync) { stringBuilder.Append(ServicesVSResources.Unknown_parameters); @@ -130,7 +118,7 @@ private static string FormatFrame(MethodBase method, Type declaringType) FormatParameters(stringBuilder, method); } - stringBuilder.Append(")"); + stringBuilder.Append(')'); return stringBuilder.ToString(); } @@ -174,7 +162,7 @@ private static void FormatGenericArguments(StringBuilder stringBuilder, Type[] g return; } - stringBuilder.Append("[" + String.Join(",", genericTypeArguments.Select(args => args.Name)) + "]"); + stringBuilder.Append("[" + string.Join(",", genericTypeArguments.Select(args => args.Name)) + "]"); } private static void FormatParameters(StringBuilder stringBuilder, MethodBase method) => diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.cs index 6d28cc1735082..96868c61c0aed 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.cs @@ -21,6 +21,8 @@ internal partial class VisualStudioErrorReportingService : IErrorReportingServic public VisualStudioErrorReportingService(IInfoBarService infoBarService) => _infoBarService = infoBarService; + public string HostDisplayName => "Visual Studio"; + public void ShowErrorInfoInActiveView(string message, params InfoBarUI[] items) => _infoBarService.ShowInfoBarInActiveView(message, items); @@ -33,6 +35,8 @@ public void ShowDetailedErrorInfo(Exception exception) new DetailedErrorInfoDialog(exception.Message, errorInfo).ShowModal(); } + // obsolete - will remove once we remove JsonRpcConnection + // https://github.com/dotnet/roslyn/issues/45859 public void ShowRemoteHostCrashedErrorInfo(Exception? exception) { if (s_infoBarReported) @@ -64,5 +68,21 @@ public void ShowRemoteHostCrashedErrorInfo(Exception? exception) ServicesVSResources.Unfortunately_a_process_used_by_Visual_Studio_has_encountered_an_unrecoverable_error_We_recommend_saving_your_work_and_then_closing_and_restarting_Visual_Studio, infoBarUIs.ToArray()); } + + public void ShowFeatureNotAvailableErrorInfo(string message, Exception? exception) + { + var infoBarUIs = new List(); + + if (exception != null) + { + infoBarUIs.Add(new InfoBarUI( + WorkspacesResources.Show_Stack_Trace, + InfoBarUI.UIKind.HyperLink, + () => ShowDetailedErrorInfo(exception), + closeAfterAction: true)); + } + + ShowGlobalErrorInfo(message, infoBarUIs.ToArray()); + } } } diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioRemoteHostClientShutdownCancellationService.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioRemoteHostClientShutdownCancellationService.cs new file mode 100644 index 0000000000000..0bf150f5dfaad --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioRemoteHostClientShutdownCancellationService.cs @@ -0,0 +1,28 @@ +// 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 enable + +using System; +using System.Composition; +using System.Threading; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.VisualStudio.Shell; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation +{ + [ExportWorkspaceService(typeof(IRemoteHostClientShutdownCancellationService), ServiceLayer.Host), Shared] + internal sealed class VisualStudioRemoteHostClientShutdownCancellationService : IRemoteHostClientShutdownCancellationService + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public VisualStudioRemoteHostClientShutdownCancellationService() + { + } + + public CancellationToken ShutdownToken => VsShellUtilities.ShutdownToken; + } +} diff --git a/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.LogService.cs b/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.LogService.cs index d5583833880c3..8a6be3647de88 100644 --- a/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.LogService.cs +++ b/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.LogService.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.SymbolSearch; using Microsoft.VisualStudio.Shell.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.SymbolSearch { @@ -26,23 +25,23 @@ public LogService(IThreadingContext threadingContext, IVsActivityLog activityLog _activityLog = activityLog; } - public Task LogInfoAsync(string text) + public ValueTask LogInfoAsync(string text, CancellationToken cancellationToken) => LogAsync(text, __ACTIVITYLOG_ENTRYTYPE.ALE_INFORMATION); - public Task LogExceptionAsync(string exception, string text) + public ValueTask LogExceptionAsync(string exception, string text, CancellationToken cancellationToken) => LogAsync(text + ". " + exception, __ACTIVITYLOG_ENTRYTYPE.ALE_ERROR); - private Task LogAsync(string text, __ACTIVITYLOG_ENTRYTYPE type) + private ValueTask LogAsync(string text, __ACTIVITYLOG_ENTRYTYPE type) { Log(text, type); - return Task.CompletedTask; + return default; } private void Log(string text, __ACTIVITYLOG_ENTRYTYPE type) { - if (!this.IsForeground()) + if (!IsForeground()) { - this.InvokeBelowInputPriorityAsync(() => Log(text, type)); + InvokeBelowInputPriorityAsync(() => Log(text, type)); return; } diff --git a/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs b/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs index 1c0b5be7ac4ef..05295bdd9cd66 100644 --- a/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs +++ b/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs @@ -77,7 +77,7 @@ protected override void StartWorking() { // Always pull down the nuget.org index. It contains the MS reference assembly index // inside of it. - Task.Run(() => UpdateSourceInBackgroundAsync(SymbolSearchUpdateEngine.NugetOrgSource)); + Task.Run(() => UpdateSourceInBackgroundAsync(SymbolSearchUpdateEngine.NugetOrgSource, ThreadingContext.DisposalToken)); } private async Task GetEngineAsync(CancellationToken cancellationToken) @@ -89,13 +89,13 @@ private async Task GetEngineAsync(CancellationToken c } } - private async Task UpdateSourceInBackgroundAsync(string sourceName) + private async Task UpdateSourceInBackgroundAsync(string sourceName, CancellationToken cancellationToken) { - var engine = await GetEngineAsync(this.ThreadingContext.DisposalToken).ConfigureAwait(false); - await engine.UpdateContinuouslyAsync(sourceName, _localSettingsDirectory).ConfigureAwait(false); + var engine = await GetEngineAsync(cancellationToken).ConfigureAwait(false); + await engine.UpdateContinuouslyAsync(sourceName, _localSettingsDirectory, cancellationToken).ConfigureAwait(false); } - public async Task> FindPackagesWithTypeAsync( + public async ValueTask> FindPackagesWithTypeAsync( string source, string name, int arity, CancellationToken cancellationToken) { var engine = await GetEngineAsync(cancellationToken).ConfigureAwait(false); @@ -105,7 +105,7 @@ public async Task> FindPackagesWithTypeAsync( return FilterAndOrderPackages(allPackagesWithType); } - public async Task> FindPackagesWithAssemblyAsync( + public async ValueTask> FindPackagesWithAssemblyAsync( string source, string assemblyName, CancellationToken cancellationToken) { var engine = await GetEngineAsync(cancellationToken).ConfigureAwait(false); @@ -157,7 +157,7 @@ private ImmutableArray FilterAndOrderPackages( return result.ToImmutableAndFree(); } - public async Task> FindReferenceAssembliesWithTypeAsync( + public async ValueTask> FindReferenceAssembliesWithTypeAsync( string name, int arity, CancellationToken cancellationToken) { var engine = await GetEngineAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/VisualStudio/Core/Test.Next/Remote/RemoteHostClientServiceFactoryTests.cs b/src/VisualStudio/Core/Test.Next/Remote/RemoteHostClientServiceFactoryTests.cs index a0a8660271e02..cb9cd2ab96322 100644 --- a/src/VisualStudio/Core/Test.Next/Remote/RemoteHostClientServiceFactoryTests.cs +++ b/src/VisualStudio/Core/Test.Next/Remote/RemoteHostClientServiceFactoryTests.cs @@ -76,12 +76,10 @@ public async Task TestSessionWithNoSolution() var mock = new MockLogService(); var client = await service.TryGetRemoteHostClientAsync(CancellationToken.None); - using var connection = await client.CreateConnectionAsync(WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine, callbackTarget: mock, CancellationToken.None); - await connection.RunRemoteAsync( - nameof(IRemoteSymbolSearchUpdateEngine.UpdateContinuouslyAsync), - solution: null, - new object[] { "emptySource", Path.GetTempPath() }, - CancellationToken.None); + using var connection = await client.CreateConnectionAsync(callbackTarget: mock, CancellationToken.None); + Assert.True(await connection.TryInvokeAsync( + (service, cancellationToken) => service.UpdateContinuouslyAsync("emptySource", Path.GetTempPath(), cancellationToken), + CancellationToken.None)); } [Fact] @@ -152,8 +150,8 @@ public Assembly LoadFromPath(string fullPath) private class MockLogService : ISymbolSearchLogService { - public Task LogExceptionAsync(string exception, string text) => Task.CompletedTask; - public Task LogInfoAsync(string text) => Task.CompletedTask; + public ValueTask LogExceptionAsync(string exception, string text, CancellationToken cancellationToken) => default; + public ValueTask LogInfoAsync(string text, CancellationToken cancellationToken) => default; } } } diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index e2d786272672d..db7c6216f43f7 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -138,7 +138,7 @@ public async Task TestTodoComments() var cancellationTokenSource = new CancellationTokenSource(); - using var connection = await client.CreateConnectionAsync(callback, cancellationTokenSource.Token); + using var connection = await client.CreateConnectionAsync(callback, cancellationTokenSource.Token); var invokeTask = connection.TryInvokeAsync( (service, cancellationToken) => service.ComputeTodoCommentsAsync(cancellationToken), @@ -149,18 +149,16 @@ public async Task TestTodoComments() Assert.Equal(1, data.Item2.Length); var commentInfo = data.Item2[0]; - Assert.Equal(new TodoCommentData - { - DocumentId = solution.Projects.Single().Documents.Single().Id, - Priority = 1, - Message = "TODO: Test", - MappedFilePath = null, - OriginalFilePath = "test1.cs", - OriginalLine = 2, - MappedLine = 2, - OriginalColumn = 3, - MappedColumn = 3, - }, commentInfo); + Assert.Equal(new TodoCommentData( + documentId: solution.Projects.Single().Documents.Single().Id, + priority: 1, + message: "TODO: Test", + mappedFilePath: null, + originalFilePath: "test1.cs", + originalLine: 2, + mappedLine: 2, + originalColumn: 3, + mappedColumn: 3), commentInfo); cancellationTokenSource.Cancel(); @@ -216,7 +214,7 @@ public async Task TestDesignerAttributes() var callback = new DesignerAttributeListener(); - using var connection = await client.CreateConnectionAsync(callback, cancellationTokenSource.Token); + using var connection = await client.CreateConnectionAsync(callback, cancellationTokenSource.Token); var invokeTask = connection.TryInvokeAsync( (service, cancellationToken) => service.StartScanningForDesignerAttributesAsync(cancellationToken), diff --git a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs index 6fcb0e98f00db..daa81fadf0575 100644 --- a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs @@ -98,7 +98,7 @@ End Sub } } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/47720"), Trait(Traits.Feature, Traits.Features.RemoteHost)] + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] public async Task TestCancellation() { var code = @"class Test { void Method() { } }"; diff --git a/src/VisualStudio/Core/Test/Diagnostics/TodoListTableDataSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/TodoListTableDataSourceTests.vb index 9a96accafd5eb..382b984035ce4 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/TodoListTableDataSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/TodoListTableDataSourceTests.vb @@ -236,8 +236,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim snapshot1 = factory.GetCurrentSnapshot() provider.Items = New TodoCommentData() { - New TodoCommentData With {.Priority = 1, .Message = "test2", .DocumentId = documentId, .MappedLine = 11, .OriginalLine = 11, .MappedColumn = 21, .OriginalColumn = 21, .MappedFilePath = Nothing, .OriginalFilePath = "test2"}, - New TodoCommentData With {.Priority = 0, .Message = "test", .DocumentId = documentId, .MappedLine = 11, .OriginalLine = 11, .MappedColumn = 21, .OriginalColumn = 21, .MappedFilePath = Nothing, .OriginalFilePath = "test1"} + New TodoCommentData(priority:=1, message:="test2", documentId:=documentId, mappedLine:=11, originalLine:=11, mappedColumn:=21, originalColumn:=21, mappedFilePath:=Nothing, originalFilePath:="test2"), + New TodoCommentData(priority:=0, message:="test", documentId:=documentId, mappedLine:=11, originalLine:=11, mappedColumn:=21, originalColumn:=21, mappedFilePath:=Nothing, originalFilePath:="test1") } provider.RaiseTodoListUpdated(workspace) @@ -270,8 +270,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim snapshot1 = factory.GetCurrentSnapshot() provider.Items = New TodoCommentData() { - New TodoCommentData With {.Priority = 1, .Message = "test2", .DocumentId = documentId, .MappedLine = 11, .OriginalLine = 11, .MappedColumn = 21, .OriginalColumn = 21, .MappedFilePath = Nothing, .OriginalFilePath = "test2"}, - New TodoCommentData With {.Priority = 0, .Message = "test3", .DocumentId = documentId, .MappedLine = 11, .OriginalLine = 11, .MappedColumn = 21, .OriginalColumn = 21, .MappedFilePath = Nothing, .OriginalFilePath = "test3"} + New TodoCommentData(priority:=1, message:="test2", documentId:=documentId, mappedLine:=11, originalLine:=11, mappedColumn:=21, originalColumn:=21, mappedFilePath:=Nothing, originalFilePath:="test2"), + New TodoCommentData(priority:=0, message:="test3", documentId:=documentId, mappedLine:=11, originalLine:=11, mappedColumn:=21, originalColumn:=21, mappedFilePath:=Nothing, originalFilePath:="test3") } provider.RaiseTodoListUpdated(workspace) @@ -360,17 +360,16 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics End Sub Private Function CreateItem(documentId As DocumentId) As TodoCommentData - Return New TodoCommentData With { - .Priority = 0, - .Message = "test", - .DocumentId = documentId, - .MappedLine = 10, - .OriginalLine = 10, - .MappedColumn = 20, - .OriginalColumn = 20, - .MappedFilePath = Nothing, - .OriginalFilePath = "test1" - } + Return New TodoCommentData( + priority:=0, + message:="test", + documentId:=documentId, + mappedLine:=10, + originalLine:=10, + mappedColumn:=20, + originalColumn:=20, + mappedFilePath:=Nothing, + originalFilePath:="test1") End Function Private Class TestTodoListProvider diff --git a/src/VisualStudio/Core/Test/SymbolSearch/SymbolSearchUpdateEngineTests.vb b/src/VisualStudio/Core/Test/SymbolSearch/SymbolSearchUpdateEngineTests.vb index d5f83e815a2a2..cefc8fe99a6b2 100644 --- a/src/VisualStudio/Core/Test/SymbolSearch/SymbolSearchUpdateEngineTests.vb +++ b/src/VisualStudio/Core/Test/SymbolSearch/SymbolSearchUpdateEngineTests.vb @@ -744,12 +744,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch Private Sub New() End Sub - Public Function LogExceptionAsync(exception As String, text As String) As Task Implements ISymbolSearchLogService.LogExceptionAsync - Return Task.CompletedTask + Public Function LogExceptionAsync(exception As String, text As String, cancellationToken As CancellationToken) As ValueTask Implements ISymbolSearchLogService.LogExceptionAsync + Return Nothing End Function - Public Function LogInfoAsync(text As String) As Task Implements ISymbolSearchLogService.LogInfoAsync - Return Task.CompletedTask + Public Function LogInfoAsync(text As String, cancellationToken As CancellationToken) As ValueTask Implements ISymbolSearchLogService.LogInfoAsync + Return Nothing End Function End Class End Class diff --git a/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb b/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb index c86a1a16434e7..94c321c941ce5 100644 --- a/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb +++ b/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb @@ -143,6 +143,7 @@ class {|Definition:C1|} Dim root = Await startDocument.GetSyntaxRootAsync() Dim node = root.FindNode(originalDocument.AnnotatedSpans("Original").First()).AncestorsAndSelf().OfType(Of ClassDeclarationSyntax).First() Dim results = Await codelensService.FindReferenceLocationsAsync(workspace.CurrentSolution, startDocument.Id, node, CancellationToken.None) + Assert.True(results.HasValue) Dim definitionDocument = workspace.Documents.First(Function(d) d.AnnotatedSpans.ContainsKey("Definition")) Dim definitionText = Await workspace.CurrentSolution.GetDocument(definitionDocument.Id).GetTextAsync() @@ -152,7 +153,7 @@ class {|Definition:C1|} Dim actual = New List(Of (String, LinePosition, String)) - For Each result In results + For Each result In results.Value actual.Add((result.FilePath, New LinePosition(result.LineNumber, result.ColumnNumber), result.ReferenceLineText)) Next diff --git a/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs b/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs index 062a5fac72e5d..4017dfb785d5d 100644 --- a/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; @@ -26,9 +27,13 @@ ValueTask GetSemanticClassificationsAsync( /// first int is the index of classification type in , and the /// second and third ints encode the span. /// + [DataContract] internal sealed class SerializableClassifiedSpans { + [DataMember(Order = 0)] public List? ClassificationTypes; + + [DataMember(Order = 1)] public List? ClassificationTriples; internal static SerializableClassifiedSpans Dehydrate(ImmutableArray classifiedSpans) diff --git a/src/Workspaces/Core/Portable/DesignerAttribute/DesignerAttributeData.cs b/src/Workspaces/Core/Portable/DesignerAttribute/DesignerAttributeData.cs index c6600938ce0ed..a364fad975817 100644 --- a/src/Workspaces/Core/Portable/DesignerAttribute/DesignerAttributeData.cs +++ b/src/Workspaces/Core/Portable/DesignerAttribute/DesignerAttributeData.cs @@ -4,26 +4,32 @@ #nullable enable +using System.Runtime.Serialization; + namespace Microsoft.CodeAnalysis.DesignerAttribute { /// /// Serialization typed used to pass information to/from OOP and VS. /// + [DataContract] internal struct DesignerAttributeData { /// /// The category specified in a [DesignerCategory("...")] attribute. /// + [DataMember(Order = 0)] public string? Category; /// /// The document this applies to. /// + [DataMember(Order = 1)] public DocumentId DocumentId; /// /// Path for this . /// + [DataMember(Order = 2)] public string FilePath; } } diff --git a/src/Workspaces/Core/Portable/DesignerAttribute/IRemoteDesignerAttributeService.cs b/src/Workspaces/Core/Portable/DesignerAttribute/IRemoteDesignerAttributeDiscoveryService.cs similarity index 90% rename from src/Workspaces/Core/Portable/DesignerAttribute/IRemoteDesignerAttributeService.cs rename to src/Workspaces/Core/Portable/DesignerAttribute/IRemoteDesignerAttributeDiscoveryService.cs index 4f2565e061768..d6a1c90950d69 100644 --- a/src/Workspaces/Core/Portable/DesignerAttribute/IRemoteDesignerAttributeService.cs +++ b/src/Workspaces/Core/Portable/DesignerAttribute/IRemoteDesignerAttributeDiscoveryService.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.DesignerAttribute /// Interface to allow host (VS) to inform the OOP service to start incrementally analyzing and /// reporting results back to the host. /// - internal interface IRemoteDesignerAttributeService + internal interface IRemoteDesignerAttributeDiscoveryService { ValueTask StartScanningForDesignerAttributesAsync(CancellationToken cancellation); } diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs index 934f8e10f3cdd..c4b3bca3b5b58 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs @@ -11,6 +11,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -21,32 +22,64 @@ namespace Microsoft.CodeAnalysis.Diagnostics { + [DataContract] internal sealed class DiagnosticData : IEquatable { + [DataMember(Order = 0)] public readonly string Id; + + [DataMember(Order = 1)] public readonly string Category; + + [DataMember(Order = 2)] public readonly string? Message; + + [DataMember(Order = 3)] public readonly string? ENUMessageForBingSearch; + [DataMember(Order = 4)] public readonly DiagnosticSeverity Severity; + + [DataMember(Order = 5)] public readonly DiagnosticSeverity DefaultSeverity; + + [DataMember(Order = 6)] public readonly bool IsEnabledByDefault; + + [DataMember(Order = 7)] public readonly int WarningLevel; + + [DataMember(Order = 8)] public readonly IReadOnlyList CustomTags; + + [DataMember(Order = 9)] public readonly ImmutableDictionary Properties; + [DataMember(Order = 10)] public readonly ProjectId? ProjectId; + + [DataMember(Order = 11)] public readonly DiagnosticDataLocation? DataLocation; + + [DataMember(Order = 12)] public readonly IReadOnlyCollection AdditionalLocations; /// /// Language name () or null if the diagnostic is not associated with source code. /// + [DataMember(Order = 13)] public readonly string? Language; + [DataMember(Order = 14)] public readonly string? Title; + + [DataMember(Order = 15)] public readonly string? Description; + + [DataMember(Order = 16)] public readonly string? HelpLink; + + [DataMember(Order = 17)] public readonly bool IsSuppressed; /// diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataLocation.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataLocation.cs index b4e00bf899551..8a3715b98cee5 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataLocation.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticDataLocation.cs @@ -4,33 +4,56 @@ #nullable enable +using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics { + [DataContract] internal sealed class DiagnosticDataLocation { + [DataMember(Order = 0)] public readonly DocumentId? DocumentId; // text can be either given or calculated from original line/column + [DataMember(Order = 1)] public readonly TextSpan? SourceSpan; + [DataMember(Order = 2)] + public readonly string? OriginalFilePath; + + [DataMember(Order = 3)] + public readonly int OriginalStartLine; + + [DataMember(Order = 4)] + public readonly int OriginalStartColumn; + + [DataMember(Order = 5)] + public readonly int OriginalEndLine; + + [DataMember(Order = 6)] + public readonly int OriginalEndColumn; + /// /// Null if path is not mapped and contains the actual path. /// Note that the value might be a relative path. In that case should be used /// as a base path for path resolution. /// + [DataMember(Order = 7)] public readonly string? MappedFilePath; + + [DataMember(Order = 8)] public readonly int MappedStartLine; + + [DataMember(Order = 9)] public readonly int MappedStartColumn; + + [DataMember(Order = 10)] public readonly int MappedEndLine; + + [DataMember(Order = 11)] public readonly int MappedEndColumn; - public readonly string? OriginalFilePath; - public readonly int OriginalStartLine; - public readonly int OriginalStartColumn; - public readonly int OriginalEndLine; - public readonly int OriginalEndColumn; public DiagnosticDataLocation( DocumentId? documentId = null, diff --git a/src/Workspaces/Core/Portable/Diagnostics/SerializableDiagnosticAnalysisResultMap.cs b/src/Workspaces/Core/Portable/Diagnostics/SerializableDiagnosticAnalysisResultMap.cs new file mode 100644 index 0000000000000..f13422cecdc79 --- /dev/null +++ b/src/Workspaces/Core/Portable/Diagnostics/SerializableDiagnosticAnalysisResultMap.cs @@ -0,0 +1,62 @@ +// 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 enable + +using System.Collections.Immutable; +using System.Runtime.Serialization; +using Microsoft.CodeAnalysis.Diagnostics.Telemetry; + +namespace Microsoft.CodeAnalysis.Diagnostics +{ + [DataContract] + internal readonly struct SerializableDiagnosticAnalysisResults + { + public static readonly SerializableDiagnosticAnalysisResults Empty = new( + ImmutableArray<(string, SerializableDiagnosticMap)>.Empty, + ImmutableArray<(string, AnalyzerTelemetryInfo)>.Empty); + + [DataMember(Order = 0)] + internal readonly ImmutableArray<(string analyzerId, SerializableDiagnosticMap diagnosticMap)> Diagnostics; + + [DataMember(Order = 1)] + internal readonly ImmutableArray<(string analyzerId, AnalyzerTelemetryInfo telemetry)> Telemetry; + + public SerializableDiagnosticAnalysisResults( + ImmutableArray<(string analyzerId, SerializableDiagnosticMap diagnosticMap)> diagnostics, + ImmutableArray<(string analyzerId, AnalyzerTelemetryInfo)> telemetry) + { + Diagnostics = diagnostics; + Telemetry = telemetry; + } + } + + [DataContract] + internal readonly struct SerializableDiagnosticMap + { + [DataMember(Order = 0)] + public readonly ImmutableArray<(DocumentId, ImmutableArray)> Syntax; + + [DataMember(Order = 1)] + public readonly ImmutableArray<(DocumentId, ImmutableArray)> Semantic; + + [DataMember(Order = 2)] + public readonly ImmutableArray<(DocumentId, ImmutableArray)> NonLocal; + + [DataMember(Order = 3)] + public readonly ImmutableArray Other; + + public SerializableDiagnosticMap( + ImmutableArray<(DocumentId, ImmutableArray)> syntax, + ImmutableArray<(DocumentId, ImmutableArray)> semantic, + ImmutableArray<(DocumentId, ImmutableArray)> nonLocal, + ImmutableArray other) + { + Syntax = syntax; + Semantic = semantic; + NonLocal = nonLocal; + Other = other; + } + } +} diff --git a/src/Workspaces/Core/Portable/ExtensionManager/IErrorReportingService.cs b/src/Workspaces/Core/Portable/ExtensionManager/IErrorReportingService.cs index d95e110c16773..465dd29994310 100644 --- a/src/Workspaces/Core/Portable/ExtensionManager/IErrorReportingService.cs +++ b/src/Workspaces/Core/Portable/ExtensionManager/IErrorReportingService.cs @@ -11,6 +11,11 @@ namespace Microsoft.CodeAnalysis.Extensions { internal interface IErrorReportingService : IWorkspaceService { + /// + /// Name of the host to be used in error messages (e.g. "Visual Studio"). + /// + string HostDisplayName { get; } + /// /// Show error info in an active view. /// @@ -31,7 +36,12 @@ internal interface IErrorReportingService : IWorkspaceService /// /// Shows info-bar reporting ServiceHub process crash. /// "Unfortunately a process used by Visual Studio has encountered an unrecoverable error". + /// + /// Obsolete - will remove once we remove JsonRpcConnection. + /// https://github.com/dotnet/roslyn/issues/45859 /// void ShowRemoteHostCrashedErrorInfo(Exception? exception); + + void ShowFeatureNotAvailableErrorInfo(string message, Exception? exception); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder_AllDeclarations.cs b/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder_AllDeclarations.cs index 2226c881f907c..4160004b0785c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder_AllDeclarations.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder_AllDeclarations.cs @@ -41,15 +41,18 @@ public static async Task> FindAllDeclarationsWithNormalQ { var solution = project.Solution; - var result = await client.RunRemoteAsync>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteSymbolFinder.FindAllDeclarationsWithNormalQueryAsync), + var result = await client.TryInvokeAsync>( solution, - new object[] { project.Id, query.Name, query.Kind, criteria }, + (service, solutionInfo, cancellationToken) => service.FindAllDeclarationsWithNormalQueryAsync(solutionInfo, project.Id, query.Name, query.Kind, criteria, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); - return await RehydrateAsync(solution, result, cancellationToken).ConfigureAwait(false); + if (!result.HasValue) + { + return ImmutableArray.Empty; + } + + return await RehydrateAsync(solution, result.Value, cancellationToken).ConfigureAwait(false); } return await FindAllDeclarationsWithNormalQueryInCurrentProcessAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder_SourceDeclarations.cs b/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder_SourceDeclarations.cs index 50bab9639d6de..c8c65209cf36e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder_SourceDeclarations.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder_SourceDeclarations.cs @@ -44,15 +44,18 @@ public static async Task> FindSourceDeclarationsWithNorm var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); if (client != null) { - var result = await client.RunRemoteAsync>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteSymbolFinder.FindSolutionSourceDeclarationsWithNormalQueryAsync), + var result = await client.TryInvokeAsync>( solution, - new object[] { name, ignoreCase, criteria }, + (service, solutionInfo, cancellationToken) => service.FindSolutionSourceDeclarationsWithNormalQueryAsync(solutionInfo, name, ignoreCase, criteria, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); - return await RehydrateAsync(solution, result, cancellationToken).ConfigureAwait(false); + if (!result.HasValue) + { + return ImmutableArray.Empty; + } + + return await RehydrateAsync(solution, result.Value, cancellationToken).ConfigureAwait(false); } return await FindSourceDeclarationsWithNormalQueryInCurrentProcessAsync( @@ -80,15 +83,18 @@ public static async Task> FindSourceDeclarationsWithNorm var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); if (client != null) { - var result = await client.RunRemoteAsync>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteSymbolFinder.FindProjectSourceDeclarationsWithNormalQueryAsync), + var result = await client.TryInvokeAsync>( project.Solution, - new object[] { project.Id, name, ignoreCase, criteria }, + (service, solutionInfo, cancellationToken) => service.FindProjectSourceDeclarationsWithNormalQueryAsync(solutionInfo, project.Id, name, ignoreCase, criteria, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); - return await RehydrateAsync(project.Solution, result, cancellationToken).ConfigureAwait(false); + if (!result.HasValue) + { + return ImmutableArray.Empty; + } + + return await RehydrateAsync(project.Solution, result.Value, cancellationToken).ConfigureAwait(false); } return await FindSourceDeclarationsWithNormalQueryInCurrentProcessAsync( @@ -111,15 +117,18 @@ public static async Task> FindSourceDeclarationsWithPatt var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); if (client != null) { - var result = await client.RunRemoteAsync>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteSymbolFinder.FindSolutionSourceDeclarationsWithPatternAsync), + var result = await client.TryInvokeAsync>( solution, - new object[] { pattern, criteria }, + (service, solutionInfo, cancellationToken) => service.FindSolutionSourceDeclarationsWithPatternAsync(solutionInfo, pattern, criteria, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); - return await RehydrateAsync(solution, result, cancellationToken).ConfigureAwait(false); + if (!result.HasValue) + { + return ImmutableArray.Empty; + } + + return await RehydrateAsync(solution, result.Value, cancellationToken).ConfigureAwait(false); } return await FindSourceDeclarationsWithPatternInCurrentProcessAsync( @@ -142,15 +151,18 @@ public static async Task> FindSourceDeclarationsWithPatt var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); if (client != null) { - var result = await client.RunRemoteAsync>( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteSymbolFinder.FindProjectSourceDeclarationsWithPatternAsync), + var result = await client.TryInvokeAsync>( project.Solution, - new object[] { project.Id, pattern, criteria }, + (service, solutionInfo, cancellationToken) => service.FindProjectSourceDeclarationsWithPatternAsync(solutionInfo, project.Id, pattern, criteria, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); - return await RehydrateAsync(project.Solution, result, cancellationToken).ConfigureAwait(false); + if (!result.HasValue) + { + return ImmutableArray.Empty; + } + + return await RehydrateAsync(project.Solution, result.Value, cancellationToken).ConfigureAwait(false); } return await FindSourceDeclarationsWithPatternInCurrentProcessAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs index 8ce7208f93db0..13f3de4b0392d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs @@ -62,7 +62,7 @@ internal static partial class DependentTypeFinder private static async Task> DescendInheritanceTreeAsync( INamedTypeSymbol type, Solution solution, - IImmutableSet projects, + IImmutableSet? projects, Func typeMatches, Func shouldContinueSearching, bool transitive, diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_DerivedClasses.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_DerivedClasses.cs index 8087a7201385d..0d1610aa035f7 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_DerivedClasses.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_DerivedClasses.cs @@ -8,45 +8,22 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Internal.Log; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols { - using SymbolSet = HashSet; - internal static partial class DependentTypeFinder { - public static async Task> FindDerivedClassesAsync( - INamedTypeSymbol type, - Solution solution, - IImmutableSet projects, - bool transitive, - CancellationToken cancellationToken) - { - var result = await TryFindRemoteTypesAsync( - type, solution, projects, transitive, - FunctionId.DependentTypeFinder_FindAndCacheDerivedClassesAsync, - nameof(IRemoteDependentTypeFinder.FindDerivedClassesAsync), - cancellationToken).ConfigureAwait(false); - - if (result.HasValue) - return result.Value; - - return await FindDerivedClassesInCurrentProcessAsync( - type, solution, projects, transitive, cancellationToken).ConfigureAwait(false); - } - private static Task> FindDerivedClassesInCurrentProcessAsync( INamedTypeSymbol type, Solution solution, - IImmutableSet projects, + IImmutableSet? projects, bool transitive, CancellationToken cancellationToken) { if (s_isNonSealedClass(type)) { - static bool TypeMatches(INamedTypeSymbol type, SymbolSet set) + static bool TypeMatches(INamedTypeSymbol type, HashSet set) => TypeHasBaseTypeInSet(type, set); return DescendInheritanceTreeAsync(type, solution, projects, diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_DerivedInterfaces.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_DerivedInterfaces.cs index 83c69d9b29e8f..2968cb04f7031 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_DerivedInterfaces.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_DerivedInterfaces.cs @@ -13,41 +13,19 @@ namespace Microsoft.CodeAnalysis.FindSymbols { - using SymbolSet = HashSet; - internal static partial class DependentTypeFinder { - public static async Task> FindDerivedInterfacesAsync( - INamedTypeSymbol type, - Solution solution, - IImmutableSet projects, - bool transitive, - CancellationToken cancellationToken) - { - var result = await TryFindRemoteTypesAsync( - type, solution, projects, transitive, - FunctionId.DependentTypeFinder_FindAndCacheDerivedInterfacesAsync, - nameof(IRemoteDependentTypeFinder.FindDerivedInterfacesAsync), - cancellationToken).ConfigureAwait(false); - - if (result.HasValue) - return result.Value; - - return await FindDerivedInterfacesInCurrentProcessAsync( - type, solution, projects, transitive, cancellationToken).ConfigureAwait(false); - } - private static Task> FindDerivedInterfacesInCurrentProcessAsync( INamedTypeSymbol type, Solution solution, - IImmutableSet projects, + IImmutableSet? projects, bool transitive, CancellationToken cancellationToken) { // Only an interface can be implemented. if (s_isInterface(type)) { - static bool TypeMatches(INamedTypeSymbol type, SymbolSet set) + static bool TypeMatches(INamedTypeSymbol type, HashSet set) => s_isInterface(type) && TypeHasInterfaceInSet(type, set); return DescendInheritanceTreeAsync(type, solution, projects, diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ImplementingTypes.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ImplementingTypes.cs index fb5da99be31f5..97b4288b65f85 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ImplementingTypes.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ImplementingTypes.cs @@ -8,38 +8,15 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Internal.Log; namespace Microsoft.CodeAnalysis.FindSymbols { - using SymbolSet = HashSet; - internal static partial class DependentTypeFinder { - public static async Task> FindImplementingTypesAsync( - INamedTypeSymbol type, - Solution solution, - IImmutableSet projects, - bool transitive, - CancellationToken cancellationToken) - { - var result = await TryFindRemoteTypesAsync( - type, solution, projects, transitive, - FunctionId.DependentTypeFinder_FindAndCacheImplementingTypesAsync, - nameof(IRemoteDependentTypeFinder.FindImplementingTypesAsync), - cancellationToken).ConfigureAwait(false); - - if (result.HasValue) - return result.Value; - - return await FindImplementingTypesInCurrentProcessAsync( - type, solution, projects, transitive, cancellationToken).ConfigureAwait(false); - } - private static async Task> FindImplementingTypesInCurrentProcessAsync( INamedTypeSymbol type, Solution solution, - IImmutableSet projects, + IImmutableSet? projects, bool transitive, CancellationToken cancellationToken) { @@ -59,7 +36,7 @@ private static async Task> FindImplementingType // 'Base' match and will add to the set. Then, we'll look for types that have 'Base' in their // inheritance chain, and we need to match that by looking in the .BaseType inheritance chain when // looking at 'Derived'. - static bool TypeMatches(INamedTypeSymbol type, SymbolSet set) + static bool TypeMatches(INamedTypeSymbol type, HashSet set) => TypeHasBaseTypeInSet(type, set) || TypeHasInterfaceInSet(type, set); // As long as we keep hitting derived interfaces or implementing non-sealed classes we need to keep diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_Remote.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_Remote.cs index 516b9899a347c..79c01bc5faec3 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_Remote.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_Remote.cs @@ -4,6 +4,7 @@ #nullable enable +using System; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -11,46 +12,75 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols { internal static partial class DependentTypeFinder { - public static async Task?> TryFindRemoteTypesAsync( + public static async Task> FindTypesAsync( INamedTypeSymbol type, Solution solution, - IImmutableSet projects, + IImmutableSet? projects, bool transitive, - FunctionId functionId, - string remoteFunctionName, + DependentTypesKind kind, CancellationToken cancellationToken) { - using (Logger.LogBlock(functionId, cancellationToken)) + if (SerializableSymbolAndProjectId.TryCreate(type, solution, cancellationToken, out var serializedType)) { - if (SerializableSymbolAndProjectId.TryCreate(type, solution, cancellationToken, out var serializedType)) + var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); + if (client != null) { - var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); - if (client != null) - { - var result = await client.RunRemoteAsync>( - WellKnownServiceHubService.CodeAnalysis, - remoteFunctionName, - solution, - new object?[] - { - serializedType, - projects?.Select(p => p.Id).ToArray(), - transitive, - }, - null, - cancellationToken).ConfigureAwait(false); + var projectIds = projects?.SelectAsArray(p => p.Id) ?? default; + + var result = await client.TryInvokeAsync>( + solution, + (service, solutionInfo, cancellationToken) => service.FindTypesAsync(solutionInfo, serializedType, projectIds, transitive, kind, cancellationToken), + callbackTarget: null, + cancellationToken).ConfigureAwait(false); - return await RehydrateAsync(solution, result, cancellationToken).ConfigureAwait(false); + if (!result.HasValue) + { + return ImmutableArray.Empty; } + + return await RehydrateAsync(solution, result.Value, cancellationToken).ConfigureAwait(false); } + + // TODO: Do not fall back to in-proc https://github.com/dotnet/roslyn/issues/47557 } - return null; + return await FindTypesInCurrentProcessAsync(type, solution, projects, transitive, kind, cancellationToken).ConfigureAwait(false); + } + + public static async Task> FindTypesInCurrentProcessAsync( + INamedTypeSymbol type, + Solution solution, + IImmutableSet? projects, + bool transitive, + DependentTypesKind kind, + CancellationToken cancellationToken) + { + var functionId = kind switch + { + DependentTypesKind.DerivedClasses => FunctionId.DependentTypeFinder_FindAndCacheDerivedClassesAsync, + DependentTypesKind.DerivedInterfaces => FunctionId.DependentTypeFinder_FindAndCacheDerivedInterfacesAsync, + DependentTypesKind.ImplementingTypes => FunctionId.DependentTypeFinder_FindAndCacheImplementingTypesAsync, + _ => throw ExceptionUtilities.UnexpectedValue(kind) + }; + + using (Logger.LogBlock(functionId, cancellationToken)) + { + var task = kind switch + { + DependentTypesKind.DerivedClasses => FindDerivedClassesInCurrentProcessAsync(type, solution, projects, transitive, cancellationToken), + DependentTypesKind.DerivedInterfaces => FindDerivedInterfacesInCurrentProcessAsync(type, solution, projects, transitive, cancellationToken), + DependentTypesKind.ImplementingTypes => FindImplementingTypesInCurrentProcessAsync(type, solution, projects, transitive, cancellationToken), + _ => throw ExceptionUtilities.UnexpectedValue(kind) + }; + + return await task.ConfigureAwait(false); + } } private static async Task> RehydrateAsync(Solution solution, ImmutableArray values, CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypesKind.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypesKind.cs new file mode 100644 index 0000000000000..0a87cbfc930c2 --- /dev/null +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypesKind.cs @@ -0,0 +1,13 @@ +// 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. + +namespace Microsoft.CodeAnalysis.FindSymbols +{ + internal enum DependentTypesKind + { + DerivedClasses, + DerivedInterfaces, + ImplementingTypes + } +} diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs index cb0779667f234..82c8473e56ab1 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs @@ -113,7 +113,7 @@ private static void ValidateProjectToDocumentMap( } } - private Task HandleLocationAsync(ISymbol symbol, ReferenceLocation location) + private ValueTask HandleLocationAsync(ISymbol symbol, ReferenceLocation location) => _progress.OnReferenceFoundAsync(symbol, location); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs index c7b4b56426f88..5553edcac308c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs @@ -2,11 +2,13 @@ // 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.Runtime.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.FindSymbols { - internal class FindReferencesSearchOptions + [DataContract] + internal sealed class FindReferencesSearchOptions { public static readonly FindReferencesSearchOptions Default = new FindReferencesSearchOptions( @@ -25,12 +27,14 @@ internal class FindReferencesSearchOptions /// The default for this is false. With that default, all of the above references /// are associated with the property P and not the accessors. /// + [DataMember(Order = 0)] public bool AssociatePropertyReferencesWithSpecificAccessor { get; } /// /// Whether or not we should cascade from the original search symbol to new symbols as we're /// doing the find-references search. /// + [DataMember(Order = 1)] public bool Cascade { get; } public FindReferencesSearchOptions( diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/IRemoteDependentTypeFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/IRemoteDependentTypeFinder.cs deleted file mode 100644 index 0c019db1d97db..0000000000000 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/IRemoteDependentTypeFinder.cs +++ /dev/null @@ -1,35 +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. - -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Remote; - -namespace Microsoft.CodeAnalysis.FindSymbols -{ - internal interface IRemoteDependentTypeFinder - { - Task> FindDerivedClassesAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId type, - ProjectId[] projects, - bool transitive, - CancellationToken cancellationToken); - - Task> FindDerivedInterfacesAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId type, - ProjectId[] projects, - bool transitive, - CancellationToken cancellationToken); - - Task> FindImplementingTypesAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId type, - ProjectId[] projects, - bool transitive, - CancellationToken cancellationToken); - } -} diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/IRemoteDependentTypeFinderService.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/IRemoteDependentTypeFinderService.cs new file mode 100644 index 0000000000000..476787d2bd7ea --- /dev/null +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/IRemoteDependentTypeFinderService.cs @@ -0,0 +1,22 @@ +// 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.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Remote; + +namespace Microsoft.CodeAnalysis.FindSymbols +{ + internal interface IRemoteDependentTypeFinderService + { + ValueTask> FindTypesAsync( + PinnedSolutionInfo solutionInfo, + SerializableSymbolAndProjectId type, + ImmutableArray projectsOpt, + bool transitive, + DependentTypesKind kind, + CancellationToken cancellationToken); + } +} diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/NoOpStreamingFindReferencesProgress.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/NoOpStreamingFindReferencesProgress.cs index 065d32c831e9c..aed4162ab5821 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/NoOpStreamingFindReferencesProgress.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/NoOpStreamingFindReferencesProgress.cs @@ -26,17 +26,17 @@ private NoOpStreamingFindReferencesProgress() public static Task ReportProgressAsync(int current, int maximum) => Task.CompletedTask; #pragma warning restore IDE0060 // Remove unused parameter - public Task OnCompletedAsync() => Task.CompletedTask; - public Task OnStartedAsync() => Task.CompletedTask; - public Task OnDefinitionFoundAsync(ISymbol symbol) => Task.CompletedTask; - public Task OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location) => Task.CompletedTask; - public Task OnFindInDocumentStartedAsync(Document document) => Task.CompletedTask; - public Task OnFindInDocumentCompletedAsync(Document document) => Task.CompletedTask; + public ValueTask OnCompletedAsync() => default; + public ValueTask OnStartedAsync() => default; + public ValueTask OnDefinitionFoundAsync(ISymbol symbol) => default; + public ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location) => default; + public ValueTask OnFindInDocumentStartedAsync(Document document) => default; + public ValueTask OnFindInDocumentCompletedAsync(Document document) => default; private class NoOpProgressTracker : IStreamingProgressTracker { - public Task AddItemsAsync(int count) => Task.CompletedTask; - public Task ItemCompletedAsync() => Task.CompletedTask; + public ValueTask AddItemsAsync(int count) => default; + public ValueTask ItemCompletedAsync() => default; } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/StreamingFindReferencesProgress.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/StreamingFindReferencesProgress.cs index c9e46f9d5e03b..9c662202a1412 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/StreamingFindReferencesProgress.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/StreamingFindReferencesProgress.cs @@ -20,47 +20,47 @@ internal class StreamingFindReferencesProgressAdapter : IStreamingFindReferences public StreamingFindReferencesProgressAdapter(IFindReferencesProgress progress) { _progress = progress; - this.ProgressTracker = new StreamingProgressTracker((current, max) => + ProgressTracker = new StreamingProgressTracker((current, max) => { _progress.ReportProgress(current, max); - return Task.CompletedTask; + return default; }); } - public Task OnCompletedAsync() + public ValueTask OnCompletedAsync() { _progress.OnCompleted(); - return Task.CompletedTask; + return default; } - public Task OnDefinitionFoundAsync(ISymbol symbol) + public ValueTask OnDefinitionFoundAsync(ISymbol symbol) { _progress.OnDefinitionFound(symbol); - return Task.CompletedTask; + return default; } - public Task OnFindInDocumentCompletedAsync(Document document) + public ValueTask OnFindInDocumentCompletedAsync(Document document) { _progress.OnFindInDocumentCompleted(document); - return Task.CompletedTask; + return default; } - public Task OnFindInDocumentStartedAsync(Document document) + public ValueTask OnFindInDocumentStartedAsync(Document document) { _progress.OnFindInDocumentStarted(document); - return Task.CompletedTask; + return default; } - public Task OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location) + public ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location) { _progress.OnReferenceFound(symbol, location); - return Task.CompletedTask; + return default; } - public Task OnStartedAsync() + public ValueTask OnStartedAsync() { _progress.OnStarted(); - return Task.CompletedTask; + return default; } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinder.cs deleted file mode 100644 index 24169be4667a2..0000000000000 --- a/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinder.cs +++ /dev/null @@ -1,35 +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. - -using System; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Remote; - -namespace Microsoft.CodeAnalysis.FindSymbols -{ - internal interface IRemoteSymbolFinder - { - Task FindReferencesAsync(PinnedSolutionInfo solutionInfo, SerializableSymbolAndProjectId symbolAndProjectIdArg, DocumentId[] documentArgs, - SerializableFindReferencesSearchOptions options, CancellationToken cancellationToken); - - Task FindLiteralReferencesAsync(PinnedSolutionInfo solutionInfo, object value, TypeCode typeCode, CancellationToken cancellationToken); - - Task> FindAllDeclarationsWithNormalQueryAsync( - PinnedSolutionInfo solutionInfo, ProjectId projectId, string name, SearchKind searchKind, SymbolFilter criteria, CancellationToken cancellationToken); - - Task> FindSolutionSourceDeclarationsWithNormalQueryAsync( - PinnedSolutionInfo solutionInfo, string name, bool ignoreCase, SymbolFilter criteria, CancellationToken cancellationToken); - - Task> FindProjectSourceDeclarationsWithNormalQueryAsync( - PinnedSolutionInfo solutionInfo, ProjectId projectId, string name, bool ignoreCase, SymbolFilter criteria, CancellationToken cancellationToken); - - Task> FindSolutionSourceDeclarationsWithPatternAsync( - PinnedSolutionInfo solutionInfo, string pattern, SymbolFilter criteria, CancellationToken cancellationToken); - - Task> FindProjectSourceDeclarationsWithPatternAsync( - PinnedSolutionInfo solutionInfo, ProjectId projectId, string pattern, SymbolFilter criteria, CancellationToken cancellationToken); - } -} diff --git a/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinderService.cs b/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinderService.cs new file mode 100644 index 0000000000000..16eb7e98718aa --- /dev/null +++ b/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinderService.cs @@ -0,0 +1,49 @@ +// 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.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.FindSymbols +{ + internal interface IRemoteSymbolFinderService + { + internal interface ICallback + { + ValueTask AddItemsAsync(int count); + ValueTask ItemCompletedAsync(); + ValueTask OnStartedAsync(); + ValueTask OnCompletedAsync(); + ValueTask OnFindInDocumentStartedAsync(DocumentId documentId); + ValueTask OnFindInDocumentCompletedAsync(DocumentId documentId); + ValueTask OnDefinitionFoundAsync(SerializableSymbolAndProjectId definition); + ValueTask OnReferenceFoundAsync(SerializableSymbolAndProjectId definition, SerializableReferenceLocation reference); + ValueTask OnLiteralReferenceFoundAsync(DocumentId documentId, TextSpan span); + } + + ValueTask FindReferencesAsync(PinnedSolutionInfo solutionInfo, SerializableSymbolAndProjectId symbolAndProjectIdArg, ImmutableArray documentArgs, + FindReferencesSearchOptions options, CancellationToken cancellationToken); + + ValueTask FindLiteralReferencesAsync(PinnedSolutionInfo solutionInfo, object value, TypeCode typeCode, CancellationToken cancellationToken); + + ValueTask> FindAllDeclarationsWithNormalQueryAsync( + PinnedSolutionInfo solutionInfo, ProjectId projectId, string name, SearchKind searchKind, SymbolFilter criteria, CancellationToken cancellationToken); + + ValueTask> FindSolutionSourceDeclarationsWithNormalQueryAsync( + PinnedSolutionInfo solutionInfo, string name, bool ignoreCase, SymbolFilter criteria, CancellationToken cancellationToken); + + ValueTask> FindProjectSourceDeclarationsWithNormalQueryAsync( + PinnedSolutionInfo solutionInfo, ProjectId projectId, string name, bool ignoreCase, SymbolFilter criteria, CancellationToken cancellationToken); + + ValueTask> FindSolutionSourceDeclarationsWithPatternAsync( + PinnedSolutionInfo solutionInfo, string pattern, SymbolFilter criteria, CancellationToken cancellationToken); + + ValueTask> FindProjectSourceDeclarationsWithPatternAsync( + PinnedSolutionInfo solutionInfo, ProjectId projectId, string pattern, SymbolFilter criteria, CancellationToken cancellationToken); + } +} diff --git a/src/Workspaces/Core/Portable/FindSymbols/IStreamingFindReferencesProgress.cs b/src/Workspaces/Core/Portable/FindSymbols/IStreamingFindReferencesProgress.cs index d05e6732d9104..8f69f00766c8c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/IStreamingFindReferencesProgress.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/IStreamingFindReferencesProgress.cs @@ -16,20 +16,20 @@ internal interface IStreamingFindReferencesProgress { IStreamingProgressTracker ProgressTracker { get; } - Task OnStartedAsync(); - Task OnCompletedAsync(); + ValueTask OnStartedAsync(); + ValueTask OnCompletedAsync(); - Task OnFindInDocumentStartedAsync(Document document); - Task OnFindInDocumentCompletedAsync(Document document); + ValueTask OnFindInDocumentStartedAsync(Document document); + ValueTask OnFindInDocumentCompletedAsync(Document document); - Task OnDefinitionFoundAsync(ISymbol symbol); - Task OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location); + ValueTask OnDefinitionFoundAsync(ISymbol symbol); + ValueTask OnReferenceFoundAsync(ISymbol symbol, ReferenceLocation location); } internal interface IStreamingFindLiteralReferencesProgress { IStreamingProgressTracker ProgressTracker { get; } - Task OnReferenceFoundAsync(Document document, TextSpan span); + ValueTask OnReferenceFoundAsync(Document document, TextSpan span); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/StreamingProgressCollector.cs b/src/Workspaces/Core/Portable/FindSymbols/StreamingProgressCollector.cs index fb3bf5876c39f..5187ad6dc79a1 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/StreamingProgressCollector.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/StreamingProgressCollector.cs @@ -53,13 +53,13 @@ public ImmutableArray GetReferencedSymbols() } } - public Task OnStartedAsync() => _underlyingProgress.OnStartedAsync(); - public Task OnCompletedAsync() => _underlyingProgress.OnCompletedAsync(); + public ValueTask OnStartedAsync() => _underlyingProgress.OnStartedAsync(); + public ValueTask OnCompletedAsync() => _underlyingProgress.OnCompletedAsync(); - public Task OnFindInDocumentCompletedAsync(Document document) => _underlyingProgress.OnFindInDocumentCompletedAsync(document); - public Task OnFindInDocumentStartedAsync(Document document) => _underlyingProgress.OnFindInDocumentStartedAsync(document); + public ValueTask OnFindInDocumentCompletedAsync(Document document) => _underlyingProgress.OnFindInDocumentCompletedAsync(document); + public ValueTask OnFindInDocumentStartedAsync(Document document) => _underlyingProgress.OnFindInDocumentStartedAsync(document); - public Task OnDefinitionFoundAsync(ISymbol definition) + public ValueTask OnDefinitionFoundAsync(ISymbol definition) { lock (_gate) { @@ -69,7 +69,7 @@ public Task OnDefinitionFoundAsync(ISymbol definition) return _underlyingProgress.OnDefinitionFoundAsync(definition); } - public Task OnReferenceFoundAsync(ISymbol definition, ReferenceLocation location) + public ValueTask OnReferenceFoundAsync(ISymbol definition, ReferenceLocation location) { lock (_gate) { diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindLiteralsServerCallback.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindLiteralsServerCallback.cs index fe4999b7874ef..08ff7c0808362 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindLiteralsServerCallback.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindLiteralsServerCallback.cs @@ -3,13 +3,15 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols { public static partial class SymbolFinder { - internal sealed class FindLiteralsServerCallback + internal sealed class FindLiteralsServerCallback : IRemoteSymbolFinderService.ICallback { private readonly Solution _solution; private readonly IStreamingFindLiteralReferencesProgress _progress; @@ -22,18 +24,35 @@ public FindLiteralsServerCallback( _progress = progress; } - public Task AddItemsAsync(int count) + public ValueTask AddItemsAsync(int count) => _progress.ProgressTracker.AddItemsAsync(count); - public Task ItemCompletedAsync() + public ValueTask ItemCompletedAsync() => _progress.ProgressTracker.ItemCompletedAsync(); - public async Task OnReferenceFoundAsync( - DocumentId documentId, TextSpan span) + public async ValueTask OnLiteralReferenceFoundAsync(DocumentId documentId, TextSpan span) { var document = _solution.GetDocument(documentId); await _progress.OnReferenceFoundAsync(document, span).ConfigureAwait(false); } + + public ValueTask OnCompletedAsync() + => throw ExceptionUtilities.Unreachable; + + public ValueTask OnDefinitionFoundAsync(SerializableSymbolAndProjectId definition) + => throw ExceptionUtilities.Unreachable; + + public ValueTask OnFindInDocumentCompletedAsync(DocumentId documentId) + => throw ExceptionUtilities.Unreachable; + + public ValueTask OnFindInDocumentStartedAsync(DocumentId documentId) + => throw ExceptionUtilities.Unreachable; + + public ValueTask OnReferenceFoundAsync(SerializableSymbolAndProjectId definition, SerializableReferenceLocation reference) + => throw ExceptionUtilities.Unreachable; + + public ValueTask OnStartedAsync() + => throw ExceptionUtilities.Unreachable; } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindReferencesServerCallback.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindReferencesServerCallback.cs index 656c0c0599f33..781bcabbe7f80 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindReferencesServerCallback.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindReferencesServerCallback.cs @@ -6,6 +6,8 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols { @@ -15,7 +17,7 @@ public static partial class SymbolFinder /// Callback object we pass to the OOP server to hear about the result /// of the FindReferencesEngine as it executes there. /// - internal sealed class FindReferencesServerCallback : IEqualityComparer + internal sealed class FindReferencesServerCallback : IRemoteSymbolFinderService.ICallback, IEqualityComparer { private readonly Solution _solution; private readonly IStreamingFindReferencesProgress _progress; @@ -35,25 +37,25 @@ public FindReferencesServerCallback( _definitionMap = new Dictionary(this); } - public Task AddItemsAsync(int count) => _progress.ProgressTracker.AddItemsAsync(count); - public Task ItemCompletedAsync() => _progress.ProgressTracker.ItemCompletedAsync(); + public ValueTask AddItemsAsync(int count) => _progress.ProgressTracker.AddItemsAsync(count); + public ValueTask ItemCompletedAsync() => _progress.ProgressTracker.ItemCompletedAsync(); - public Task OnStartedAsync() => _progress.OnStartedAsync(); - public Task OnCompletedAsync() => _progress.OnCompletedAsync(); + public ValueTask OnStartedAsync() => _progress.OnStartedAsync(); + public ValueTask OnCompletedAsync() => _progress.OnCompletedAsync(); - public Task OnFindInDocumentStartedAsync(DocumentId documentId) + public ValueTask OnFindInDocumentStartedAsync(DocumentId documentId) { var document = _solution.GetDocument(documentId); return _progress.OnFindInDocumentStartedAsync(document); } - public Task OnFindInDocumentCompletedAsync(DocumentId documentId) + public ValueTask OnFindInDocumentCompletedAsync(DocumentId documentId) { var document = _solution.GetDocument(documentId); return _progress.OnFindInDocumentCompletedAsync(document); } - public async Task OnDefinitionFoundAsync(SerializableSymbolAndProjectId definition) + public async ValueTask OnDefinitionFoundAsync(SerializableSymbolAndProjectId definition) { var symbol = await definition.TryRehydrateAsync( _solution, _cancellationToken).ConfigureAwait(false); @@ -69,7 +71,7 @@ public async Task OnDefinitionFoundAsync(SerializableSymbolAndProjectId definiti await _progress.OnDefinitionFoundAsync(symbol).ConfigureAwait(false); } - public async Task OnReferenceFoundAsync( + public async ValueTask OnReferenceFoundAsync( SerializableSymbolAndProjectId definition, SerializableReferenceLocation reference) { ISymbol symbol; @@ -97,6 +99,9 @@ bool IEqualityComparer.Equals(SerializableSymbol int IEqualityComparer.GetHashCode(SerializableSymbolAndProjectId obj) => obj.SymbolKeyData.GetHashCode(); + + public ValueTask OnLiteralReferenceFoundAsync(DocumentId documentId, TextSpan span) + => throw ExceptionUtilities.Unreachable; } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindLiteralReferences.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindLiteralReferences.cs index e7b0da751212b..5095a860c5867 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindLiteralReferences.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindLiteralReferences.cs @@ -29,11 +29,9 @@ internal static async Task FindLiteralReferencesAsync( // the 'progress' parameter which will then update the UI. var serverCallback = new FindLiteralsServerCallback(solution, progress); - await client.RunRemoteAsync( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteSymbolFinder.FindLiteralReferencesAsync), + _ = await client.TryInvokeAsync( solution, - new object[] { value, typeCode }, + (service, solutionInfo, cancellationToken) => service.FindLiteralReferencesAsync(solutionInfo, value, typeCode, cancellationToken), serverCallback, cancellationToken).ConfigureAwait(false); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindReferences_Current.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindReferences_Current.cs index 397e278440eff..8fcb8ca396f7f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindReferences_Current.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindReferences_Current.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.FindSymbols.Finders; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Remote; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols { @@ -37,17 +38,11 @@ internal static async Task FindReferencesAsync( // results as it finds them. When we hear about results we'll forward them to // the 'progress' parameter which will then update the UI. var serverCallback = new FindReferencesServerCallback(solution, progress, cancellationToken); + var documentIds = documents?.SelectAsArray(d => d.Id) ?? default; - await client.RunRemoteAsync( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteSymbolFinder.FindReferencesAsync), + await client.TryInvokeAsync( solution, - new object[] - { - serializedSymbol, - documents?.Select(d => d.Id).ToArray(), - SerializableFindReferencesSearchOptions.Dehydrate(options), - }, + (service, solutionInfo, cancellationToken) => service.FindReferencesAsync(solutionInfo, serializedSymbol, documentIds, options, cancellationToken), serverCallback, cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs index 7a2922d5c79e8..bb4c53c42e339 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs @@ -212,8 +212,8 @@ public static async Task> FindDerivedClassesAsync( internal static async Task> FindDerivedClassesArrayAsync( INamedTypeSymbol type, Solution solution, bool transitive, IImmutableSet projects = null, CancellationToken cancellationToken = default) { - var types = await DependentTypeFinder.FindDerivedClassesAsync( - type, solution, projects, transitive, cancellationToken).ConfigureAwait(false); + var types = await DependentTypeFinder.FindTypesAsync( + type, solution, projects, transitive, DependentTypesKind.DerivedClasses, cancellationToken).ConfigureAwait(false); return types.WhereAsArray(t => IsAccessible(t)); } @@ -248,8 +248,8 @@ public static async Task> FindDerivedInterfacesAsy internal static async Task> FindDerivedInterfacesArrayAsync( INamedTypeSymbol type, Solution solution, bool transitive, IImmutableSet projects = null, CancellationToken cancellationToken = default) { - var types = await DependentTypeFinder.FindDerivedInterfacesAsync( - type, solution, projects, transitive, cancellationToken).ConfigureAwait(false); + var types = await DependentTypeFinder.FindTypesAsync( + type, solution, projects, transitive, DependentTypesKind.DerivedInterfaces, cancellationToken).ConfigureAwait(false); return types.WhereAsArray(t => IsAccessible(t)); } @@ -284,8 +284,8 @@ public static async Task> FindImplementationsAsync internal static async Task> FindImplementationsArrayAsync( INamedTypeSymbol type, Solution solution, bool transitive, IImmutableSet projects = null, CancellationToken cancellationToken = default) { - var types = await DependentTypeFinder.FindImplementingTypesAsync( - type, solution, projects, transitive, cancellationToken).ConfigureAwait(false); + var types = await DependentTypeFinder.FindTypesAsync( + type, solution, projects, transitive, DependentTypesKind.ImplementingTypes, cancellationToken).ConfigureAwait(false); return types.WhereAsArray(t => IsAccessible(t)); } diff --git a/src/Workspaces/Core/Portable/Packaging/IPackageInstallerService.cs b/src/Workspaces/Core/Portable/Packaging/IPackageInstallerService.cs index 0ab4460adf21e..8c68787296e5d 100644 --- a/src/Workspaces/Core/Portable/Packaging/IPackageInstallerService.cs +++ b/src/Workspaces/Core/Portable/Packaging/IPackageInstallerService.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -42,9 +43,13 @@ bool TryInstallPackage(Workspace workspace, DocumentId documentId, event EventHandler PackageSourcesChanged; } - internal struct PackageSource : IEquatable + [DataContract] + internal readonly struct PackageSource : IEquatable { + [DataMember(Order = 0)] public readonly string Name; + + [DataMember(Order = 1)] public readonly string Source; public PackageSource(string name, string source) @@ -54,7 +59,7 @@ public PackageSource(string name, string source) } public override bool Equals(object obj) - => Equals((PackageSource)obj); + => obj is PackageSource source && Equals(source); public bool Equals(PackageSource other) => Name == other.Name && Source == other.Source; diff --git a/src/Workspaces/Core/Portable/ProjectTelemetry/ProjectTelemetryData.cs b/src/Workspaces/Core/Portable/ProjectTelemetry/ProjectTelemetryData.cs index b4a2a628f9db2..b5a83c34538e8 100644 --- a/src/Workspaces/Core/Portable/ProjectTelemetry/ProjectTelemetryData.cs +++ b/src/Workspaces/Core/Portable/ProjectTelemetry/ProjectTelemetryData.cs @@ -4,20 +4,47 @@ #nullable enable +using System.Runtime.Serialization; + namespace Microsoft.CodeAnalysis.ProjectTelemetry { /// /// Serialization typed used to pass information to/from OOP and VS. /// - internal struct ProjectTelemetryData + [DataContract] + internal readonly struct ProjectTelemetryData { - public ProjectId ProjectId; - public string Language; - public int AnalyzerReferencesCount; - public int ProjectReferencesCount; - public int MetadataReferencesCount; - public int DocumentsCount; - public int AdditionalDocumentsCount; + [DataMember(Order = 0)] + public readonly ProjectId ProjectId; + + [DataMember(Order = 1)] + public readonly string Language; + + [DataMember(Order = 2)] + public readonly int AnalyzerReferencesCount; + + [DataMember(Order = 3)] + public readonly int ProjectReferencesCount; + + [DataMember(Order = 4)] + public readonly int MetadataReferencesCount; + + [DataMember(Order = 5)] + public readonly int DocumentsCount; + + [DataMember(Order = 6)] + public readonly int AdditionalDocumentsCount; + + public ProjectTelemetryData(ProjectId projectId, string language, int analyzerReferencesCount, int projectReferencesCount, int metadataReferencesCount, int documentsCount, int additionalDocumentsCount) + { + ProjectId = projectId; + Language = language; + AnalyzerReferencesCount = analyzerReferencesCount; + ProjectReferencesCount = projectReferencesCount; + MetadataReferencesCount = metadataReferencesCount; + DocumentsCount = documentsCount; + AdditionalDocumentsCount = additionalDocumentsCount; + } public bool Equals(ProjectTelemetryData other) => Language.Equals(other.Language) && diff --git a/src/Workspaces/Core/Portable/Remote/IRemoteHostClientShutdownCancellationService.cs b/src/Workspaces/Core/Portable/Remote/IRemoteHostClientShutdownCancellationService.cs new file mode 100644 index 0000000000000..8dda5e81226d7 --- /dev/null +++ b/src/Workspaces/Core/Portable/Remote/IRemoteHostClientShutdownCancellationService.cs @@ -0,0 +1,19 @@ +// 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; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.Remote +{ + // TODO: consider consolidating this with IThreadingContext.DisposalToken + // https://github.com/dotnet/roslyn/issues/47840 + internal interface IRemoteHostClientShutdownCancellationService : IWorkspaceService + { + /// + /// Token signaled when the host starts to shut down. + /// + CancellationToken ShutdownToken { get; } + } +} diff --git a/src/Workspaces/Core/Portable/Remote/PinnedSolutionInfo.cs b/src/Workspaces/Core/Portable/Remote/PinnedSolutionInfo.cs index 3c292fe7b6487..c4a3ccb289e00 100644 --- a/src/Workspaces/Core/Portable/Remote/PinnedSolutionInfo.cs +++ b/src/Workspaces/Core/Portable/Remote/PinnedSolutionInfo.cs @@ -4,11 +4,14 @@ #nullable enable +using System.Runtime.Serialization; + namespace Microsoft.CodeAnalysis.Remote { /// /// Information related to pinned solution /// + [DataContract] internal sealed class PinnedSolutionInfo { /// @@ -16,6 +19,7 @@ internal sealed class PinnedSolutionInfo /// /// This later used to find matching solution between VS and remote host /// + [DataMember(Order = 0)] public readonly int ScopeId; /// @@ -24,14 +28,17 @@ internal sealed class PinnedSolutionInfo /// Features like OOP will use this flag to see whether caching information related to this solution /// can benefit other requests or not /// + [DataMember(Order = 1)] public readonly bool FromPrimaryBranch; /// /// This indicates a Solution.WorkspaceVersion of this solution. remote host engine uses this version /// to decide whether caching this solution will benefit other requests or not /// + [DataMember(Order = 2)] public readonly int WorkspaceVersion; + [DataMember(Order = 3)] public readonly Checksum SolutionChecksum; public PinnedSolutionInfo(int scopeId, bool fromPrimaryBranch, int workspaceVersion, Checksum solutionChecksum) diff --git a/src/Workspaces/Core/Portable/Remote/RemoteArguments.cs b/src/Workspaces/Core/Portable/Remote/RemoteArguments.cs index 1bb740910dc6d..2befbad3917ba 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteArguments.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteArguments.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; @@ -15,32 +16,20 @@ namespace Microsoft.CodeAnalysis.Remote { #region FindReferences - internal class SerializableFindReferencesSearchOptions + [DataContract] + internal sealed class SerializableSymbolAndProjectId { - public bool AssociatePropertyReferencesWithSpecificAccessor; - public bool Cascade; + [DataMember(Order = 0)] + public readonly string SymbolKeyData; - public static SerializableFindReferencesSearchOptions Dehydrate(FindReferencesSearchOptions options) - { - return new SerializableFindReferencesSearchOptions - { - AssociatePropertyReferencesWithSpecificAccessor = options.AssociatePropertyReferencesWithSpecificAccessor, - Cascade = options.Cascade, - }; - } + [DataMember(Order = 1)] + public readonly ProjectId ProjectId; - public FindReferencesSearchOptions Rehydrate() + public SerializableSymbolAndProjectId(string symbolKeyData, ProjectId projectId) { - return new FindReferencesSearchOptions( - associatePropertyReferencesWithSpecificAccessor: AssociatePropertyReferencesWithSpecificAccessor, - cascade: Cascade); + SymbolKeyData = symbolKeyData; + ProjectId = projectId; } - } - - internal class SerializableSymbolAndProjectId - { - public string SymbolKeyData; - public ProjectId ProjectId; public static SerializableSymbolAndProjectId Dehydrate( IAliasSymbol alias, Document document, CancellationToken cancellationToken) @@ -60,11 +49,7 @@ public static SerializableSymbolAndProjectId Dehydrate( } public static SerializableSymbolAndProjectId Create(ISymbol symbol, Project project, CancellationToken cancellationToken) - => new SerializableSymbolAndProjectId - { - SymbolKeyData = symbol.GetSymbolKey(cancellationToken).ToString(), - ProjectId = project.Id, - }; + => new(symbol.GetSymbolKey(cancellationToken).ToString(), project.Id); public static bool TryCreate( ISymbol symbol, Solution solution, CancellationToken cancellationToken, @@ -90,11 +75,7 @@ public static bool TryCreate( return false; } - result = new SerializableSymbolAndProjectId - { - SymbolKeyData = SymbolKey.CreateString(symbol, cancellationToken), - ProjectId = project.Id, - }; + result = new SerializableSymbolAndProjectId(SymbolKey.CreateString(symbol, cancellationToken), project.Id); return true; } public async Task TryRehydrateAsync( @@ -126,83 +107,59 @@ public async Task TryRehydrateAsync( } } - internal class SerializableSymbolUsageInfo : IEquatable + [DataContract] + internal readonly struct SerializableReferenceLocation { - public bool IsValueUsageInfo; - public int UsageInfoUnderlyingValue; - - public static SerializableSymbolUsageInfo Dehydrate(SymbolUsageInfo symbolUsageInfo) - { - bool isValueUsageInfo; - int usageInfoUnderlyingValue; - if (symbolUsageInfo.ValueUsageInfoOpt.HasValue) - { - isValueUsageInfo = true; - usageInfoUnderlyingValue = (int)symbolUsageInfo.ValueUsageInfoOpt.Value; - } - else - { - isValueUsageInfo = false; - usageInfoUnderlyingValue = (int)symbolUsageInfo.TypeOrNamespaceUsageInfoOpt.Value; - } - - return new SerializableSymbolUsageInfo - { - IsValueUsageInfo = isValueUsageInfo, - UsageInfoUnderlyingValue = usageInfoUnderlyingValue - }; - } + [DataMember(Order = 0)] + public readonly DocumentId Document; - public SymbolUsageInfo Rehydrate() - { - return IsValueUsageInfo - ? SymbolUsageInfo.Create((ValueUsageInfo)UsageInfoUnderlyingValue) - : SymbolUsageInfo.Create((TypeOrNamespaceUsageInfo)UsageInfoUnderlyingValue); - } + [DataMember(Order = 1)] + public readonly SerializableSymbolAndProjectId Alias; - public bool Equals(SerializableSymbolUsageInfo other) - { - return other != null && - IsValueUsageInfo == other.IsValueUsageInfo && - UsageInfoUnderlyingValue == other.UsageInfoUnderlyingValue; - } + [DataMember(Order = 2)] + public readonly TextSpan Location; - public override bool Equals(object obj) - => Equals(obj as SerializableSymbolUsageInfo); + [DataMember(Order = 3)] + public readonly bool IsImplicit; - public override int GetHashCode() - => Hash.Combine(IsValueUsageInfo.GetHashCode(), UsageInfoUnderlyingValue.GetHashCode()); - } + [DataMember(Order = 4)] + public readonly SymbolUsageInfo SymbolUsageInfo; - internal class SerializableReferenceLocation - { - public DocumentId Document { get; set; } + [DataMember(Order = 5)] + public readonly ImmutableDictionary AdditionalProperties; - public SerializableSymbolAndProjectId Alias { get; set; } + [DataMember(Order = 6)] + public readonly CandidateReason CandidateReason; - public TextSpan Location { get; set; } - - public bool IsImplicit { get; set; } - - public SerializableSymbolUsageInfo SymbolUsageInfo { get; set; } - - public ImmutableDictionary AdditionalProperties { get; set; } - - public CandidateReason CandidateReason { get; set; } + public SerializableReferenceLocation( + DocumentId document, + SerializableSymbolAndProjectId alias, + TextSpan location, + bool isImplicit, + SymbolUsageInfo symbolUsageInfo, + ImmutableDictionary additionalProperties, + CandidateReason candidateReason) + { + Document = document; + Alias = alias; + Location = location; + IsImplicit = isImplicit; + SymbolUsageInfo = symbolUsageInfo; + AdditionalProperties = additionalProperties; + CandidateReason = candidateReason; + } public static SerializableReferenceLocation Dehydrate( ReferenceLocation referenceLocation, CancellationToken cancellationToken) { - return new SerializableReferenceLocation - { - Document = referenceLocation.Document.Id, - Alias = SerializableSymbolAndProjectId.Dehydrate(referenceLocation.Alias, referenceLocation.Document, cancellationToken), - Location = referenceLocation.Location.SourceSpan, - IsImplicit = referenceLocation.IsImplicit, - SymbolUsageInfo = SerializableSymbolUsageInfo.Dehydrate(referenceLocation.SymbolUsageInfo), - AdditionalProperties = referenceLocation.AdditionalProperties, - CandidateReason = referenceLocation.CandidateReason - }; + return new SerializableReferenceLocation( + referenceLocation.Document.Id, + SerializableSymbolAndProjectId.Dehydrate(referenceLocation.Alias, referenceLocation.Document, cancellationToken), + referenceLocation.Location.SourceSpan, + referenceLocation.IsImplicit, + referenceLocation.SymbolUsageInfo, + referenceLocation.AdditionalProperties, + referenceLocation.CandidateReason); } public async Task RehydrateAsync( @@ -217,7 +174,7 @@ public async Task RehydrateAsync( aliasSymbol, CodeAnalysis.Location.Create(syntaxTree, Location), isImplicit: IsImplicit, - symbolUsageInfo: SymbolUsageInfo.Rehydrate(), + symbolUsageInfo: SymbolUsageInfo, additionalProperties: additionalProperties ?? ImmutableDictionary.Empty, candidateReason: CandidateReason); } diff --git a/src/Workspaces/Core/Portable/Remote/RemoteServiceName.cs b/src/Workspaces/Core/Portable/Remote/RemoteServiceName.cs index ca4092ad760e7..51955fcac88c5 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteServiceName.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteServiceName.cs @@ -50,10 +50,6 @@ public string ToString(bool isRemoteHost64Bit) { (WellKnownServiceHubService.RemoteHost, false) => Prefix + nameof(WellKnownServiceHubService.RemoteHost), (WellKnownServiceHubService.RemoteHost, true) => Prefix + nameof(WellKnownServiceHubService.RemoteHost) + Suffix64, - (WellKnownServiceHubService.CodeAnalysis, false) => Prefix + nameof(WellKnownServiceHubService.CodeAnalysis), - (WellKnownServiceHubService.CodeAnalysis, true) => Prefix + nameof(WellKnownServiceHubService.CodeAnalysis) + Suffix64, - (WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine, false) => Prefix + nameof(WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine), - (WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine, true) => Prefix + nameof(WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine) + Suffix64, (WellKnownServiceHubService.LanguageServer, false) => Prefix + nameof(WellKnownServiceHubService.LanguageServer), (WellKnownServiceHubService.LanguageServer, true) => Prefix + nameof(WellKnownServiceHubService.LanguageServer) + Suffix64, diff --git a/src/Workspaces/Core/Portable/Remote/RemoteUtilities.cs b/src/Workspaces/Core/Portable/Remote/RemoteUtilities.cs index 6397763589adc..69667ac1e81c5 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteUtilities.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteUtilities.cs @@ -2,6 +2,7 @@ // 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.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -17,12 +18,12 @@ internal static class RemoteUtilities /// the set of document text changes necessary to convert to . /// - public static async Task<(DocumentId, TextChange[])[]> GetDocumentTextChangesAsync( + public static async ValueTask)>> GetDocumentTextChangesAsync( Solution oldSolution, Solution newSolution, CancellationToken cancellationToken) { - using var _ = ArrayBuilder<(DocumentId, TextChange[])>.GetInstance(out var builder); + using var _ = ArrayBuilder<(DocumentId, ImmutableArray)>.GetInstance(out var builder); var solutionChanges = newSolution.GetChanges(oldSolution); foreach (var projectChange in solutionChanges.GetProjectChanges()) @@ -32,11 +33,11 @@ internal static class RemoteUtilities var oldDoc = oldSolution.GetDocument(docId); var newDoc = newSolution.GetDocument(docId); var textChanges = await newDoc.GetTextChangesAsync(oldDoc, cancellationToken).ConfigureAwait(false); - builder.Add((docId, textChanges.ToArray())); + builder.Add((docId, textChanges.ToImmutableArray())); } } - return builder.ToArray(); + return builder.ToImmutable(); } /// @@ -44,7 +45,7 @@ internal static class RemoteUtilities /// a solution textually equivalent to the newSolution passed to . /// public static async Task UpdateSolutionAsync( - Solution oldSolution, (DocumentId, TextChange[])[] documentTextChanges, CancellationToken cancellationToken) + Solution oldSolution, ImmutableArray<(DocumentId, ImmutableArray)> documentTextChanges, CancellationToken cancellationToken) { var currentSolution = oldSolution; foreach (var (docId, textChanges) in documentTextChanges) diff --git a/src/Workspaces/Core/Portable/Remote/WellKnownServiceHubService.cs b/src/Workspaces/Core/Portable/Remote/WellKnownServiceHubService.cs index 787fd1e23f60d..d162ce804315f 100644 --- a/src/Workspaces/Core/Portable/Remote/WellKnownServiceHubService.cs +++ b/src/Workspaces/Core/Portable/Remote/WellKnownServiceHubService.cs @@ -10,8 +10,8 @@ internal enum WellKnownServiceHubService { None = 0, RemoteHost = 1, - CodeAnalysis = 2, - RemoteSymbolSearchUpdateEngine = 3, + // obsolete: CodeAnalysis = 2, + // obsolete: RemoteSymbolSearchUpdateService = 3, // obsolete: RemoteDesignerAttributeService = 4, // obsolete: RemoteProjectTelemetryService = 5, // obsolete: RemoteTodoCommentsService = 6, diff --git a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ComplexifiedSpan.cs b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ComplexifiedSpan.cs index 25dfd69e333ca..a807d161e7a35 100644 --- a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ComplexifiedSpan.cs +++ b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ComplexifiedSpan.cs @@ -3,14 +3,21 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Rename.ConflictEngine { + [DataContract] internal readonly struct ComplexifiedSpan { + [DataMember(Order = 0)] public readonly TextSpan OriginalSpan; + + [DataMember(Order = 1)] public readonly TextSpan NewSpan; + + [DataMember(Order = 2)] public readonly ImmutableArray<(TextSpan oldSpan, TextSpan newSpan)> ModifiedSubSpans; public ComplexifiedSpan(TextSpan originalSpan, TextSpan newSpan, ImmutableArray<(TextSpan oldSpan, TextSpan newSpan)> modifiedSubSpans) diff --git a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs index be0f2f98c4695..3e69d9e7aad5d 100644 --- a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs +++ b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs @@ -52,21 +52,19 @@ internal static async Task ResolveConflictsAsync( var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); if (client != null) { - var result = await client.RunRemoteAsync( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteRenamer.ResolveConflictsAsync), + var serializableLocationSet = renameLocationSet.Dehydrate(solution, cancellationToken); + var nonConflictSymbolIds = nonConflictSymbols?.SelectAsArray(s => SerializableSymbolAndProjectId.Dehydrate(solution, s, cancellationToken)) ?? default; + + var result = await client.TryInvokeAsync( solution, - new object?[] - { - renameLocationSet.Dehydrate(solution, cancellationToken), - replacementText, - nonConflictSymbols?.Select(s => SerializableSymbolAndProjectId.Dehydrate(solution, s, cancellationToken)).ToArray(), - }, + (service, solutionInfo, cancellationToken) => service.ResolveConflictsAsync(solutionInfo, serializableLocationSet, replacementText, nonConflictSymbolIds, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); - if (result != null) - return await result.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false); + if (result.HasValue && result.Value != null) + return await result.Value.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false); + + // TODO: do not fall back to in-proc if client is available (https://github.com/dotnet/roslyn/issues/47557) } } diff --git a/src/Workspaces/Core/Portable/Rename/ConflictEngine/RelatedLocation.cs b/src/Workspaces/Core/Portable/Rename/ConflictEngine/RelatedLocation.cs index 6474f170706af..29a5b5c52d530 100644 --- a/src/Workspaces/Core/Portable/Rename/ConflictEngine/RelatedLocation.cs +++ b/src/Workspaces/Core/Portable/Rename/ConflictEngine/RelatedLocation.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Rename.ConflictEngine @@ -11,30 +12,39 @@ namespace Microsoft.CodeAnalysis.Rename.ConflictEngine /// /// Gives information about an identifier span that was affected by Rename (Reference or Non reference) /// + [DataContract] internal readonly struct RelatedLocation : IEquatable { /// /// The Span of the original identifier if it was in source, otherwise the span to check for implicit /// references. /// + [DataMember(Order = 0)] public readonly TextSpan ConflictCheckSpan; + + [DataMember(Order = 1)] + public readonly DocumentId DocumentId; + + [DataMember(Order = 2)] public readonly RelatedLocationType Type; + + [DataMember(Order = 3)] public readonly bool IsReference; - public readonly DocumentId DocumentId; /// /// If there was a conflict at ConflictCheckSpan during rename, then the next phase in rename uses /// ComplexifiedTargetSpan span to be expanded to resolve the conflict. /// + [DataMember(Order = 4)] public readonly TextSpan ComplexifiedTargetSpan; public RelatedLocation(TextSpan conflictCheckSpan, DocumentId documentId, RelatedLocationType type, bool isReference = false, TextSpan complexifiedTargetSpan = default) { - this.ConflictCheckSpan = conflictCheckSpan; - this.Type = type; - this.IsReference = isReference; - this.DocumentId = documentId; - this.ComplexifiedTargetSpan = complexifiedTargetSpan; + ConflictCheckSpan = conflictCheckSpan; + Type = type; + IsReference = isReference; + DocumentId = documentId; + ComplexifiedTargetSpan = complexifiedTargetSpan; } public RelatedLocation WithType(RelatedLocationType type) diff --git a/src/Workspaces/Core/Portable/Rename/IRemoteRenamer.cs b/src/Workspaces/Core/Portable/Rename/IRemoteRenamer.cs deleted file mode 100644 index bc907c60a8921..0000000000000 --- a/src/Workspaces/Core/Portable/Rename/IRemoteRenamer.cs +++ /dev/null @@ -1,300 +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 enable - -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.FindSymbols; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Remote; -using Microsoft.CodeAnalysis.Rename.ConflictEngine; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Rename -{ - internal interface IRemoteRenamer - { - /// - /// Runs the entire rename operation OOP and returns the final result. More efficient (due to less back and - /// forth marshaling) when the intermediary results of rename are not needed. To get the individual parts of - /// rename remoted use and . - /// - Task RenameSymbolAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId symbolAndProjectId, - string replacementText, - SerializableRenameOptionSet options, - SerializableSymbolAndProjectId[] nonConflictSymbolIds, - CancellationToken cancellationToken); - - Task FindRenameLocationsAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId symbolAndProjectId, - SerializableRenameOptionSet options, - CancellationToken cancellationToken); - - Task ResolveConflictsAsync( - PinnedSolutionInfo solutionInfo, - SerializableRenameLocations renameLocationSet, - string replacementText, - SerializableSymbolAndProjectId[] nonConflictSymbolIds, - CancellationToken cancellationToken); - } - - internal struct SerializableRenameOptionSet - { - public bool RenameOverloads; - public bool RenameInStrings; - public bool RenameInComments; - public bool RenameFile; - - public static SerializableRenameOptionSet Dehydrate(RenameOptionSet optionSet) - => new SerializableRenameOptionSet - { - RenameOverloads = optionSet.RenameOverloads, - RenameInStrings = optionSet.RenameInStrings, - RenameInComments = optionSet.RenameInComments, - RenameFile = optionSet.RenameFile, - }; - - public RenameOptionSet Rehydrate() - => new RenameOptionSet(RenameOverloads, RenameInStrings, RenameInComments, RenameFile); - } - - internal class SerializableSearchResult - { - // We use arrays so we can represent default immutable arrays. - - public SerializableRenameLocation[]? Locations; - public SerializableReferenceLocation[]? ImplicitLocations; - public SerializableSymbolAndProjectId[]? ReferencedSymbols; - - [return: NotNullIfNotNull("result")] - public static SerializableSearchResult? Dehydrate(Solution solution, RenameLocations.SearchResult? result, CancellationToken cancellationToken) - => result == null ? null : new SerializableSearchResult - { - Locations = result.Locations.Select(loc => SerializableRenameLocation.Dehydrate(loc)).ToArray(), - ImplicitLocations = result.ImplicitLocations.IsDefault ? null : result.ImplicitLocations.Select(loc => SerializableReferenceLocation.Dehydrate(loc, cancellationToken)).ToArray(), - ReferencedSymbols = result.ReferencedSymbols.IsDefault ? null : result.ReferencedSymbols.Select(s => SerializableSymbolAndProjectId.Dehydrate(solution, s, cancellationToken)).ToArray(), - }; - - public async Task RehydrateAsync(Solution solution, CancellationToken cancellationToken) - { - ImmutableArray implicitLocations = default; - ImmutableArray referencedSymbols = default; - - Contract.ThrowIfNull(Locations); - - using var _1 = ArrayBuilder.GetInstance(Locations.Length, out var locBuilder); - foreach (var loc in Locations) - locBuilder.Add(await loc.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false)); - - var locations = locBuilder.ToImmutableHashSet(); - - if (ImplicitLocations != null) - { - using var _2 = ArrayBuilder.GetInstance(ImplicitLocations.Length, out var builder); - foreach (var loc in ImplicitLocations) - builder.Add(await loc.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false)); - - implicitLocations = builder.ToImmutable(); - } - - if (ReferencedSymbols != null) - { - using var _3 = ArrayBuilder.GetInstance(ReferencedSymbols.Length, out var builder); - foreach (var symbol in ReferencedSymbols) - builder.AddIfNotNull(await symbol.TryRehydrateAsync(solution, cancellationToken).ConfigureAwait(false)); - - referencedSymbols = builder.ToImmutable(); - } - - return new RenameLocations.SearchResult(locations, implicitLocations, referencedSymbols); - } - } - - internal struct SerializableRenameLocation - { - public TextSpan Location; - public DocumentId DocumentId; - public CandidateReason CandidateReason; - public bool IsRenamableAliasUsage; - public bool IsRenamableAccessor; - public TextSpan ContainingLocationForStringOrComment; - public bool IsWrittenTo; - - public static SerializableRenameLocation Dehydrate(RenameLocation location) - => new SerializableRenameLocation - { - Location = location.Location.SourceSpan, - DocumentId = location.DocumentId, - CandidateReason = location.CandidateReason, - IsRenamableAliasUsage = location.IsRenamableAliasUsage, - IsRenamableAccessor = location.IsRenamableAccessor, - ContainingLocationForStringOrComment = location.ContainingLocationForStringOrComment, - IsWrittenTo = location.IsWrittenTo, - }; - - public async Task RehydrateAsync(Solution solution, CancellationToken cancellation) - { - var document = solution.GetRequiredDocument(DocumentId); - var tree = await document.GetRequiredSyntaxTreeAsync(cancellation).ConfigureAwait(false); - - return new RenameLocation( - CodeAnalysis.Location.Create(tree, Location), - DocumentId, - CandidateReason, - IsRenamableAliasUsage, - IsRenamableAccessor, - IsWrittenTo, - ContainingLocationForStringOrComment); - } - } - - internal partial class RenameLocations - { - public SerializableRenameLocations Dehydrate(Solution solution, CancellationToken cancellationToken) - => new SerializableRenameLocations - { - Symbol = SerializableSymbolAndProjectId.Dehydrate(solution, Symbol, cancellationToken), - Options = SerializableRenameOptionSet.Dehydrate(Options), - Result = SerializableSearchResult.Dehydrate(solution, _result, cancellationToken), - }; - - internal static async Task TryRehydrateAsync(Solution solution, SerializableRenameLocations locations, CancellationToken cancellationToken) - { - if (locations == null) - return null; - - if (locations.Symbol == null) - return null; - - var symbol = await locations.Symbol.TryRehydrateAsync(solution, cancellationToken).ConfigureAwait(false); - if (symbol == null) - return null; - - Contract.ThrowIfNull(locations.Result); - - return new RenameLocations( - symbol, - solution, - locations.Options.Rehydrate(), - await locations.Result.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false)); - } - } - - internal class SerializableRenameLocations - { - public SerializableSymbolAndProjectId? Symbol; - public SerializableRenameOptionSet Options; - public SerializableSearchResult? Result; - } - - internal class SerializableComplexifiedSpan - { - public TextSpan OriginalSpan; - public TextSpan NewSpan; - public ImmutableArray<(TextSpan oldSpan, TextSpan newSpan)> ModifiedSubSpans; - - public ComplexifiedSpan Rehydrate() - => new ComplexifiedSpan(OriginalSpan, NewSpan, ModifiedSubSpans); - - public static SerializableComplexifiedSpan Dehydrate(ComplexifiedSpan span) - => new SerializableComplexifiedSpan - { - OriginalSpan = span.OriginalSpan, - NewSpan = span.NewSpan, - ModifiedSubSpans = span.ModifiedSubSpans, - }; - } - - internal class SerializableRelatedLocation - { - public TextSpan ConflictCheckSpan; - public RelatedLocationType Type; - public bool IsReference; - public DocumentId? DocumentId; - public TextSpan ComplexifiedTargetSpan; - - public RelatedLocation Rehydrate() - => new RelatedLocation(ConflictCheckSpan, DocumentId, Type, IsReference, ComplexifiedTargetSpan); - - public static SerializableRelatedLocation Dehydrate(RelatedLocation location) - => new SerializableRelatedLocation - { - ConflictCheckSpan = location.ConflictCheckSpan, - Type = location.Type, - IsReference = location.IsReference, - DocumentId = location.DocumentId, - ComplexifiedTargetSpan = location.ComplexifiedTargetSpan, - }; - } - - internal class SerializableConflictResolution - { - public string? ErrorMessage; - - public bool ReplacementTextValid; - - public (DocumentId documentId, string newName) RenamedDocument; - - // Note: arrays are used (instead of ImmutableArray) as jsonrpc can't marshal null values to/from those types. - // - // We also flatten dictionaries into key/value tuples because jsonrpc only supports dictionaries with string keys. - - public DocumentId[]? DocumentIds; - public SerializableRelatedLocation[]? RelatedLocations; - public (DocumentId, TextChange[])[]? DocumentTextChanges; - public (DocumentId, ImmutableArray<(TextSpan oldSpan, TextSpan newSpan)>)[]? DocumentToModifiedSpansMap; - public (DocumentId, ImmutableArray)[]? DocumentToComplexifiedSpansMap; - public (DocumentId, ImmutableArray)[]? DocumentToRelatedLocationsMap; - - public async Task RehydrateAsync(Solution oldSolution, CancellationToken cancellationToken) - { - if (ErrorMessage != null) - return new ConflictResolution(ErrorMessage); - - var newSolutionWithoutRenamedDocument = await RemoteUtilities.UpdateSolutionAsync( - oldSolution, DocumentTextChanges, cancellationToken).ConfigureAwait(false); - return new ConflictResolution( - oldSolution, - newSolutionWithoutRenamedDocument, - ReplacementTextValid, - RenamedDocument, - DocumentIds.ToImmutableArrayOrEmpty(), - RelatedLocations.SelectAsArray(loc => loc.Rehydrate()), - DocumentToModifiedSpansMap.ToImmutableDictionaryOrEmpty(t => t.Item1, t => t.Item2), - DocumentToComplexifiedSpansMap.ToImmutableDictionaryOrEmpty(t => t.Item1, t => t.Item2.SelectAsArray(c => c.Rehydrate())), - DocumentToRelatedLocationsMap.ToImmutableDictionaryOrEmpty(t => t.Item1, t => t.Item2.SelectAsArray(c => c.Rehydrate()))); - } - } - - internal partial struct ConflictResolution - { - public async Task DehydrateAsync(CancellationToken cancellationToken) - { - if (ErrorMessage != null) - return new SerializableConflictResolution { ErrorMessage = ErrorMessage }; - - var documentTextChanges = await RemoteUtilities.GetDocumentTextChangesAsync(OldSolution, _newSolutionWithoutRenamedDocument, cancellationToken).ConfigureAwait(false); - return new SerializableConflictResolution - { - ReplacementTextValid = ReplacementTextValid, - RenamedDocument = _renamedDocument, - DocumentIds = DocumentIds.ToArray(), - RelatedLocations = RelatedLocations.Select(loc => SerializableRelatedLocation.Dehydrate(loc)).ToArray(), - DocumentTextChanges = documentTextChanges, - DocumentToModifiedSpansMap = _documentToModifiedSpansMap.Select(kvp => (kvp.Key, kvp.Value)).ToArray(), - DocumentToComplexifiedSpansMap = _documentToComplexifiedSpansMap.Select(kvp => (kvp.Key, kvp.Value.SelectAsArray(s => SerializableComplexifiedSpan.Dehydrate(s)))).ToArray(), - DocumentToRelatedLocationsMap = _documentToRelatedLocationsMap.Select(kvp => (kvp.Key, kvp.Value.SelectAsArray(s => SerializableRelatedLocation.Dehydrate(s)))).ToArray(), - }; - } - } -} diff --git a/src/Workspaces/Core/Portable/Rename/IRemoteRenamerService.cs b/src/Workspaces/Core/Portable/Rename/IRemoteRenamerService.cs new file mode 100644 index 0000000000000..8141d4047fbe6 --- /dev/null +++ b/src/Workspaces/Core/Portable/Rename/IRemoteRenamerService.cs @@ -0,0 +1,367 @@ +// 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 enable + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Rename.ConflictEngine; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Rename +{ + internal interface IRemoteRenamerService + { + /// + /// Runs the entire rename operation OOP and returns the final result. More efficient (due to less back and + /// forth marshaling) when the intermediary results of rename are not needed. To get the individual parts of + /// rename remoted use and . + /// + ValueTask RenameSymbolAsync( + PinnedSolutionInfo solutionInfo, + SerializableSymbolAndProjectId symbolAndProjectId, + string replacementText, + SerializableRenameOptionSet options, + ImmutableArray nonConflictSymbolIds, + CancellationToken cancellationToken); + + ValueTask FindRenameLocationsAsync( + PinnedSolutionInfo solutionInfo, + SerializableSymbolAndProjectId symbolAndProjectId, + SerializableRenameOptionSet options, + CancellationToken cancellationToken); + + ValueTask ResolveConflictsAsync( + PinnedSolutionInfo solutionInfo, + SerializableRenameLocations renameLocationSet, + string replacementText, + ImmutableArray nonConflictSymbolIds, + CancellationToken cancellationToken); + } + + [DataContract] + internal readonly struct SerializableRenameOptionSet + { + [DataMember(Order = 0)] + public readonly bool RenameOverloads; + + [DataMember(Order = 1)] + public readonly bool RenameInStrings; + + [DataMember(Order = 2)] + public readonly bool RenameInComments; + + [DataMember(Order = 3)] + public readonly bool RenameFile; + + public SerializableRenameOptionSet(bool renameOverloads, bool renameInStrings, bool renameInComments, bool renameFile) + { + RenameOverloads = renameOverloads; + RenameInStrings = renameInStrings; + RenameInComments = renameInComments; + RenameFile = renameFile; + } + + public static SerializableRenameOptionSet Dehydrate(RenameOptionSet optionSet) + => new(renameOverloads: optionSet.RenameOverloads, + renameInStrings: optionSet.RenameInStrings, + renameInComments: optionSet.RenameInComments, + renameFile: optionSet.RenameFile); + + public RenameOptionSet Rehydrate() + => new(RenameOverloads, RenameInStrings, RenameInComments, RenameFile); + } + + [DataContract] + internal class SerializableSearchResult + { + // We use arrays so we can represent default immutable arrays. + + [DataMember(Order = 0)] + public SerializableRenameLocation[]? Locations; + + [DataMember(Order = 1)] + public SerializableReferenceLocation[]? ImplicitLocations; + + [DataMember(Order = 2)] + public SerializableSymbolAndProjectId[]? ReferencedSymbols; + + [return: NotNullIfNotNull("result")] + public static SerializableSearchResult? Dehydrate(Solution solution, RenameLocations.SearchResult? result, CancellationToken cancellationToken) + => result == null ? null : new SerializableSearchResult + { + Locations = result.Locations.Select(loc => SerializableRenameLocation.Dehydrate(loc)).ToArray(), + ImplicitLocations = result.ImplicitLocations.IsDefault ? null : result.ImplicitLocations.Select(loc => SerializableReferenceLocation.Dehydrate(loc, cancellationToken)).ToArray(), + ReferencedSymbols = result.ReferencedSymbols.IsDefault ? null : result.ReferencedSymbols.Select(s => SerializableSymbolAndProjectId.Dehydrate(solution, s, cancellationToken)).ToArray(), + }; + + public async Task RehydrateAsync(Solution solution, CancellationToken cancellationToken) + { + ImmutableArray implicitLocations = default; + ImmutableArray referencedSymbols = default; + + Contract.ThrowIfNull(Locations); + + using var _1 = ArrayBuilder.GetInstance(Locations.Length, out var locBuilder); + foreach (var loc in Locations) + locBuilder.Add(await loc.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false)); + + var locations = locBuilder.ToImmutableHashSet(); + + if (ImplicitLocations != null) + { + using var _2 = ArrayBuilder.GetInstance(ImplicitLocations.Length, out var builder); + foreach (var loc in ImplicitLocations) + builder.Add(await loc.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false)); + + implicitLocations = builder.ToImmutable(); + } + + if (ReferencedSymbols != null) + { + using var _3 = ArrayBuilder.GetInstance(ReferencedSymbols.Length, out var builder); + foreach (var symbol in ReferencedSymbols) + builder.AddIfNotNull(await symbol.TryRehydrateAsync(solution, cancellationToken).ConfigureAwait(false)); + + referencedSymbols = builder.ToImmutable(); + } + + return new RenameLocations.SearchResult(locations, implicitLocations, referencedSymbols); + } + } + + [DataContract] + internal readonly struct SerializableRenameLocation + { + [DataMember(Order = 0)] + public readonly TextSpan Location; + + [DataMember(Order = 1)] + public readonly DocumentId DocumentId; + + [DataMember(Order = 2)] + public readonly CandidateReason CandidateReason; + + [DataMember(Order = 3)] + public readonly bool IsRenamableAliasUsage; + + [DataMember(Order = 4)] + public readonly bool IsRenamableAccessor; + + [DataMember(Order = 5)] + public readonly TextSpan ContainingLocationForStringOrComment; + + [DataMember(Order = 6)] + public readonly bool IsWrittenTo; + + public SerializableRenameLocation( + TextSpan location, + DocumentId documentId, + CandidateReason candidateReason, + bool isRenamableAliasUsage, + bool isRenamableAccessor, + TextSpan containingLocationForStringOrComment, + bool isWrittenTo) + { + Location = location; + DocumentId = documentId; + CandidateReason = candidateReason; + IsRenamableAliasUsage = isRenamableAliasUsage; + IsRenamableAccessor = isRenamableAccessor; + ContainingLocationForStringOrComment = containingLocationForStringOrComment; + IsWrittenTo = isWrittenTo; + } + + public static SerializableRenameLocation Dehydrate(RenameLocation location) + => new(location.Location.SourceSpan, + location.DocumentId, + location.CandidateReason, + location.IsRenamableAliasUsage, + location.IsRenamableAccessor, + location.ContainingLocationForStringOrComment, + location.IsWrittenTo); + + public async Task RehydrateAsync(Solution solution, CancellationToken cancellation) + { + var document = solution.GetRequiredDocument(DocumentId); + var tree = await document.GetRequiredSyntaxTreeAsync(cancellation).ConfigureAwait(false); + + return new RenameLocation( + CodeAnalysis.Location.Create(tree, Location), + DocumentId, + CandidateReason, + IsRenamableAliasUsage, + IsRenamableAccessor, + IsWrittenTo, + ContainingLocationForStringOrComment); + } + } + + internal partial class RenameLocations + { + public SerializableRenameLocations Dehydrate(Solution solution, CancellationToken cancellationToken) + => new SerializableRenameLocations( + SerializableSymbolAndProjectId.Dehydrate(solution, Symbol, cancellationToken), + SerializableRenameOptionSet.Dehydrate(Options), + SerializableSearchResult.Dehydrate(solution, _result, cancellationToken)); + + internal static async Task TryRehydrateAsync(Solution solution, SerializableRenameLocations locations, CancellationToken cancellationToken) + { + if (locations == null) + return null; + + if (locations.Symbol == null) + return null; + + var symbol = await locations.Symbol.TryRehydrateAsync(solution, cancellationToken).ConfigureAwait(false); + if (symbol == null) + return null; + + Contract.ThrowIfNull(locations.Result); + + return new RenameLocations( + symbol, + solution, + locations.Options.Rehydrate(), + await locations.Result.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false)); + } + } + + [DataContract] + internal sealed class SerializableRenameLocations + { + [DataMember(Order = 0)] + public readonly SerializableSymbolAndProjectId? Symbol; + + [DataMember(Order = 1)] + public readonly SerializableRenameOptionSet Options; + + [DataMember(Order = 2)] + public readonly SerializableSearchResult? Result; + + public SerializableRenameLocations(SerializableSymbolAndProjectId? symbol, SerializableRenameOptionSet options, SerializableSearchResult? result) + { + Symbol = symbol; + Options = options; + Result = result; + } + } + + [DataContract] + internal sealed class SerializableConflictResolution + { + [DataMember(Order = 0)] + public readonly string? ErrorMessage; + + [DataMember(Order = 1)] + public readonly SuccessfulConflictResolution? Resolution; + + public SerializableConflictResolution(string? errorMessage, SuccessfulConflictResolution? resolution) + { + ErrorMessage = errorMessage; + Resolution = resolution; + } + + public async Task RehydrateAsync(Solution oldSolution, CancellationToken cancellationToken) + { + if (ErrorMessage != null) + return new ConflictResolution(ErrorMessage); + + Contract.ThrowIfNull(Resolution); + + var newSolutionWithoutRenamedDocument = await RemoteUtilities.UpdateSolutionAsync( + oldSolution, Resolution.DocumentTextChanges, cancellationToken).ConfigureAwait(false); + + return new ConflictResolution( + oldSolution, + newSolutionWithoutRenamedDocument, + Resolution.ReplacementTextValid, + Resolution.RenamedDocument, + Resolution.DocumentIds, + Resolution.RelatedLocations, + Resolution.DocumentToModifiedSpansMap, + Resolution.DocumentToComplexifiedSpansMap, + Resolution.DocumentToRelatedLocationsMap); + } + } + + [DataContract] + internal sealed class SuccessfulConflictResolution + { + [DataMember(Order = 0)] + public readonly bool ReplacementTextValid; + + [DataMember(Order = 1)] + public readonly (DocumentId documentId, string newName) RenamedDocument; + + [DataMember(Order = 2)] + public readonly ImmutableArray DocumentIds; + + [DataMember(Order = 3)] + public readonly ImmutableArray RelatedLocations; + + [DataMember(Order = 4)] + public readonly ImmutableArray<(DocumentId, ImmutableArray)> DocumentTextChanges; + + [DataMember(Order = 5)] + public readonly ImmutableDictionary> DocumentToModifiedSpansMap; + + [DataMember(Order = 6)] + public readonly ImmutableDictionary> DocumentToComplexifiedSpansMap; + + [DataMember(Order = 7)] + public readonly ImmutableDictionary> DocumentToRelatedLocationsMap; + + public SuccessfulConflictResolution( + bool replacementTextValid, + (DocumentId documentId, string newName) renamedDocument, + ImmutableArray documentIds, + ImmutableArray relatedLocations, + ImmutableArray<(DocumentId, ImmutableArray)> documentTextChanges, + ImmutableDictionary> documentToModifiedSpansMap, + ImmutableDictionary> documentToComplexifiedSpansMap, + ImmutableDictionary> documentToRelatedLocationsMap) + { + ReplacementTextValid = replacementTextValid; + RenamedDocument = renamedDocument; + DocumentIds = documentIds; + RelatedLocations = relatedLocations; + DocumentTextChanges = documentTextChanges; + DocumentToModifiedSpansMap = documentToModifiedSpansMap; + DocumentToComplexifiedSpansMap = documentToComplexifiedSpansMap; + DocumentToRelatedLocationsMap = documentToRelatedLocationsMap; + } + } + + internal partial struct ConflictResolution + { + public async Task DehydrateAsync(CancellationToken cancellationToken) + { + if (ErrorMessage != null) + return new SerializableConflictResolution(ErrorMessage, resolution: null); + + var documentTextChanges = await RemoteUtilities.GetDocumentTextChangesAsync(OldSolution, _newSolutionWithoutRenamedDocument, cancellationToken).ConfigureAwait(false); + return new SerializableConflictResolution( + errorMessage: null, + new SuccessfulConflictResolution( + ReplacementTextValid, + _renamedDocument, + DocumentIds, + RelatedLocations, + documentTextChanges, + _documentToModifiedSpansMap, + _documentToComplexifiedSpansMap, + _documentToRelatedLocationsMap)); + } + } +} diff --git a/src/Workspaces/Core/Portable/Rename/RenameLocations.cs b/src/Workspaces/Core/Portable/Rename/RenameLocations.cs index 2c1ece38c04ab..d8f46cdcf6e01 100644 --- a/src/Workspaces/Core/Portable/Rename/RenameLocations.cs +++ b/src/Workspaces/Core/Portable/Rename/RenameLocations.cs @@ -62,47 +62,6 @@ internal static RenameLocations Create( new SearchResult(locations, implicitLocations, referencedSymbols)); } - private static RenameLocations Create( - ISymbol symbol, - Solution solution, - RenameOptionSet options, - SearchResult originalSymbolResult, - ImmutableArray overloadsResult, - ImmutableArray stringsResult, - ImmutableArray commentsResult) - { - var mergedLocations = ImmutableHashSet.CreateBuilder(); - using var _1 = ArrayBuilder.GetInstance(out var mergedReferencedSymbols); - using var _2 = ArrayBuilder.GetInstance(out var mergedImplicitLocations); - - if (options.RenameInStrings) - mergedLocations.AddRange(stringsResult); - - if (options.RenameInComments) - mergedLocations.AddRange(commentsResult); - - var renameMethodGroupReferences = options.RenameOverloads || !GetOverloadedSymbols(symbol).Any(); - var overloadsToMerge = options.RenameOverloads - ? overloadsResult.NullToEmpty() - : ImmutableArray.Empty; - foreach (var result in overloadsToMerge.Concat(originalSymbolResult)) - { - mergedLocations.AddRange(renameMethodGroupReferences - ? result.Locations - : result.Locations.Where(x => x.CandidateReason != CandidateReason.MemberGroup)); - - mergedImplicitLocations.AddRange(result.ImplicitLocations); - mergedReferencedSymbols.AddRange(result.ReferencedSymbols); - } - - return new RenameLocations( - symbol, solution, options, - new SearchResult( - mergedLocations.ToImmutable(), - mergedImplicitLocations.ToImmutable(), - mergedReferencedSymbols.ToImmutable())); - } - /// /// Find the locations that need to be renamed. /// @@ -121,26 +80,24 @@ public static async Task FindLocationsAsync( var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); if (client != null) { - var result = await client.RunRemoteAsync( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteRenamer.FindRenameLocationsAsync), + var options = SerializableRenameOptionSet.Dehydrate(optionSet); + + var result = await client.TryInvokeAsync( solution, - new object[] - { - serializedSymbol, - SerializableRenameOptionSet.Dehydrate(optionSet), - }, + (service, solutionInfo, cancellationToken) => service.FindRenameLocationsAsync(solutionInfo, serializedSymbol, options, cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); - if (result != null) + if (result.HasValue && result.Value != null) { - var rehydrated = await RenameLocations.TryRehydrateAsync( - solution, result, cancellationToken).ConfigureAwait(false); + var rehydrated = await TryRehydrateAsync( + solution, result.Value, cancellationToken).ConfigureAwait(false); if (rehydrated != null) return rehydrated; } + + // TODO: do not fall back to in-proc if client is available (https://github.com/dotnet/roslyn/issues/47557) } } } diff --git a/src/Workspaces/Core/Portable/Rename/Renamer.cs b/src/Workspaces/Core/Portable/Rename/Renamer.cs index 9af151b77fc0d..c192cf019736c 100644 --- a/src/Workspaces/Core/Portable/Rename/Renamer.cs +++ b/src/Workspaces/Core/Portable/Rename/Renamer.cs @@ -127,22 +127,25 @@ internal static async Task RenameSymbolAsync( var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); if (client != null) { - var result = await client.RunRemoteAsync( - WellKnownServiceHubService.CodeAnalysis, - nameof(IRemoteRenamer.RenameSymbolAsync), + var options = SerializableRenameOptionSet.Dehydrate(optionSet); + var nonConflictSymbolIds = nonConflictSymbols?.SelectAsArray(s => SerializableSymbolAndProjectId.Dehydrate(solution, s, cancellationToken)) ?? default; + + var result = await client.TryInvokeAsync( solution, - new object?[] - { + (service, solutionInfo, cancellationToken) => service.RenameSymbolAsync( + solutionInfo, serializedSymbol, newName, - SerializableRenameOptionSet.Dehydrate(optionSet), - nonConflictSymbols?.Select(s => SerializableSymbolAndProjectId.Dehydrate(solution, s, cancellationToken)).ToArray(), - }, + options, + nonConflictSymbolIds, + cancellationToken), callbackTarget: null, cancellationToken).ConfigureAwait(false); - if (result != null) - return await result.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false); + if (result.HasValue && result.Value != null) + return await result.Value.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false); + + // TODO: do not fall back to in-proc if client is available (https://github.com/dotnet/roslyn/issues/47557) } } } diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/IStreamingProgressTracker.cs b/src/Workspaces/Core/Portable/Shared/Utilities/IStreamingProgressTracker.cs index 37088d4b1609e..cea0381877f5e 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/IStreamingProgressTracker.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/IStreamingProgressTracker.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.Shared.Utilities { internal interface IStreamingProgressTracker { - Task AddItemsAsync(int count); - Task ItemCompletedAsync(); + ValueTask AddItemsAsync(int count); + ValueTask ItemCompletedAsync(); } } diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/StreamingProgressTracker.cs b/src/Workspaces/Core/Portable/Shared/Utilities/StreamingProgressTracker.cs index 7e4be62ec4407..ec673f6cc912f 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/StreamingProgressTracker.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/StreamingProgressTracker.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. +#nullable enable + using System; using System.Threading; using System.Threading.Tasks; @@ -11,41 +13,36 @@ namespace Microsoft.CodeAnalysis.Shared.Utilities /// /// Utility class that can be used to track the progress of an operation in a threadsafe manner. /// - internal class StreamingProgressTracker : IStreamingProgressTracker + internal sealed class StreamingProgressTracker : IStreamingProgressTracker { private int _completedItems; private int _totalItems; - private readonly Func _updateActionOpt; - - public StreamingProgressTracker() - : this(null) - { - } + private readonly Func? _updateAction; - public StreamingProgressTracker(Func updateActionOpt) - => _updateActionOpt = updateActionOpt; + public StreamingProgressTracker(Func? updateAction = null) + => _updateAction = updateAction; - public Task AddItemsAsync(int count) + public ValueTask AddItemsAsync(int count) { Interlocked.Add(ref _totalItems, count); return UpdateAsync(); } - public Task ItemCompletedAsync() + public ValueTask ItemCompletedAsync() { Interlocked.Increment(ref _completedItems); return UpdateAsync(); } - private Task UpdateAsync() + private ValueTask UpdateAsync() { - if (_updateActionOpt == null) + if (_updateAction == null) { - return Task.CompletedTask; + return default; } - return _updateActionOpt(_completedItems, _totalItems); + return _updateAction(_completedItems, _totalItems); } } } diff --git a/src/Workspaces/Core/Portable/SymbolSearch/IRemoteSymbolSearchUpdateEngine.cs b/src/Workspaces/Core/Portable/SymbolSearch/IRemoteSymbolSearchUpdateEngine.cs index 09255a5cfd178..88a3c952e654d 100644 --- a/src/Workspaces/Core/Portable/SymbolSearch/IRemoteSymbolSearchUpdateEngine.cs +++ b/src/Workspaces/Core/Portable/SymbolSearch/IRemoteSymbolSearchUpdateEngine.cs @@ -2,18 +2,17 @@ // 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.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.SymbolSearch { - internal interface IRemoteSymbolSearchUpdateEngine + internal interface IRemoteSymbolSearchUpdateService { - Task UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory); - - Task> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken); - Task> FindPackagesWithAssemblyAsync(string source, string name, CancellationToken cancellationToken); - Task> FindReferenceAssembliesWithTypeAsync(string name, int arity, CancellationToken cancellationToken); + ValueTask UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory, CancellationToken cancellationToken); + ValueTask> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken); + ValueTask> FindPackagesWithAssemblyAsync(string source, string name, CancellationToken cancellationToken); + ValueTask> FindReferenceAssembliesWithTypeAsync(string name, int arity, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchLogService.cs b/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchLogService.cs index bfe34a21e5f1a..93fffce2ead10 100644 --- a/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchLogService.cs +++ b/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchLogService.cs @@ -2,6 +2,7 @@ // 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; using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.SymbolSearch @@ -11,7 +12,7 @@ namespace Microsoft.CodeAnalysis.SymbolSearch /// internal interface ISymbolSearchLogService { - Task LogExceptionAsync(string exception, string text); - Task LogInfoAsync(string text); + ValueTask LogExceptionAsync(string exception, string text, CancellationToken cancellationToken); + ValueTask LogInfoAsync(string text, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchService.cs b/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchService.cs index 30c959949d02e..5f1404a7ac876 100644 --- a/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchService.cs +++ b/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchService.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -27,7 +28,7 @@ internal interface ISymbolSearchService : IWorkspaceService /// Implementations should return results in order from best to worst (from their /// perspective). /// - Task> FindPackagesWithTypeAsync( + ValueTask> FindPackagesWithTypeAsync( string source, string name, int arity, CancellationToken cancellationToken); /// @@ -38,7 +39,7 @@ Task> FindPackagesWithTypeAsync( /// Implementations should return results in order from best to worst (from their /// perspective). /// - Task> FindPackagesWithAssemblyAsync( + ValueTask> FindPackagesWithAssemblyAsync( string source, string assemblyName, CancellationToken cancellationToken); /// @@ -50,14 +51,18 @@ Task> FindPackagesWithAssemblyAsync( /// Implementations should return results in order from best to worst (from their /// perspective). /// - Task> FindReferenceAssembliesWithTypeAsync( + ValueTask> FindReferenceAssembliesWithTypeAsync( string name, int arity, CancellationToken cancellationToken); } + [DataContract] internal abstract class PackageResult { + [DataMember(Order = 0)] public readonly string PackageName; - internal readonly int Rank; + + [DataMember(Order = 1)] + public readonly int Rank; protected PackageResult(string packageName, int rank) { @@ -66,37 +71,45 @@ protected PackageResult(string packageName, int rank) } } - internal class PackageWithTypeResult : PackageResult + [DataContract] + internal sealed class PackageWithTypeResult : PackageResult { - public readonly IList ContainingNamespaceNames; + [DataMember(Order = 2)] public readonly string TypeName; + + [DataMember(Order = 3)] public readonly string? Version; + [DataMember(Order = 4)] + public readonly ImmutableArray ContainingNamespaceNames; + public PackageWithTypeResult( string packageName, - string typeName, - string version, int rank, - IList containingNamespaceNames) + string typeName, + string? version, + ImmutableArray containingNamespaceNames) : base(packageName, rank) { TypeName = typeName; - Version = string.IsNullOrWhiteSpace(version) ? null : version; + Version = version; ContainingNamespaceNames = containingNamespaceNames; } } - internal class PackageWithAssemblyResult : PackageResult, IEquatable, IComparable + [DataContract] + internal sealed class PackageWithAssemblyResult : PackageResult, IEquatable, IComparable { + [DataMember(Order = 2)] public readonly string? Version; public PackageWithAssemblyResult( string packageName, - string version, - int rank) + int rank, + string version) : base(packageName, rank) { - Version = string.IsNullOrWhiteSpace(version) ? null : version; + Version = version; } public override int GetHashCode() @@ -120,16 +133,22 @@ public int CompareTo(PackageWithAssemblyResult? other) ImmutableArray.Create>(p => p.Rank, p => p.PackageName); } - internal class ReferenceAssemblyWithTypeResult + [DataContract] + internal sealed class ReferenceAssemblyWithTypeResult { - public readonly IList ContainingNamespaceNames; + [DataMember(Order = 0)] public readonly string AssemblyName; + + [DataMember(Order = 1)] public readonly string TypeName; + [DataMember(Order = 2)] + public readonly ImmutableArray ContainingNamespaceNames; + public ReferenceAssemblyWithTypeResult( string assemblyName, string typeName, - IList containingNamespaceNames) + ImmutableArray containingNamespaceNames) { AssemblyName = assemblyName; TypeName = typeName; @@ -146,22 +165,13 @@ public DefaultSymbolSearchService() { } - public Task> FindPackagesWithTypeAsync( - string source, string name, int arity, CancellationToken cancellationToken) - { - return SpecializedTasks.EmptyList(); - } + public ValueTask> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken) + => new(ImmutableArray.Empty); - public Task> FindPackagesWithAssemblyAsync( - string source, string assemblyName, CancellationToken cancellationToken) - { - return SpecializedTasks.EmptyList(); - } + public ValueTask> FindPackagesWithAssemblyAsync(string source, string assemblyName, CancellationToken cancellationToken) + => new(ImmutableArray.Empty); - public Task> FindReferenceAssembliesWithTypeAsync( - string name, int arity, CancellationToken cancellationToken) - { - return SpecializedTasks.EmptyList(); - } + public ValueTask> FindReferenceAssembliesWithTypeAsync(string name, int arity, CancellationToken cancellationToken) + => new(ImmutableArray.Empty); } } diff --git a/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchUpdateEngine.cs b/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchUpdateEngine.cs index 4d7bf2db34729..e7891ed6388ef 100644 --- a/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchUpdateEngine.cs +++ b/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchUpdateEngine.cs @@ -14,13 +14,13 @@ namespace Microsoft.CodeAnalysis.SymbolSearch /// internal interface ISymbolSearchUpdateEngine { - Task UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory); + ValueTask UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory, CancellationToken cancellationToken); - Task> FindPackagesWithTypeAsync( + ValueTask> FindPackagesWithTypeAsync( string source, string name, int arity, CancellationToken cancellationToken); - Task> FindPackagesWithAssemblyAsync( + ValueTask> FindPackagesWithAssemblyAsync( string source, string assemblyName, CancellationToken cancellationToken); - Task> FindReferenceAssembliesWithTypeAsync( + ValueTask> FindReferenceAssembliesWithTypeAsync( string name, int arity, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/TodoComments/IRemoteTodoCommentsService.cs b/src/Workspaces/Core/Portable/TodoComments/IRemoteTodoCommentsService.cs index 286a07b2b063c..cf26b2e2c049d 100644 --- a/src/Workspaces/Core/Portable/TodoComments/IRemoteTodoCommentsService.cs +++ b/src/Workspaces/Core/Portable/TodoComments/IRemoteTodoCommentsService.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.TodoComments /// Interface to allow host (VS) to inform the OOP service to start incrementally analyzing and /// reporting results back to the host. /// - internal interface IRemoteTodoCommentsService + internal interface IRemoteTodoCommentsDiscoveryService { ValueTask ComputeTodoCommentsAsync(CancellationToken cancellation); } diff --git a/src/Workspaces/Core/Portable/TodoComments/TodoCommentData.cs b/src/Workspaces/Core/Portable/TodoComments/TodoCommentData.cs index afc80298a0588..2676e24bba86a 100644 --- a/src/Workspaces/Core/Portable/TodoComments/TodoCommentData.cs +++ b/src/Workspaces/Core/Portable/TodoComments/TodoCommentData.cs @@ -5,6 +5,7 @@ #nullable enable using System; +using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -13,17 +14,48 @@ namespace Microsoft.CodeAnalysis.TodoComments /// /// Serialization type used to pass information to/from OOP and VS. /// - internal struct TodoCommentData : IEquatable + [DataContract] + internal readonly struct TodoCommentData : IEquatable { - public int Priority; - public string Message; - public DocumentId DocumentId; - public string? MappedFilePath; - public string? OriginalFilePath; - public int MappedLine; - public int MappedColumn; - public int OriginalLine; - public int OriginalColumn; + [DataMember(Order = 0)] + public readonly int Priority; + + [DataMember(Order = 1)] + public readonly string Message; + + [DataMember(Order = 2)] + public readonly DocumentId DocumentId; + + [DataMember(Order = 3)] + public readonly string? MappedFilePath; + + [DataMember(Order = 4)] + public readonly string? OriginalFilePath; + + [DataMember(Order = 5)] + public readonly int MappedLine; + + [DataMember(Order = 6)] + public readonly int MappedColumn; + + [DataMember(Order = 7)] + public readonly int OriginalLine; + + [DataMember(Order = 8)] + public readonly int OriginalColumn; + + public TodoCommentData(int priority, string message, DocumentId documentId, string? mappedFilePath, string? originalFilePath, int mappedLine, int mappedColumn, int originalLine, int originalColumn) + { + Priority = priority; + Message = message; + DocumentId = documentId; + MappedFilePath = mappedFilePath; + OriginalFilePath = originalFilePath; + MappedLine = mappedLine; + MappedColumn = mappedColumn; + OriginalLine = originalLine; + OriginalColumn = originalColumn; + } public override bool Equals(object? obj) => obj is TodoCommentData other && Equals(other); @@ -32,16 +64,14 @@ public override int GetHashCode() => GetHashCode(this); public override string ToString() - => $"{Priority} {Message} {MappedFilePath ?? ""} ({MappedLine.ToString()}, {MappedColumn.ToString()}) [original: {OriginalFilePath ?? ""} ({OriginalLine.ToString()}, {OriginalColumn.ToString()})"; + => $"{Priority} {Message} {MappedFilePath ?? ""} ({MappedLine}, {MappedColumn}) [original: {OriginalFilePath ?? ""} ({OriginalLine}, {OriginalColumn})"; public bool Equals(TodoCommentData right) - { - return DocumentId == right.DocumentId && - Priority == right.Priority && - Message == right.Message && - OriginalLine == right.OriginalLine && - OriginalColumn == right.OriginalColumn; - } + => DocumentId == right.DocumentId && + Priority == right.Priority && + Message == right.Message && + OriginalLine == right.OriginalLine && + OriginalColumn == right.OriginalColumn; public static int GetHashCode(TodoCommentData item) => Hash.Combine(item.DocumentId, @@ -64,19 +94,14 @@ internal void WriteTo(ObjectWriter writer) } internal static TodoCommentData ReadFrom(ObjectReader reader) - { - return new TodoCommentData - { - Priority = reader.ReadInt32(), - Message = reader.ReadString(), - DocumentId = DocumentId.ReadFrom(reader), - MappedFilePath = reader.ReadString(), - OriginalFilePath = reader.ReadString(), - MappedLine = reader.ReadInt32(), - MappedColumn = reader.ReadInt32(), - OriginalLine = reader.ReadInt32(), - OriginalColumn = reader.ReadInt32(), - }; - } + => new(priority: reader.ReadInt32(), + message: reader.ReadString(), + documentId: DocumentId.ReadFrom(reader), + mappedFilePath: reader.ReadString(), + originalFilePath: reader.ReadString(), + mappedLine: reader.ReadInt32(), + mappedColumn: reader.ReadInt32(), + originalLine: reader.ReadInt32(), + originalColumn: reader.ReadInt32()); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/DocumentKey.cs b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/DocumentKey.cs index ead29c303e77d..b7a29f8c918ec 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/DocumentKey.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/DocumentKey.cs @@ -2,6 +2,7 @@ // 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.Runtime.Serialization; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.PersistentStorage @@ -29,26 +30,34 @@ public DocumentKey(ProjectKey project, DocumentId id, string filePath, string na } public static explicit operator DocumentKey(Document document) - => new DocumentKey((ProjectKey)document.Project, document.Id, document.FilePath, document.Name); + => new((ProjectKey)document.Project, document.Id, document.FilePath, document.Name); public SerializableDocumentKey Dehydrate() - { - return new SerializableDocumentKey - { - Project = Project.Dehydrate(), - Id = Id, - FilePath = FilePath, - Name = Name, - }; - } + => new(Project.Dehydrate(), Id, FilePath, Name); } - internal class SerializableDocumentKey + [DataContract] + internal readonly struct SerializableDocumentKey { - public SerializableProjectKey Project; - public DocumentId Id; - public string FilePath; - public string Name; + [DataMember(Order = 0)] + public readonly SerializableProjectKey Project; + + [DataMember(Order = 1)] + public readonly DocumentId Id; + + [DataMember(Order = 2)] + public readonly string FilePath; + + [DataMember(Order = 3)] + public readonly string Name; + + public SerializableDocumentKey(SerializableProjectKey project, DocumentId id, string filePath, string name) + { + Project = project; + Id = id; + FilePath = filePath; + Name = name; + } public DocumentKey Rehydrate() => new DocumentKey(Project.Rehydrate(), Id, FilePath, Name); diff --git a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/ProjectKey.cs b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/ProjectKey.cs index 680d1d244c257..5d81d2ad19f65 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/ProjectKey.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/ProjectKey.cs @@ -2,6 +2,7 @@ // 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.Runtime.Serialization; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.PersistentStorage @@ -29,28 +30,36 @@ public ProjectKey(SolutionKey solution, ProjectId id, string filePath, string na } public static explicit operator ProjectKey(Project project) - => new ProjectKey((SolutionKey)project.Solution, project.Id, project.FilePath, project.Name); + => new((SolutionKey)project.Solution, project.Id, project.FilePath, project.Name); public SerializableProjectKey Dehydrate() - { - return new SerializableProjectKey - { - Solution = Solution.Dehydrate(), - Id = Id, - FilePath = FilePath, - Name = Name, - }; - } + => new(Solution.Dehydrate(), Id, FilePath, Name); } - internal class SerializableProjectKey + [DataContract] + internal readonly struct SerializableProjectKey { - public SerializableSolutionKey Solution; - public ProjectId Id; - public string FilePath; - public string Name; + [DataMember(Order = 0)] + public readonly SerializableSolutionKey Solution; + + [DataMember(Order = 1)] + public readonly ProjectId Id; + + [DataMember(Order = 2)] + public readonly string FilePath; + + [DataMember(Order = 3)] + public readonly string Name; + + public SerializableProjectKey(SerializableSolutionKey solution, ProjectId id, string filePath, string name) + { + Solution = solution; + Id = id; + FilePath = filePath; + Name = name; + } public ProjectKey Rehydrate() - => new ProjectKey(Solution.Rehydrate(), Id, FilePath, Name); + => new(Solution.Rehydrate(), Id, FilePath, Name); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/SolutionKey.cs b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/SolutionKey.cs index f2569e7ac0eb1..9bd8ba8b09c10 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/SolutionKey.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/SolutionKey.cs @@ -2,6 +2,7 @@ // 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.Runtime.Serialization; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.PersistentStorage @@ -26,26 +27,32 @@ public SolutionKey(SolutionId id, string filePath, bool isPrimaryBranch) } public static explicit operator SolutionKey(Solution solution) - => new SolutionKey(solution.Id, solution.FilePath, solution.BranchId == solution.Workspace.PrimaryBranchId); + => new(solution.Id, solution.FilePath, solution.BranchId == solution.Workspace.PrimaryBranchId); public SerializableSolutionKey Dehydrate() - { - return new SerializableSolutionKey - { - Id = Id, - FilePath = FilePath, - IsPrimaryBranch = IsPrimaryBranch, - }; - } + => new(Id, FilePath, IsPrimaryBranch); } - internal class SerializableSolutionKey + [DataContract] + internal readonly struct SerializableSolutionKey { - public SolutionId Id; - public string FilePath; - public bool IsPrimaryBranch; + [DataMember(Order = 0)] + public readonly SolutionId Id; + + [DataMember(Order = 1)] + public readonly string FilePath; + + [DataMember(Order = 2)] + public readonly bool IsPrimaryBranch; + + public SerializableSolutionKey(SolutionId id, string filePath, bool isPrimaryBranch) + { + Id = id; + FilePath = filePath; + IsPrimaryBranch = isPrimaryBranch; + } public SolutionKey Rehydrate() - => new SolutionKey(Id, FilePath, IsPrimaryBranch); + => new(Id, FilePath, IsPrimaryBranch); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum.cs index b81b7662ee16d..0f5d07ac7aa2f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Linq; using System.Runtime.InteropServices; +using System.Runtime.Serialization; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -15,6 +16,7 @@ namespace Microsoft.CodeAnalysis /// Checksum of data can be used later to see whether two data are same or not /// without actually comparing data itself /// + [DataContract] internal sealed partial class Checksum : IObjectWritable, IEquatable { /// @@ -24,8 +26,12 @@ internal sealed partial class Checksum : IObjectWritable, IEquatable public static readonly Checksum Null = new Checksum(default); + [DataMember(Order = 0)] private readonly HashData _checksum; + public Checksum(HashData hash) + => _checksum = hash; + /// /// Create Checksum from given byte array. if byte array is bigger than /// , it will be truncated to the size @@ -95,9 +101,6 @@ private static unsafe Checksum FromWorker(byte[] checksum) } } - private Checksum(HashData hash) - => _checksum = hash; - public bool Equals(Checksum other) { if (other == null) @@ -149,17 +152,25 @@ public static string GetChecksumsLogInfo(IEnumerable checksums) /// This structure stores the 20-byte hash as an inline value rather than requiring the use of /// byte[]. /// + [DataContract] [StructLayout(LayoutKind.Explicit, Size = HashSize)] - private struct HashData : IEquatable + public readonly struct HashData : IEquatable { - [FieldOffset(0)] - private long Data1; + [FieldOffset(0), DataMember(Order = 0)] + private readonly long Data1; - [FieldOffset(8)] - private long Data2; + [FieldOffset(8), DataMember(Order = 1)] + private readonly long Data2; - [FieldOffset(16)] - private int Data3; + [FieldOffset(16), DataMember(Order = 2)] + private readonly int Data3; + + public HashData(long data1, long data2, int data3) + { + Data1 = data1; + Data2 = data2; + Data3 = data3; + } public static bool operator ==(HashData x, HashData y) => x.Equals(y); @@ -175,22 +186,10 @@ public void WriteTo(ObjectWriter writer) } public static unsafe HashData FromPointer(HashData* hash) - { - HashData result = default; - result.Data1 = hash->Data1; - result.Data2 = hash->Data2; - result.Data3 = hash->Data3; - return result; - } + => new(hash->Data1, hash->Data2, hash->Data3); public static HashData ReadFrom(ObjectReader reader) - { - HashData result = default; - result.Data1 = reader.ReadInt64(); - result.Data2 = reader.ReadInt64(); - result.Data3 = reader.ReadInt32(); - return result; - } + => new(reader.ReadInt64(), reader.ReadInt64(), reader.ReadInt32()); public override int GetHashCode() { diff --git a/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs b/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs new file mode 100644 index 0000000000000..15df12c257f50 --- /dev/null +++ b/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs @@ -0,0 +1,162 @@ +// 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.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using MessagePack; +using Roslyn.Test.Utilities; +using Roslyn.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Remote.UnitTests +{ + public class ServiceDescriptorTests + { + private static Dictionary GetAllParameterTypesOfRemoteApis() + { + var interfaces = new List(); + + foreach (var (serviceType, (descriptor, _)) in ServiceDescriptors.Descriptors) + { + interfaces.Add(serviceType); + if (descriptor.ClientInterface != null) + { + interfaces.Add(descriptor.ClientInterface); + } + } + + var types = new Dictionary(); + + void AddTypeRecursive(Type type, MemberInfo declaringMember) + { + if (type.IsArray) + { + type = type.GetElementType(); + } + + if (types.ContainsKey(type)) + { + return; + } + + types.Add(type, declaringMember); + + if (type.IsGenericType) + { + // Immutable collections and tuples have custom formatters which would fail during serialization if + // formatters were not available for the element types. + if (type.Namespace == typeof(ImmutableArray<>).Namespace || + type.GetGenericTypeDefinition() == typeof(Nullable<>) || + type.Namespace == "System" && type.Name.StartsWith("ValueTuple", StringComparison.Ordinal) || + type.Namespace == "System" && type.Name.StartsWith("Tuple", StringComparison.Ordinal)) + { + foreach (var genericArgument in type.GetGenericArguments()) + { + AddTypeRecursive(genericArgument, declaringMember); + } + } + } + + foreach (var field in type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) + { + if (field.GetCustomAttributes().Any()) + { + AddTypeRecursive(field.FieldType, type); + } + } + + foreach (var property in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) + { + if (property.GetCustomAttributes().Any()) + { + AddTypeRecursive(property.PropertyType, type); + } + } + } + + foreach (var interfaceType in interfaces) + { + foreach (var method in interfaceType.GetMethods()) + { + if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>)) + { + AddTypeRecursive(method.ReturnType.GetGenericArguments().Single(), method); + } + else + { + // remote API must return ValueTask or ValueTask + Assert.Equal(typeof(ValueTask), method.ReturnType); + } + + foreach (var type in method.GetParameters().Select(p => p.ParameterType)) + { + // stream is special cased by JSON-RPC for streaming APIs + if (type != typeof(Stream)) + { + AddTypeRecursive(type, method); + } + } + } + } + + types.Remove(typeof(CancellationToken)); + + return types; + } + + [Fact] + public void TypesUsedInRemoteApisMustBeMessagePackSerializable() + { + var types = GetAllParameterTypesOfRemoteApis(); + var resolver = ServiceDescriptor.TestAccessor.Options.Resolver; + + var errors = new List(); + + foreach (var (type, declaringMember) in types) + { + try + { + if (resolver.GetFormatterDynamic(type) == null) + { + errors.Add($"{type} referenced by {declaringMember} is not serializable"); + } + } + catch (Exception e) + { + // Known issues: + // Internal enums need a custom formatter: https://github.com/neuecc/MessagePack-CSharp/issues/1025 + // This test fails with "... is attempting to implement an inaccessible interface." error message. + if (type.IsEnum && type.IsNotPublic || + type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && + type.GetGenericArguments().Single().IsEnum && type.GetGenericArguments().Single().IsNotPublic) + { + errors.Add($"{type} referenced by {declaringMember} is an internal enum and needs a custom formatter"); + } + else + { + errors.Add($"{type} referenced by {declaringMember} failed to serialize with exception: {e}"); + } + } + } + + AssertEx.Empty(errors, "Types are not MessagePack-serializable"); + } + + [Fact] + public void GetFeatureName() + { + foreach (var (serviceType, _) in ServiceDescriptors.Descriptors) + { + Assert.NotEmpty(ServiceDescriptors.GetFeatureName(serviceType)); + } + } + } +} diff --git a/src/Workspaces/CoreTestUtilities/MEF/FeaturesTestCompositions.cs b/src/Workspaces/CoreTestUtilities/MEF/FeaturesTestCompositions.cs index aaca36d050a8f..d0996fd7511fc 100644 --- a/src/Workspaces/CoreTestUtilities/MEF/FeaturesTestCompositions.cs +++ b/src/Workspaces/CoreTestUtilities/MEF/FeaturesTestCompositions.cs @@ -15,7 +15,9 @@ public static class FeaturesTestCompositions { public static readonly TestComposition Features = TestComposition.Empty .AddAssemblies(MefHostServices.DefaultAssemblies) - .AddParts(typeof(MockWorkspaceEventListenerProvider)); // by default, avoid running Solution Crawler and other services that start in workspace event listeners + .AddParts( + typeof(MockWorkspaceEventListenerProvider), // by default, avoid running Solution Crawler and other services that start in workspace event listeners + typeof(TestErrorReportingService)); // mocks the info-bar error reporting public static readonly TestComposition RemoteHost = TestComposition.Empty .AddAssemblies(RemoteWorkspaceManager.RemoteHostAssemblies); diff --git a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs index b263854dc6324..f22a39812ee6d 100644 --- a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs +++ b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs @@ -14,6 +14,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Experiments; +using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.TodoComments; @@ -120,7 +121,7 @@ public override async ValueTask> CreateConnectionAsyn #pragma warning restore Contract.ThrowIfNull(proxy); - return new BrokeredServiceConnection(proxy, assetStorage, errorReportingService: null); + return new BrokeredServiceConnection(proxy, assetStorage, _workspaceServices.GetRequiredService(), shutdownCancellationService: null); } public override async Task CreateConnectionAsync(RemoteServiceName serviceName, object? callbackTarget, CancellationToken cancellationToken) @@ -234,15 +235,26 @@ public InProcRemoteServices(HostWorkspaceServices workspaceServices, RemoteHostT ServiceBroker = new InProcServiceBroker(this); RegisterService(WellKnownServiceHubService.RemoteHost, (s, p, o) => new RemoteHostService(s, p)); - RegisterService(WellKnownServiceHubService.CodeAnalysis, (s, p, o) => new CodeAnalysisService(s, p)); - RegisterService(WellKnownServiceHubService.RemoteSymbolSearchUpdateEngine, (s, p, o) => new RemoteSymbolSearchUpdateEngine(s, p)); RegisterInProcBrokeredService(SolutionAssetProvider.ServiceDescriptor, () => new SolutionAssetProvider(workspaceServices)); - RegisterRemoteBrokeredService(new RemoteDesignerAttributeService.Factory()); + RegisterRemoteBrokeredService(new RemoteSymbolSearchUpdateService.Factory()); + RegisterRemoteBrokeredService(new RemoteDesignerAttributeDiscoveryService.Factory()); RegisterRemoteBrokeredService(new RemoteProjectTelemetryService.Factory()); - RegisterRemoteBrokeredService(new RemoteTodoCommentsService.Factory()); + RegisterRemoteBrokeredService(new RemoteTodoCommentsDiscoveryService.Factory()); RegisterRemoteBrokeredService(new RemoteDiagnosticAnalyzerService.Factory()); RegisterRemoteBrokeredService(new RemoteSemanticClassificationService.Factory()); RegisterRemoteBrokeredService(new RemoteSemanticClassificationCacheService.Factory()); + RegisterRemoteBrokeredService(new RemoteDocumentHighlightsService.Factory()); + RegisterRemoteBrokeredService(new RemoteEncapsulateFieldService.Factory()); + RegisterRemoteBrokeredService(new RemoteRenamerService.Factory()); + RegisterRemoteBrokeredService(new RemoteConvertTupleToStructCodeRefactoringService.Factory()); + RegisterRemoteBrokeredService(new RemoteFindUsagesService.Factory()); + RegisterRemoteBrokeredService(new RemoteSymbolFinderService.Factory()); + RegisterRemoteBrokeredService(new RemoteNavigateToSearchService.Factory()); + RegisterRemoteBrokeredService(new RemoteMissingImportDiscoveryService.Factory()); + RegisterRemoteBrokeredService(new RemoteExtensionMethodImportCompletionService.Factory()); + RegisterRemoteBrokeredService(new RemoteDependentTypeFinderService.Factory()); + RegisterRemoteBrokeredService(new RemoteGlobalNotificationDeliveryService.Factory()); + RegisterRemoteBrokeredService(new RemoteCodeLensReferencesService.Factory()); RegisterService(WellKnownServiceHubService.LanguageServer, (s, p, o) => new LanguageServer(s, p)); } @@ -280,7 +292,7 @@ public object CreateBrokeredService(ServiceRpcDescriptor descriptor, IDuplexPipe // Currently don't support callback creation as we don't have in-proc service with callbacks yet. Contract.ThrowIfFalse(descriptor.ClientInterface == null); - var serviceConnection = descriptor.ConstructRpcConnection(pipe); + var serviceConnection = descriptor.WithTraceSource(_serviceProvider.TraceSource).ConstructRpcConnection(pipe); var service = inProcFactory(); serviceConnection.AddLocalRpcTarget(service); diff --git a/src/Workspaces/CoreTestUtilities/TestErrorReportingService.cs b/src/Workspaces/CoreTestUtilities/TestErrorReportingService.cs new file mode 100644 index 0000000000000..9b111d2befb25 --- /dev/null +++ b/src/Workspaces/CoreTestUtilities/TestErrorReportingService.cs @@ -0,0 +1,44 @@ +// 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 enable + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.Extensions; +using Microsoft.CodeAnalysis.Host.Mef; +using Xunit; + +namespace Microsoft.CodeAnalysis.Test.Utilities +{ + [ExportWorkspaceService(typeof(IErrorReportingService), ServiceLayer.Test), Shared] + internal sealed class TestErrorReportingService : IErrorReportingService + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public TestErrorReportingService() + { + } + + public Action OnError { get; set; } = message => Assert.False(true, message); + + public string HostDisplayName + => "Test Host"; + + public void ShowDetailedErrorInfo(Exception exception) + => OnError(exception.Message); + + public void ShowErrorInfoInActiveView(string message, params InfoBarUI[] items) + => OnError(message); + + public void ShowGlobalErrorInfo(string message, params InfoBarUI[] items) + => OnError(message); + + public void ShowRemoteHostCrashedErrorInfo(Exception? exception) + => OnError(exception?.Message ?? "Unexpected error"); + + public void ShowFeatureNotAvailableErrorInfo(string message, Exception? exception) + => OnError($"{message} {exception}"); + } +} diff --git a/src/Workspaces/Remote/Core/BrokeredServiceConnection.cs b/src/Workspaces/Remote/Core/BrokeredServiceConnection.cs index 98ab8b399cc9d..15accfa6d8154 100644 --- a/src/Workspaces/Remote/Core/BrokeredServiceConnection.cs +++ b/src/Workspaces/Remote/Core/BrokeredServiceConnection.cs @@ -2,26 +2,40 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.IO; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using MessagePack; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Extensions; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Internal.Log; using Nerdbank.Streams; +using StreamJsonRpc; +using StreamJsonRpc.Protocol; namespace Microsoft.CodeAnalysis.Remote { internal sealed class BrokeredServiceConnection : RemoteServiceConnection where TService : class { - private readonly IErrorReportingService _errorReportingService; + private readonly IErrorReportingService? _errorReportingService; + private readonly IRemoteHostClientShutdownCancellationService? _shutdownCancellationService; private readonly SolutionAssetStorage _solutionAssetStorage; private readonly TService _service; - public BrokeredServiceConnection(TService service, SolutionAssetStorage solutionAssetStorage, IErrorReportingService errorReportingService) + public BrokeredServiceConnection( + TService service, + SolutionAssetStorage solutionAssetStorage, + IErrorReportingService? errorReportingService, + IRemoteHostClientShutdownCancellationService? shutdownCancellationService) { _errorReportingService = errorReportingService; + _shutdownCancellationService = shutdownCancellationService; _solutionAssetStorage = solutionAssetStorage; _service = service; } @@ -38,7 +52,7 @@ public override async ValueTask TryInvokeAsync(Func> TryInvokeAsync(Func< { return await invocation(_service, cancellationToken).ConfigureAwait(false); } - catch (Exception exception) when (FatalError.ReportWithoutCrashUnlessCanceled(exception, cancellationToken)) + catch (Exception exception) when (ReportUnexpectedException(exception, cancellationToken)) { OnUnexpectedException(exception, cancellationToken); return default; @@ -67,7 +81,7 @@ public override async ValueTask> TryInvokeAsync( { return await InvokeStreamingServiceAsync(_service, invocation, reader, cancellationToken).ConfigureAwait(false); } - catch (Exception exception) when (FatalError.ReportWithoutCrashUnlessCanceled(exception, cancellationToken)) + catch (Exception exception) when (ReportUnexpectedException(exception, cancellationToken)) { OnUnexpectedException(exception, cancellationToken); return default; @@ -84,7 +98,7 @@ public override async ValueTask TryInvokeAsync(Solution solution, Func> TryInvokeAsync(Solut using var scope = await _solutionAssetStorage.StoreAssetsAsync(solution, cancellationToken).ConfigureAwait(false); return await invocation(_service, scope.SolutionInfo, cancellationToken).ConfigureAwait(false); } - catch (Exception exception) when (FatalError.ReportWithoutCrashUnlessCanceled(exception, cancellationToken)) + catch (Exception exception) when (ReportUnexpectedException(exception, cancellationToken)) { OnUnexpectedException(exception, cancellationToken); return default; @@ -120,7 +134,7 @@ public override async ValueTask> TryInvokeAsync( reader, cancellationToken).ConfigureAwait(false); } - catch (Exception exception) when (FatalError.ReportWithoutCrashUnlessCanceled(exception, cancellationToken)) + catch (Exception exception) when (ReportUnexpectedException(exception, cancellationToken)) { OnUnexpectedException(exception, cancellationToken); return default; @@ -147,18 +161,70 @@ internal static async ValueTask InvokeStreamingServiceAsync( return readerTask.Result; } + private bool ReportUnexpectedException(Exception exception, CancellationToken cancellationToken) + { + // Do not report telemetry when the host is shutting down or the remote service threw an IO exception: + if (IsHostShuttingDown || IsRemoteIOException(exception)) + { + return true; + } + + // report telemetry event: + Logger.Log(FunctionId.FeatureNotAvailable, $"{ServiceDescriptors.GetServiceName(typeof(TService))}: {exception.GetType()}: {exception.Message}"); + + return FatalError.ReportWithoutCrashUnlessCanceled(exception, cancellationToken); + } + + private bool IsHostShuttingDown + => _shutdownCancellationService?.ShutdownToken.IsCancellationRequested == true; + + // TODO: we need https://github.com/microsoft/vs-streamjsonrpc/issues/468 to be implemented in order to check for IOException subtypes. + private static bool IsRemoteIOException(Exception exception) + => exception is RemoteInvocationException { ErrorData: CommonErrorData { TypeName: "System.IO.IOException" } }; + private void OnUnexpectedException(Exception exception, CancellationToken cancellationToken) { - // Remote call may fail with different exception even when our cancellation token is signaled - // (e.g. on shutdown if the connection is dropped): + // If the cancellation token passed to the remote call is not linked with the host shutdown cancellation token, + // various non-cancellation exceptions may occur during the remote call. + // Throw cancellation exception if the cancellation token is signaled. + // If it is not then show info to the user that the service is not available dure to shutdown. cancellationToken.ThrowIfCancellationRequested(); - // TODO: better message depending on the exception (https://github.com/dotnet/roslyn/issues/40476): - // "Feature xyz is currently unavailable due to network issues" (connection exceptions) - // "Feature xyz is currently unavailable due to an internal error [Details]" (exception is RemoteInvocationException, serialization issues) - // "Feature xyz is currently unavailable" (connection exceptions during shutdown cancellation when cancellationToken is not signalled) + if (_errorReportingService == null) + { + return; + } + + // Show the error on the client. See https://github.com/dotnet/roslyn/issues/40476 for error classification details. + // Based on the exception type and the state of the system we report one of the following: + // - "Feature xyz is currently unavailable due to an intermittent error. Please try again later. Error message: '{1}'" (RemoteInvocationException: IOException) + // - "Feature xyz is currently unavailable due to an internal error [Details]" (exception is RemoteInvocationException, MessagePackSerializationException, ConnectionLostException) + // - "Feature xyz is currently unavailable since Visual Studio is shutting down" (connection exceptions during shutdown cancellation when cancellationToken is not signalled) + + // We expect all RPC calls to complete and not drop the connection. + // ConnectionLostException indicates a bug that is likely thrown because the remote process crashed. + // Currently, ConnectionLostException is also throw when the result of the RPC method fails to serialize + // (see https://github.com/microsoft/vs-streamjsonrpc/issues/549) + + string message; + Exception? internalException = null; + var featureName = ServiceDescriptors.GetFeatureName(typeof(TService)); + + if (IsRemoteIOException(exception)) + { + message = string.Format(RemoteWorkspacesResources.Feature_0_is_currently_unavailable_due_to_an_intermittent_error, featureName, exception.Message); + } + else if (IsHostShuttingDown) + { + message = string.Format(RemoteWorkspacesResources.Feature_0_is_currently_unavailable_host_shutting_down, featureName, _errorReportingService.HostDisplayName); + } + else + { + message = string.Format(RemoteWorkspacesResources.Feature_0_is_currently_unavailable_due_to_an_internal_error, featureName); + internalException = exception; + } - _errorReportingService?.ShowRemoteHostCrashedErrorInfo(exception); + _errorReportingService.ShowFeatureNotAvailableErrorInfo(message, internalException); } } } diff --git a/src/Workspaces/Remote/Core/GlobalNotificationRemoteDeliveryService.cs b/src/Workspaces/Remote/Core/GlobalNotificationRemoteDeliveryService.cs index a12b667038805..b31e6fe36cce4 100644 --- a/src/Workspaces/Remote/Core/GlobalNotificationRemoteDeliveryService.cs +++ b/src/Workspaces/Remote/Core/GlobalNotificationRemoteDeliveryService.cs @@ -94,11 +94,8 @@ private async Task SendStartNotificationAsync(Task(), + _ = await client.TryInvokeAsync( + (service, cancellationToken) => service.OnGlobalOperationStartedAsync(cancellationToken), callbackTarget: null, _cancellationToken).ConfigureAwait(false); @@ -129,11 +126,8 @@ private async Task SendStoppedNotificationAsync(Task( + (service, cancellationToken) => service.OnGlobalOperationStoppedAsync(e.Operations, e.Cancelled, cancellationToken), callbackTarget: null, _cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Remote/Core/IRemoteGlobalNotificationDeliveryService.cs b/src/Workspaces/Remote/Core/IRemoteGlobalNotificationDeliveryService.cs index 33932ee12bb12..9fd47a3e1c8e4 100644 --- a/src/Workspaces/Remote/Core/IRemoteGlobalNotificationDeliveryService.cs +++ b/src/Workspaces/Remote/Core/IRemoteGlobalNotificationDeliveryService.cs @@ -5,13 +5,15 @@ #nullable enable using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.Remote { internal interface IRemoteGlobalNotificationDeliveryService { - void OnGlobalOperationStarted(); + ValueTask OnGlobalOperationStartedAsync(CancellationToken cancellationToken); - void OnGlobalOperationStopped(IReadOnlyList operations, bool cancelled); + ValueTask OnGlobalOperationStoppedAsync(IReadOnlyList operations, bool cancelled, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Remote/Core/Microsoft.CodeAnalysis.Remote.Workspaces.csproj b/src/Workspaces/Remote/Core/Microsoft.CodeAnalysis.Remote.Workspaces.csproj index 969e173ca909f..4bc5b43b3c16f 100644 --- a/src/Workspaces/Remote/Core/Microsoft.CodeAnalysis.Remote.Workspaces.csproj +++ b/src/Workspaces/Remote/Core/Microsoft.CodeAnalysis.Remote.Workspaces.csproj @@ -34,6 +34,9 @@ + + + diff --git a/src/Workspaces/Remote/Core/RemoteCallback.cs b/src/Workspaces/Remote/Core/RemoteCallback.cs index 93a1569025f8b..941efd4404637 100644 --- a/src/Workspaces/Remote/Core/RemoteCallback.cs +++ b/src/Workspaces/Remote/Core/RemoteCallback.cs @@ -8,6 +8,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using MessagePack; using Microsoft.CodeAnalysis.ErrorReporting; using Nerdbank.Streams; using Newtonsoft.Json; @@ -78,45 +79,55 @@ public async ValueTask InvokeAsync( } } - // TODO: https://github.com/microsoft/vs-streamjsonrpc/issues/246 + // Remote calls can only throw 4 types of exceptions that correspond to // - // We need to get to a state when remote calls can only throw 4 types of exceptions that correspond to // 1) Connection issue (connection dropped for any reason) // 2) Serialization issue - bug in serialization of arguments (types are not serializable, etc.) // 3) Remote exception - an exception was thrown by the callee // 4) Cancelation - // When a connection is dropped and CancelLocallyInvokedMethodsWhenConnectionIsClosed is set the connection dropped exception [1] should not be thrown. - // Instead a the cancellation token should be signaled and OperationCancelledException should be thrown ([4]). // - // Until the above issue in JSON-RPC is fixed we do a best guess on what the issue is. - private bool ReportUnexpectedException(Exception exception, CancellationToken cancellationToken) { - if (exception is RemoteInvocationException or JsonException) + if (exception is IOException) { - // indicates bug on client side or in serialization, propagate the exception - return FatalError.ReportWithoutCrashAndPropagate(exception); + // propagate intermittent exceptions without reporting telemetry: + return false; } - if (cancellationToken.IsCancellationRequested) + if (exception is OperationCanceledException) { - // If cancelation is requested and we see a different exception the handler will throw OperationCancelledException. - return exception is not OperationCanceledException; + if (cancellationToken.IsCancellationRequested) + { + return false; + } + + // It is not guaranteed that RPC only throws OCE when our token is signaled. + // Signal the cancelation source that our token is linked to and throw new cancellation + // exception in OnUnexpectedException. + ClientDisconnectedSource.Cancel(); + + return true; } - // We assume that any other exception indicates lost connection (it might not), - // cancel any ongoing work since the client can't receive the results. - // This should be handled by JSON-RPC but it's not guaranteed due to https://github.com/microsoft/vs-streamjsonrpc/issues/246. - ClientDisconnectedSource.Cancel(); + // When a connection is dropped and CancelLocallyInvokedMethodsWhenConnectionIsClosed is + // set ConnectionLostException should not be thrown. Instead the cancellation token should be + // signaled and OperationCancelledException should be thrown. + // Seems to not work in all cases currently, so we need to cancel ourselves (bug https://github.com/microsoft/vs-streamjsonrpc/issues/551). + // Once this issue is fixed we can remov this if statement and fall back to reporting NFW + // as any observation of ConnectionLostException indicates a bug (e.g. https://github.com/microsoft/vs-streamjsonrpc/issues/549). + if (exception is ConnectionLostException) + { + ClientDisconnectedSource.Cancel(); + + return true; + } - // catch the exception, cancellation exception will be thrown by the handler. - return true; + // Indicates bug on client side or in serialization, report NFW and propagate the exception. + return FatalError.ReportWithoutCrashAndPropagate(exception); } private static Exception OnUnexpectedException(CancellationToken cancellationToken) { - // Remote call may fail with different exception even when our cancellation token is signaled - // (e.g. on shutdown if the connection is dropped): cancellationToken.ThrowIfCancellationRequested(); // If this is hit the cancellation token passed to the service implementation did not use the correct token. diff --git a/src/Workspaces/Remote/Core/RemoteWorkspacesResources.cs b/src/Workspaces/Remote/Core/RemoteWorkspacesResources.cs deleted file mode 100644 index fe0767a041a3d..0000000000000 --- a/src/Workspaces/Remote/Core/RemoteWorkspacesResources.cs +++ /dev/null @@ -1,13 +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. - -namespace Microsoft.CodeAnalysis.Remote -{ - /// - /// Stub type - replace with type generated from resx file when resources are needed in this assembly. - /// - internal static class RemoteWorkspacesResources - { - } -} diff --git a/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx b/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx new file mode 100644 index 0000000000000..a7055d04205cf --- /dev/null +++ b/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Feature '{0}' is currently unavailable since {1} is shutting down. + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + Feature '{0}' is currently unavailable due to an internal error. + + + Missing import discovery + + + CodeLens references + + + Convert tuple to struct refactoring + + + Dependent type finder + + + DesignerAttribute discovery + + + Diagnostic analyzer runner + + + Document higlights + + + Encapsulate field refactoring + + + Extension method import completion + + + Find usages + + + Global notification delivery + + + Navigate to + + + Project telemetry collection + + + Rename + + + Semantic classification + + + Semantic classification cache + + + Symbol finder + + + Symbol search + + + TODO comments discovery + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/Serialization/ImmutableCollectionMessagePackResolver.cs b/src/Workspaces/Remote/Core/Serialization/ImmutableCollectionMessagePackResolver.cs new file mode 100644 index 0000000000000..7389dc20c37e6 --- /dev/null +++ b/src/Workspaces/Remote/Core/Serialization/ImmutableCollectionMessagePackResolver.cs @@ -0,0 +1,394 @@ +// 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.Collections.Generic; +using System.Collections.Immutable; +using System.Reflection; +using MessagePack; +using MessagePack.Formatters; + +// TODO: Copied from https://github.com/neuecc/MessagePack-CSharp/blob/master/src/MessagePack.ImmutableCollection/Formatters.cs. +// Replace with an implementation shipping with VS: +// https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1198374 +// https://github.com/neuecc/MessagePack-CSharp/issues/606 +// +// The following code includes fix for: https://github.com/neuecc/MessagePack-CSharp/issues/1033. Make sure the fix is included in the replacement. + +namespace Microsoft.CodeAnalysis.Remote +{ + internal sealed class ImmutableCollectionMessagePackResolver : IFormatterResolver + { + public static readonly ImmutableCollectionMessagePackResolver Instance = new ImmutableCollectionMessagePackResolver(); + + private ImmutableCollectionMessagePackResolver() + { + } + + public IMessagePackFormatter GetFormatter() + => FormatterCache.Formatter; + + private static class FormatterCache + { + internal static readonly IMessagePackFormatter Formatter; + + static FormatterCache() + { + Formatter = (IMessagePackFormatter)GetFormatter(typeof(T)); + } + } + + private static readonly Dictionary s_formatterMap = new Dictionary() + { + { typeof(ImmutableArray<>), typeof(ImmutableArrayFormatter<>) }, + { typeof(ImmutableList<>), typeof(ImmutableListFormatter<>) }, + { typeof(ImmutableDictionary<,>), typeof(ImmutableDictionaryFormatter<,>) }, + { typeof(ImmutableHashSet<>), typeof(ImmutableHashSetFormatter<>) }, + { typeof(ImmutableSortedDictionary<,>), typeof(ImmutableSortedDictionaryFormatter<,>) }, + { typeof(ImmutableSortedSet<>), typeof(ImmutableSortedSetFormatter<>) }, + { typeof(ImmutableQueue<>), typeof(ImmutableQueueFormatter<>) }, + { typeof(ImmutableStack<>), typeof(ImmutableStackFormatter<>) }, + { typeof(IImmutableList<>), typeof(InterfaceImmutableListFormatter<>) }, + { typeof(IImmutableDictionary<,>), typeof(InterfaceImmutableDictionaryFormatter<,>) }, + { typeof(IImmutableQueue<>), typeof(InterfaceImmutableQueueFormatter<>) }, + { typeof(IImmutableSet<>), typeof(InterfaceImmutableSetFormatter<>) }, + { typeof(IImmutableStack<>), typeof(InterfaceImmutableStackFormatter<>) }, + }; + + internal static object GetFormatter(Type t) + { + var ti = t.GetTypeInfo(); + + if (ti.IsGenericType) + { + var genericType = ti.GetGenericTypeDefinition(); + var genericTypeInfo = genericType.GetTypeInfo(); + var isNullable = genericTypeInfo.IsGenericType && genericTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>); + var nullableElementType = isNullable ? ti.GenericTypeArguments[0] : null; + + if (s_formatterMap.TryGetValue(genericType, out var formatterType)) + { + return CreateInstance(formatterType, ti.GenericTypeArguments); + } + + if (isNullable && nullableElementType.IsConstructedGenericType && nullableElementType.GetGenericTypeDefinition() == typeof(ImmutableArray<>)) + { + return CreateInstance(typeof(NullableFormatter<>), new[] { nullableElementType }); + } + } + + return null; + } + + private static object CreateInstance(Type genericType, Type[] genericTypeArguments, params object[] arguments) + => Activator.CreateInstance(genericType.MakeGenericType(genericTypeArguments), arguments); + + // ImmutableArray.Enumerator is 'not' IEnumerator, can't use abstraction layer. + internal sealed class ImmutableArrayFormatter : IMessagePackFormatter> + { + public void Serialize(ref MessagePackWriter writer, ImmutableArray value, MessagePackSerializerOptions options) + { + if (value.IsDefault) + { + writer.WriteNil(); + } + else + { + var formatter = options.Resolver.GetFormatterWithVerify(); + + writer.WriteArrayHeader(value.Length); + + foreach (var item in value) + { + formatter.Serialize(ref writer, item, options); + } + } + } + + public ImmutableArray Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + else + { + var formatter = options.Resolver.GetFormatterWithVerify(); + + var len = reader.ReadArrayHeader(); + + var builder = ImmutableArray.CreateBuilder(len); + options.Security.DepthStep(ref reader); + try + { + for (var i = 0; i < len; i++) + { + builder.Add(formatter.Deserialize(ref reader, options)); + } + } + finally + { + reader.Depth--; + } + + return builder.ToImmutable(); + } + } + } + + internal sealed class ImmutableListFormatter : CollectionFormatterBase.Builder, ImmutableList.Enumerator, ImmutableList> + { + protected override void Add(ImmutableList.Builder collection, int index, T value, MessagePackSerializerOptions options) + { + collection.Add(value); + } + + protected override ImmutableList Complete(ImmutableList.Builder intermediateCollection) + { + return intermediateCollection.ToImmutable(); + } + + protected override ImmutableList.Builder Create(int count, MessagePackSerializerOptions options) + { + return ImmutableList.CreateBuilder(); + } + + protected override ImmutableList.Enumerator GetSourceEnumerator(ImmutableList source) + { + return source.GetEnumerator(); + } + } + + internal sealed class ImmutableDictionaryFormatter : DictionaryFormatterBase.Builder, ImmutableDictionary.Enumerator, ImmutableDictionary> + { + protected override void Add(ImmutableDictionary.Builder collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) + { + collection.Add(key, value); + } + + protected override ImmutableDictionary Complete(ImmutableDictionary.Builder intermediateCollection) + { + return intermediateCollection.ToImmutable(); + } + + protected override ImmutableDictionary.Builder Create(int count, MessagePackSerializerOptions options) + { + return ImmutableDictionary.CreateBuilder(options.Security.GetEqualityComparer()); + } + + protected override ImmutableDictionary.Enumerator GetSourceEnumerator(ImmutableDictionary source) + { + return source.GetEnumerator(); + } + } + + internal sealed class ImmutableHashSetFormatter : CollectionFormatterBase.Builder, ImmutableHashSet.Enumerator, ImmutableHashSet> + { + protected override void Add(ImmutableHashSet.Builder collection, int index, T value, MessagePackSerializerOptions options) + { + collection.Add(value); + } + + protected override ImmutableHashSet Complete(ImmutableHashSet.Builder intermediateCollection) + { + return intermediateCollection.ToImmutable(); + } + + protected override ImmutableHashSet.Builder Create(int count, MessagePackSerializerOptions options) + { + return ImmutableHashSet.CreateBuilder(options.Security.GetEqualityComparer()); + } + + protected override ImmutableHashSet.Enumerator GetSourceEnumerator(ImmutableHashSet source) + { + return source.GetEnumerator(); + } + } + + internal sealed class ImmutableSortedDictionaryFormatter : DictionaryFormatterBase.Builder, ImmutableSortedDictionary.Enumerator, ImmutableSortedDictionary> + { + protected override void Add(ImmutableSortedDictionary.Builder collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) + { + collection.Add(key, value); + } + + protected override ImmutableSortedDictionary Complete(ImmutableSortedDictionary.Builder intermediateCollection) + { + return intermediateCollection.ToImmutable(); + } + + protected override ImmutableSortedDictionary.Builder Create(int count, MessagePackSerializerOptions options) + { + return ImmutableSortedDictionary.CreateBuilder(); + } + + protected override ImmutableSortedDictionary.Enumerator GetSourceEnumerator(ImmutableSortedDictionary source) + { + return source.GetEnumerator(); + } + } + + internal sealed class ImmutableSortedSetFormatter : CollectionFormatterBase.Builder, ImmutableSortedSet.Enumerator, ImmutableSortedSet> + { + protected override void Add(ImmutableSortedSet.Builder collection, int index, T value, MessagePackSerializerOptions options) + { + collection.Add(value); + } + + protected override ImmutableSortedSet Complete(ImmutableSortedSet.Builder intermediateCollection) + { + return intermediateCollection.ToImmutable(); + } + + protected override ImmutableSortedSet.Builder Create(int count, MessagePackSerializerOptions options) + { + return ImmutableSortedSet.CreateBuilder(); + } + + protected override ImmutableSortedSet.Enumerator GetSourceEnumerator(ImmutableSortedSet source) + { + return source.GetEnumerator(); + } + } + + // not best for performance(does not use ImmutableQueue.Enumerator) + internal sealed class ImmutableQueueFormatter : CollectionFormatterBase, ImmutableQueue> + { + protected override void Add(ImmutableQueueBuilder collection, int index, T value, MessagePackSerializerOptions options) + { + collection.Add(value); + } + + protected override ImmutableQueue Complete(ImmutableQueueBuilder intermediateCollection) + { + return intermediateCollection.Q; + } + + protected override ImmutableQueueBuilder Create(int count, MessagePackSerializerOptions options) + { + return new ImmutableQueueBuilder(); + } + } + + // not best for performance(does not use ImmutableQueue.Enumerator) + internal sealed class ImmutableStackFormatter : CollectionFormatterBase> + { + protected override void Add(T[] collection, int index, T value, MessagePackSerializerOptions options) + { + collection[collection.Length - 1 - index] = value; + } + + protected override ImmutableStack Complete(T[] intermediateCollection) + { + return ImmutableStack.CreateRange(intermediateCollection); + } + + protected override T[] Create(int count, MessagePackSerializerOptions options) + { + return new T[count]; + } + } + + internal sealed class InterfaceImmutableListFormatter : CollectionFormatterBase.Builder, IImmutableList> + { + protected override void Add(ImmutableList.Builder collection, int index, T value, MessagePackSerializerOptions options) + { + collection.Add(value); + } + + protected override IImmutableList Complete(ImmutableList.Builder intermediateCollection) + { + return intermediateCollection.ToImmutable(); + } + + protected override ImmutableList.Builder Create(int count, MessagePackSerializerOptions options) + { + return ImmutableList.CreateBuilder(); + } + } + + internal sealed class InterfaceImmutableDictionaryFormatter : DictionaryFormatterBase.Builder, IImmutableDictionary> + { + protected override void Add(ImmutableDictionary.Builder collection, int index, TKey key, TValue value, MessagePackSerializerOptions options) + { + collection.Add(key, value); + } + + protected override IImmutableDictionary Complete(ImmutableDictionary.Builder intermediateCollection) + { + return intermediateCollection.ToImmutable(); + } + + protected override ImmutableDictionary.Builder Create(int count, MessagePackSerializerOptions options) + { + return ImmutableDictionary.CreateBuilder(options.Security.GetEqualityComparer()); + } + } + + internal sealed class InterfaceImmutableSetFormatter : CollectionFormatterBase.Builder, IImmutableSet> + { + protected override void Add(ImmutableHashSet.Builder collection, int index, T value, MessagePackSerializerOptions options) + { + collection.Add(value); + } + + protected override IImmutableSet Complete(ImmutableHashSet.Builder intermediateCollection) + { + return intermediateCollection.ToImmutable(); + } + + protected override ImmutableHashSet.Builder Create(int count, MessagePackSerializerOptions options) + { + return ImmutableHashSet.CreateBuilder(options.Security.GetEqualityComparer()); + } + } + + internal sealed class InterfaceImmutableQueueFormatter : CollectionFormatterBase, IImmutableQueue> + { + protected override void Add(ImmutableQueueBuilder collection, int index, T value, MessagePackSerializerOptions options) + { + collection.Add(value); + } + + protected override IImmutableQueue Complete(ImmutableQueueBuilder intermediateCollection) + { + return intermediateCollection.Q; + } + + protected override ImmutableQueueBuilder Create(int count, MessagePackSerializerOptions options) + { + return new ImmutableQueueBuilder(); + } + } + + internal sealed class InterfaceImmutableStackFormatter : CollectionFormatterBase> + { + protected override void Add(T[] collection, int index, T value, MessagePackSerializerOptions options) + { + collection[collection.Length - 1 - index] = value; + } + + protected override IImmutableStack Complete(T[] intermediateCollection) + { + return ImmutableStack.CreateRange(intermediateCollection); + } + + protected override T[] Create(int count, MessagePackSerializerOptions options) + { + return new T[count]; + } + } + + // pseudo builders + internal sealed class ImmutableQueueBuilder + { + public ImmutableQueue Q { get; set; } = ImmutableQueue.Empty; + + public void Add(T value) + { + Q = Q.Enqueue(value); + } + } + } +} diff --git a/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs b/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs new file mode 100644 index 0000000000000..fe506f3064669 --- /dev/null +++ b/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs @@ -0,0 +1,307 @@ +// 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 enable + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Runtime.Serialization; +using MessagePack; +using MessagePack.Formatters; +using Microsoft.CodeAnalysis.AddImport; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.ConvertTupleToStruct; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.DocumentHighlighting; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.NavigateTo; +using Microsoft.CodeAnalysis.Rename.ConflictEngine; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Remote +{ + /// + /// Defines MessagePack formatters for public types without a public constructor suitable for deserialization. + /// Roslyn internal types should always be annotated with and have the right constructor. + /// + internal sealed class MessagePackFormatters + { + public static ImmutableArray GetFormatters() => ImmutableArray.Create( + SolutionIdFormatter.Instance, + ProjectIdFormatter.Instance, + DocumentIdFormatter.Instance, + EnumFormatters.AnalysisKind, + EnumFormatters.AnalysisKind.CreateNullable(), + EnumFormatters.HighlightSpanKind, + EnumFormatters.Scope, + EnumFormatters.RelatedLocationType, + EnumFormatters.SearchKind, + EnumFormatters.NavigateToMatchKind, + EnumFormatters.Glyph, + EnumFormatters.Glyph.CreateNullable(), + EnumFormatters.TaggedTextStyle, + EnumFormatters.ValueUsageInfo, + EnumFormatters.ValueUsageInfo.CreateNullable(), + EnumFormatters.TypeOrNamespaceUsageInfo, + EnumFormatters.TypeOrNamespaceUsageInfo.CreateNullable(), + EnumFormatters.AddImportFixKind, + EnumFormatters.CodeActionPriority, + EnumFormatters.DependentTypesKind); + + // TODO: remove https://github.com/neuecc/MessagePack-CSharp/issues/1025 + internal static class EnumFormatters + { + public static readonly EnumFormatter AnalysisKind = new(value => (int)value, value => (AnalysisKind)value); + public static readonly EnumFormatter HighlightSpanKind = new(value => (int)value, value => (HighlightSpanKind)value); + public static readonly EnumFormatter Scope = new(value => (int)value, value => (Scope)value); + public static readonly EnumFormatter RelatedLocationType = new(value => (int)value, value => (RelatedLocationType)value); + public static readonly EnumFormatter SearchKind = new(value => (int)value, value => (SearchKind)value); + public static readonly EnumFormatter NavigateToMatchKind = new(value => (int)value, value => (NavigateToMatchKind)value); + public static readonly EnumFormatter Glyph = new(value => (int)value, value => (Glyph)value); + public static readonly EnumFormatter TaggedTextStyle = new(value => (int)value, value => (TaggedTextStyle)value); + public static readonly EnumFormatter ValueUsageInfo = new(value => (int)value, value => (ValueUsageInfo)value); + public static readonly EnumFormatter TypeOrNamespaceUsageInfo = new(value => (int)value, value => (TypeOrNamespaceUsageInfo)value); + public static readonly EnumFormatter AddImportFixKind = new(value => (int)value, value => (AddImportFixKind)value); + public static readonly EnumFormatter CodeActionPriority = new(value => (int)value, value => (CodeActionPriority)value); + public static readonly EnumFormatter DependentTypesKind = new(value => (int)value, value => (DependentTypesKind)value); + } + + internal sealed class SolutionIdFormatter : IMessagePackFormatter + { + public static readonly SolutionIdFormatter Instance = new SolutionIdFormatter(); + + public SolutionId? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + try + { + if (reader.TryReadNil()) + { + return null; + } + + Contract.ThrowIfFalse(reader.ReadArrayHeader() == 2); + var id = GuidFormatter.Instance.Deserialize(ref reader, options); + var debugName = reader.ReadString(); + + return SolutionId.CreateFromSerialized(id, debugName); + } + catch (Exception e) when (e is not MessagePackSerializationException) + { + throw new MessagePackSerializationException(e.Message, e); + } + } + + public void Serialize(ref MessagePackWriter writer, SolutionId? value, MessagePackSerializerOptions options) + { + try + { + if (value is null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(2); + GuidFormatter.Instance.Serialize(ref writer, value.Id, options); + writer.Write(value.DebugName); + } + } + catch (Exception e) when (e is not MessagePackSerializationException) + { + throw new MessagePackSerializationException(e.Message, e); + } + } + } + + internal sealed class ProjectIdFormatter : IMessagePackFormatter + { + public static readonly ProjectIdFormatter Instance = new ProjectIdFormatter(); + + public ProjectId? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + try + { + if (reader.TryReadNil()) + { + return null; + } + + Contract.ThrowIfFalse(reader.ReadArrayHeader() == 2); + var id = GuidFormatter.Instance.Deserialize(ref reader, options); + var debugName = reader.ReadString(); + + return ProjectId.CreateFromSerialized(id, debugName); + } + catch (Exception e) when (e is not MessagePackSerializationException) + { + throw new MessagePackSerializationException(e.Message, e); + } + } + + public void Serialize(ref MessagePackWriter writer, ProjectId? value, MessagePackSerializerOptions options) + { + try + { + if (value is null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(2); + GuidFormatter.Instance.Serialize(ref writer, value.Id, options); + writer.Write(value.DebugName); + } + } + catch (Exception e) when (e is not MessagePackSerializationException) + { + throw new MessagePackSerializationException(e.Message, e); + } + } + } + + internal sealed class DocumentIdFormatter : IMessagePackFormatter + { + public static readonly DocumentIdFormatter Instance = new DocumentIdFormatter(); + + public DocumentId? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + try + { + if (reader.TryReadNil()) + { + return null; + } + + Contract.ThrowIfFalse(reader.ReadArrayHeader() == 3); + + var projectId = ProjectIdFormatter.Instance.Deserialize(ref reader, options); + Contract.ThrowIfNull(projectId); + + var id = GuidFormatter.Instance.Deserialize(ref reader, options); + var debugName = reader.ReadString(); + + return DocumentId.CreateFromSerialized(projectId, id, debugName); + } + catch (Exception e) when (e is not MessagePackSerializationException) + { + throw new MessagePackSerializationException(e.Message, e); + } + } + + public void Serialize(ref MessagePackWriter writer, DocumentId? value, MessagePackSerializerOptions options) + { + try + { + if (value is null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(3); + ProjectIdFormatter.Instance.Serialize(ref writer, value.ProjectId, options); + GuidFormatter.Instance.Serialize(ref writer, value.Id, options); + writer.Write(value.DebugName); + } + } + catch (Exception e) when (e is not MessagePackSerializationException) + { + throw new MessagePackSerializationException(e.Message, e); + } + } + } + + // TODO: remove https://github.com/neuecc/MessagePack-CSharp/issues/1025 + internal sealed class EnumFormatter : IMessagePackFormatter + where TEnum : struct + { + private readonly Func _toInt; + private readonly Func _toEnum; + + static EnumFormatter() + { + var underlyingType = typeof(TEnum).GetEnumUnderlyingType(); + Contract.ThrowIfTrue(underlyingType == typeof(long) || underlyingType == typeof(ulong)); + } + + public EnumFormatter(Func toInt, Func toEnum) + { + _toInt = toInt; + _toEnum = toEnum; + } + + public TEnum Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + try + { + return _toEnum(reader.ReadInt32()); + } + catch (Exception e) when (e is not MessagePackSerializationException) + { + throw new MessagePackSerializationException(e.Message, e); + } + } + + public void Serialize(ref MessagePackWriter writer, TEnum value, MessagePackSerializerOptions options) + { + try + { + writer.WriteInt32(_toInt(value)); + } + catch (Exception e) when (e is not MessagePackSerializationException) + { + throw new MessagePackSerializationException(e.Message, e); + } + } + + public NullableEnum CreateNullable() + => new NullableEnum(_toInt, _toEnum); + + internal sealed class NullableEnum : IMessagePackFormatter + { + private readonly Func _toInt; + private readonly Func _toEnum; + + public NullableEnum(Func toInt, Func toEnum) + { + _toInt = toInt; + _toEnum = toEnum; + } + + public TEnum? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + try + { + return reader.TryReadNil() ? null : _toEnum(reader.ReadInt32()); + } + catch (Exception e) when (e is not MessagePackSerializationException) + { + throw new MessagePackSerializationException(e.Message, e); + } + } + + public void Serialize(ref MessagePackWriter writer, TEnum? value, MessagePackSerializerOptions options) + { + try + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteInt32(_toInt(value.Value)); + } + } + catch (Exception e) when (e is not MessagePackSerializationException) + { + throw new MessagePackSerializationException(e.Message, e); + } + } + } + } + } +} diff --git a/src/Workspaces/Remote/Core/Serialization/RoslynJsonConverter.RoslynOnly.cs b/src/Workspaces/Remote/Core/Serialization/RoslynJsonConverter.RoslynOnly.cs index ef2aab06fc605..d229f6378e9b1 100644 --- a/src/Workspaces/Remote/Core/Serialization/RoslynJsonConverter.RoslynOnly.cs +++ b/src/Workspaces/Remote/Core/Serialization/RoslynJsonConverter.RoslynOnly.cs @@ -3,14 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.AddImport; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.DocumentHighlighting; -using Microsoft.CodeAnalysis.Packaging; -using Microsoft.CodeAnalysis.SymbolSearch; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.TodoComments; using Newtonsoft.Json; @@ -33,13 +28,6 @@ partial void AppendRoslynSpecificJsonConverters(ImmutableDictionary - { - protected override PackageSource ReadValue(JsonReader reader, JsonSerializer serializer) - { - Contract.ThrowIfFalse(reader.TokenType == JsonToken.StartObject); - - var name = ReadProperty(reader); - var source = ReadProperty(reader); - - Contract.ThrowIfFalse(reader.Read()); - Contract.ThrowIfFalse(reader.TokenType == JsonToken.EndObject); - - return new PackageSource(name, source); - } - - protected override void WriteValue(JsonWriter writer, PackageSource source, JsonSerializer serializer) - { - writer.WriteStartObject(); - - writer.WritePropertyName(nameof(PackageSource.Name)); - writer.WriteValue(source.Name); - - writer.WritePropertyName(nameof(PackageSource.Source)); - writer.WriteValue(source.Source); - - writer.WriteEndObject(); - } - } - private class HighlightSpanJsonConverter : BaseJsonConverter { protected override HighlightSpan ReadValue(JsonReader reader, JsonSerializer serializer) @@ -165,113 +124,6 @@ protected override void WriteValue(JsonWriter writer, HighlightSpan source, Json } } - private class PackageWithTypeResultJsonConverter : BaseJsonConverter - { - protected override PackageWithTypeResult ReadValue(JsonReader reader, JsonSerializer serializer) - { - Contract.ThrowIfFalse(reader.TokenType == JsonToken.StartObject); - - var packageName = ReadProperty(reader); - var typeName = ReadProperty(reader); - var version = ReadProperty(reader); - var rank = (int)ReadProperty(reader); - var containingNamespaceNames = ReadProperty>(reader, serializer); - - Contract.ThrowIfFalse(reader.Read()); - Contract.ThrowIfFalse(reader.TokenType == JsonToken.EndObject); - - return new PackageWithTypeResult(packageName, typeName, version, rank, containingNamespaceNames); - } - - protected override void WriteValue(JsonWriter writer, PackageWithTypeResult source, JsonSerializer serializer) - { - writer.WriteStartObject(); - - writer.WritePropertyName(nameof(PackageWithTypeResult.PackageName)); - writer.WriteValue(source.PackageName); - - writer.WritePropertyName(nameof(PackageWithTypeResult.TypeName)); - writer.WriteValue(source.TypeName); - - writer.WritePropertyName(nameof(PackageWithTypeResult.Version)); - writer.WriteValue(source.Version); - - writer.WritePropertyName(nameof(PackageWithTypeResult.Rank)); - writer.WriteValue(source.Rank); - - writer.WritePropertyName(nameof(PackageWithTypeResult.ContainingNamespaceNames)); - serializer.Serialize(writer, source.ContainingNamespaceNames); - - writer.WriteEndObject(); - } - } - - private class PackageWithAssemblyResultJsonConverter : BaseJsonConverter - { - protected override PackageWithAssemblyResult ReadValue(JsonReader reader, JsonSerializer serializer) - { - Contract.ThrowIfFalse(reader.TokenType == JsonToken.StartObject); - - var packageName = ReadProperty(reader); - var version = ReadProperty(reader); - var rank = (int)ReadProperty(reader); - - Contract.ThrowIfFalse(reader.Read()); - Contract.ThrowIfFalse(reader.TokenType == JsonToken.EndObject); - - return new PackageWithAssemblyResult(packageName, version, rank); - } - - protected override void WriteValue(JsonWriter writer, PackageWithAssemblyResult source, JsonSerializer serializer) - { - writer.WriteStartObject(); - - writer.WritePropertyName(nameof(PackageWithAssemblyResult.PackageName)); - writer.WriteValue(source.PackageName); - - writer.WritePropertyName(nameof(PackageWithAssemblyResult.Version)); - writer.WriteValue(source.Version); - - writer.WritePropertyName(nameof(PackageWithAssemblyResult.Rank)); - writer.WriteValue(source.Rank); - - writer.WriteEndObject(); - } - } - - private class ReferenceAssemblyWithTypeResultJsonConverter : BaseJsonConverter - { - protected override ReferenceAssemblyWithTypeResult ReadValue(JsonReader reader, JsonSerializer serializer) - { - Contract.ThrowIfFalse(reader.TokenType == JsonToken.StartObject); - - var assemblyName = ReadProperty(reader); - var typeName = ReadProperty(reader); - var containingNamespaceNames = ReadProperty>(reader, serializer); - - Contract.ThrowIfFalse(reader.Read()); - Contract.ThrowIfFalse(reader.TokenType == JsonToken.EndObject); - - return new ReferenceAssemblyWithTypeResult(assemblyName, typeName, containingNamespaceNames); - } - - protected override void WriteValue(JsonWriter writer, ReferenceAssemblyWithTypeResult source, JsonSerializer serializer) - { - writer.WriteStartObject(); - - writer.WritePropertyName(nameof(ReferenceAssemblyWithTypeResult.AssemblyName)); - writer.WriteValue(source.AssemblyName); - - writer.WritePropertyName(nameof(ReferenceAssemblyWithTypeResult.TypeName)); - writer.WriteValue(source.TypeName); - - writer.WritePropertyName(nameof(ReferenceAssemblyWithTypeResult.ContainingNamespaceNames)); - serializer.Serialize(writer, source.ContainingNamespaceNames); - - writer.WriteEndObject(); - } - } - private class TaggedTextJsonConverter : BaseJsonConverter { protected override TaggedText ReadValue(JsonReader reader, JsonSerializer serializer) @@ -301,90 +153,6 @@ protected override void WriteValue(JsonWriter writer, TaggedText source, JsonSer } } - private class AddImportFixDataJsonConverter : BaseJsonConverter - { - protected override AddImportFixData ReadValue(JsonReader reader, JsonSerializer serializer) - { - Contract.ThrowIfFalse(reader.TokenType == JsonToken.StartObject); - - var kind = (AddImportFixKind)ReadProperty(reader); - var textChanges = ReadProperty>(reader, serializer).ToImmutableArrayOrEmpty(); - var title = ReadProperty(reader); - var tags = ReadProperty>(reader, serializer).ToImmutableArrayOrEmpty(); - var priority = (CodeActionPriority)ReadProperty(reader); - - var projectReferenceToAdd = ReadProperty(reader, serializer); - - var portableExecutableReferenceProjectId = ReadProperty(reader, serializer); - var portableExecutableReferenceFilePathToAdd = ReadProperty(reader); - - var assemblyReferenceAssemblyName = ReadProperty(reader); - var assemblyReferenceFullyQualifiedTypeName = ReadProperty(reader); - - var packageSource = ReadProperty(reader); - var packageName = ReadProperty(reader); - var packageVersionOpt = ReadProperty(reader); - - Contract.ThrowIfFalse(reader.Read()); - Contract.ThrowIfFalse(reader.TokenType == JsonToken.EndObject); - - return kind switch - { - AddImportFixKind.ProjectSymbol => AddImportFixData.CreateForProjectSymbol(textChanges, title, tags, priority, projectReferenceToAdd), - AddImportFixKind.MetadataSymbol => AddImportFixData.CreateForMetadataSymbol(textChanges, title, tags, priority, portableExecutableReferenceProjectId, portableExecutableReferenceFilePathToAdd), - AddImportFixKind.PackageSymbol => AddImportFixData.CreateForPackageSymbol(textChanges, packageSource, packageName, packageVersionOpt), - AddImportFixKind.ReferenceAssemblySymbol => AddImportFixData.CreateForReferenceAssemblySymbol(textChanges, title, assemblyReferenceAssemblyName, assemblyReferenceFullyQualifiedTypeName), - _ => throw ExceptionUtilities.Unreachable, - }; - } - - protected override void WriteValue(JsonWriter writer, AddImportFixData source, JsonSerializer serializer) - { - writer.WriteStartObject(); - - writer.WritePropertyName(nameof(AddImportFixData.Kind)); - writer.WriteValue((int)source.Kind); - - writer.WritePropertyName(nameof(AddImportFixData.TextChanges)); - serializer.Serialize(writer, source.TextChanges ?? SpecializedCollections.EmptyList()); - - writer.WritePropertyName(nameof(AddImportFixData.Title)); - writer.WriteValue(source.Title); - - writer.WritePropertyName(nameof(AddImportFixData.Tags)); - serializer.Serialize(writer, source.Tags ?? SpecializedCollections.EmptyList()); - - writer.WritePropertyName(nameof(AddImportFixData.Priority)); - writer.WriteValue((int)source.Priority); - - writer.WritePropertyName(nameof(AddImportFixData.ProjectReferenceToAdd)); - serializer.Serialize(writer, source.ProjectReferenceToAdd); - - writer.WritePropertyName(nameof(AddImportFixData.PortableExecutableReferenceProjectId)); - serializer.Serialize(writer, source.PortableExecutableReferenceProjectId); - - writer.WritePropertyName(nameof(AddImportFixData.PortableExecutableReferenceFilePathToAdd)); - writer.WriteValue(source.PortableExecutableReferenceFilePathToAdd); - - writer.WritePropertyName(nameof(AddImportFixData.AssemblyReferenceAssemblyName)); - writer.WriteValue(source.AssemblyReferenceAssemblyName); - - writer.WritePropertyName(nameof(AddImportFixData.AssemblyReferenceFullyQualifiedTypeName)); - writer.WriteValue(source.AssemblyReferenceFullyQualifiedTypeName); - - writer.WritePropertyName(nameof(AddImportFixData.PackageSource)); - writer.WriteValue(source.PackageSource); - - writer.WritePropertyName(nameof(AddImportFixData.PackageName)); - writer.WriteValue(source.PackageName); - - writer.WritePropertyName(nameof(AddImportFixData.PackageVersionOpt)); - writer.WriteValue(source.PackageVersionOpt); - - writer.WriteEndObject(); - } - } - private class AnalyzerPerformanceInfoConverter : BaseJsonConverter { protected override AnalyzerPerformanceInfo ReadValue(JsonReader reader, JsonSerializer serializer) diff --git a/src/Workspaces/Remote/Core/ServiceDescriptor.cs b/src/Workspaces/Remote/Core/ServiceDescriptor.cs index c6f23e433e63f..68d4d25e8e557 100644 --- a/src/Workspaces/Remote/Core/ServiceDescriptor.cs +++ b/src/Workspaces/Remote/Core/ServiceDescriptor.cs @@ -5,6 +5,9 @@ #nullable enable using System; +using System.IO.Pipelines; +using MessagePack; +using MessagePack.Resolvers; using Microsoft.ServiceHub.Framework; using Nerdbank.Streams; using StreamJsonRpc; @@ -33,7 +36,7 @@ internal sealed class ServiceDescriptor : ServiceJsonRpcDescriptor }.GetFrozenCopy(); private ServiceDescriptor(ServiceMoniker serviceMoniker, Type? clientInterface) - : base(serviceMoniker, clientInterface, Formatters.UTF8, MessageDelimiters.HttpLikeHeaders, s_multiplexingStreamOptions) + : base(serviceMoniker, clientInterface, Formatters.MessagePack, MessageDelimiters.BigEndianInt32LengthHeader, s_multiplexingStreamOptions) { } @@ -52,11 +55,18 @@ protected override ServiceRpcDescriptor Clone() => new ServiceDescriptor(this); protected override IJsonRpcMessageFormatter CreateFormatter() - => ConfigureFormatter((JsonMessageFormatter)base.CreateFormatter()); + => ConfigureFormatter((MessagePackFormatter)base.CreateFormatter()); - internal static JsonMessageFormatter ConfigureFormatter(JsonMessageFormatter formatter) + private static readonly MessagePackSerializerOptions s_options = StandardResolverAllowPrivate.Options + .WithSecurity(MessagePackSecurity.UntrustedData) + .WithResolver(CompositeResolver.Create( + MessagePackFormatters.GetFormatters(), + new IFormatterResolver[] { ImmutableCollectionMessagePackResolver.Instance, StandardResolverAllowPrivate.Instance })); + + private static MessagePackFormatter ConfigureFormatter(MessagePackFormatter formatter) { - formatter.JsonSerializer.Converters.Add(AggregateJsonConverter.Instance); + // See https://github.com/neuecc/messagepack-csharp. + formatter.SetMessagePackSerializerOptions(s_options); return formatter; } @@ -67,5 +77,10 @@ protected override JsonRpcConnection CreateConnection(JsonRpc jsonRpc) connection.LocalRpcTargetOptions = s_jsonRpcTargetOptions; return connection; } + + internal static class TestAccessor + { + public static MessagePackSerializerOptions Options => s_options; + } } } diff --git a/src/Workspaces/Remote/Core/ServiceDescriptors.cs b/src/Workspaces/Remote/Core/ServiceDescriptors.cs index cb77027fec81c..67e83c31d1cf8 100644 --- a/src/Workspaces/Remote/Core/ServiceDescriptors.cs +++ b/src/Workspaces/Remote/Core/ServiceDescriptors.cs @@ -7,10 +7,21 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.CodeLens; +using Microsoft.CodeAnalysis.Completion.Providers; +using Microsoft.CodeAnalysis.ConvertTupleToStruct; using Microsoft.CodeAnalysis.DesignerAttribute; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.DocumentHighlighting; +using Microsoft.CodeAnalysis.EncapsulateField; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.NavigateTo; using Microsoft.CodeAnalysis.ProjectTelemetry; +using Microsoft.CodeAnalysis.Rename; +using Microsoft.CodeAnalysis.SymbolSearch; using Microsoft.CodeAnalysis.TodoComments; using Microsoft.ServiceHub.Framework; using Roslyn.Utilities; @@ -32,29 +43,45 @@ internal static class ServiceDescriptors internal static readonly ImmutableDictionary Descriptors = ImmutableDictionary.CreateRange(new[] { - CreateDescriptors(typeof(IRemoteTodoCommentsService), callbackInterface: typeof(ITodoCommentsListener)), - CreateDescriptors(typeof(IRemoteDesignerAttributeService), callbackInterface: typeof(IDesignerAttributeListener)), + CreateDescriptors(typeof(IRemoteTodoCommentsDiscoveryService), callbackInterface: typeof(ITodoCommentsListener)), + CreateDescriptors(typeof(IRemoteDesignerAttributeDiscoveryService), callbackInterface: typeof(IDesignerAttributeListener)), CreateDescriptors(typeof(IRemoteProjectTelemetryService), callbackInterface: typeof(IProjectTelemetryListener)), CreateDescriptors(typeof(IRemoteDiagnosticAnalyzerService)), CreateDescriptors(typeof(IRemoteSemanticClassificationService)), CreateDescriptors(typeof(IRemoteSemanticClassificationCacheService)), + CreateDescriptors(typeof(IRemoteDocumentHighlightsService)), + CreateDescriptors(typeof(IRemoteEncapsulateFieldService)), + CreateDescriptors(typeof(IRemoteRenamerService)), + CreateDescriptors(typeof(IRemoteConvertTupleToStructCodeRefactoringService)), + CreateDescriptors(typeof(IRemoteSymbolFinderService), callbackInterface: typeof(IRemoteSymbolFinderService.ICallback)), + CreateDescriptors(typeof(IRemoteFindUsagesService), callbackInterface: typeof(IRemoteFindUsagesService.ICallback)), + CreateDescriptors(typeof(IRemoteNavigateToSearchService)), + CreateDescriptors(typeof(IRemoteMissingImportDiscoveryService), callbackInterface: typeof(IRemoteMissingImportDiscoveryService.ICallback)), + CreateDescriptors(typeof(IRemoteSymbolSearchUpdateService), callbackInterface: typeof(ISymbolSearchLogService)), + CreateDescriptors(typeof(IRemoteExtensionMethodImportCompletionService)), + CreateDescriptors(typeof(IRemoteDependentTypeFinderService)), + CreateDescriptors(typeof(IRemoteGlobalNotificationDeliveryService)), + CreateDescriptors(typeof(IRemoteCodeLensReferencesService)), }); - private static string GetServiceName(Type serviceInterface) + internal static string GetServiceName(Type serviceInterface) { Contract.ThrowIfFalse(serviceInterface.IsInterface); var interfaceName = serviceInterface.Name; Contract.ThrowIfFalse(interfaceName.StartsWith(InterfaceNamePrefix, StringComparison.Ordinal)); Contract.ThrowIfFalse(interfaceName.EndsWith(InterfaceNameSuffix, StringComparison.Ordinal)); - return ServiceNamePrefix + interfaceName.Substring(InterfaceNamePrefix.Length, interfaceName.Length - InterfaceNamePrefix.Length - InterfaceNameSuffix.Length); + return interfaceName.Substring(InterfaceNamePrefix.Length, interfaceName.Length - InterfaceNamePrefix.Length - InterfaceNameSuffix.Length); } + internal static string GetQualifiedServiceName(Type serviceInterface) + => ServiceNamePrefix + GetServiceName(serviceInterface); + private static KeyValuePair CreateDescriptors(Type serviceInterface, Type? callbackInterface = null) { Contract.ThrowIfFalse(callbackInterface == null || callbackInterface.IsInterface); - var serviceName = GetServiceName(serviceInterface); + var serviceName = GetQualifiedServiceName(serviceInterface); var descriptor32 = ServiceDescriptor.CreateRemoteServiceDescriptor(serviceName, callbackInterface); var descriptor64 = ServiceDescriptor.CreateRemoteServiceDescriptor(serviceName + RemoteServiceName.Suffix64, callbackInterface); return new(serviceInterface, (descriptor32, descriptor64)); @@ -65,5 +92,8 @@ public static ServiceRpcDescriptor GetServiceDescriptor(Type serviceType, bool i var (descriptor32, descriptor64) = Descriptors[serviceType]; return isRemoteHost64Bit ? descriptor64 : descriptor32; } + + internal static string GetFeatureName(Type serviceInterface) + => RemoteWorkspacesResources.GetResourceString("FeatureName_" + GetServiceName(serviceInterface)); } } diff --git a/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs b/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs index c6e5fd8ffd2f4..b031f15af06e6 100644 --- a/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs +++ b/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs @@ -37,6 +37,7 @@ internal sealed partial class ServiceHubRemoteHostClient : RemoteHostClient, IRe private readonly IServiceBroker _serviceBroker; private readonly ServiceBrokerClient _serviceBrokerClient; private readonly IErrorReportingService? _errorReportingService; + private readonly IRemoteHostClientShutdownCancellationService? _shutdownCancellationService; private readonly ConnectionPools? _connectionPools; @@ -67,6 +68,7 @@ private ServiceHubRemoteHostClient( _assetStorage = services.GetRequiredService().AssetStorage; _serializer = services.GetRequiredService(); _errorReportingService = services.GetService(); + _shutdownCancellationService = services.GetService(); } private void OnUnexpectedExceptionThrown(Exception unexpectedException) @@ -167,7 +169,7 @@ public override async ValueTask> CreateConnectionAsyn Contract.ThrowIfNull(proxy, $"Brokered service not found: {descriptor.Moniker.Name}"); - return new BrokeredServiceConnection(proxy, _assetStorage, _errorReportingService); + return new BrokeredServiceConnection(proxy, _assetStorage, _errorReportingService, _shutdownCancellationService); } catch (Exception e) when (FatalError.ReportWithoutCrashUnlessCanceledAndPropagate(e)) { diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf new file mode 100644 index 0000000000000..5cf79a1dcd8ac --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf new file mode 100644 index 0000000000000..c7438653818e1 --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf new file mode 100644 index 0000000000000..961d988a73908 --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf new file mode 100644 index 0000000000000..4de4b95589c24 --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf new file mode 100644 index 0000000000000..cd16f1d4a8e8b --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf new file mode 100644 index 0000000000000..670006576714f --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf new file mode 100644 index 0000000000000..bf0c69be3b194 --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf new file mode 100644 index 0000000000000..63036390f159f --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf new file mode 100644 index 0000000000000..4a6f2853990a9 --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf new file mode 100644 index 0000000000000..1ad650d8156ff --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf new file mode 100644 index 0000000000000..3a5b8c41b3d30 --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf new file mode 100644 index 0000000000000..ce5bfe569807d --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf new file mode 100644 index 0000000000000..050669b54e42e --- /dev/null +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf @@ -0,0 +1,117 @@ + + + + + + CodeLens references + CodeLens references + + + + Convert tuple to struct refactoring + Convert tuple to struct refactoring + + + + Dependent type finder + Dependent type finder + + + + DesignerAttribute discovery + DesignerAttribute discovery + + + + Diagnostic analyzer runner + Diagnostic analyzer runner + + + + Document higlights + Document higlights + + + + Encapsulate field refactoring + Encapsulate field refactoring + + + + Extension method import completion + Extension method import completion + + + + Find usages + Find usages + + + + Global notification delivery + Global notification delivery + + + + Missing import discovery + Missing import discovery + + + + Navigate to + Navigate to + + + + Project telemetry collection + Project telemetry collection + + + + Rename + Rename + + + + Semantic classification + Semantic classification + + + + Semantic classification cache + Semantic classification cache + + + + Symbol finder + Symbol finder + + + + Symbol search + Symbol search + + + + TODO comments discovery + TODO comments discovery + + + + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + Feature '{0}' is currently unavailable due to an intermittent error, please try again later: '{1}' + + + + Feature '{0}' is currently unavailable due to an internal error. + Feature '{0}' is currently unavailable due to an internal error. + + + + Feature '{0}' is currently unavailable since {1} is shutting down. + Feature '{0}' is currently unavailable since {1} is shutting down. + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs b/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs index 7db97dd45a5ed..4841903ec58a3 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs @@ -5,6 +5,7 @@ #nullable enable using System; +using System.Diagnostics; using System.IO; using System.IO.Pipelines; using System.Threading; @@ -27,6 +28,11 @@ internal interface IFactory internal abstract class FactoryBase : IServiceHubServiceFactory, IFactory where TService : class { + static FactoryBase() + { + Debug.Assert(typeof(TService).IsInterface); + } + protected abstract TService CreateService(in ServiceConstructionArguments arguments); protected virtual TService CreateService( @@ -65,7 +71,8 @@ internal TService Create( IServiceBroker serviceBroker) { var descriptor = ServiceDescriptors.GetServiceDescriptor(typeof(TService), isRemoteHost64Bit: IntPtr.Size == 8); - var serverConnection = descriptor.ConstructRpcConnection(pipe); + var serviceHubTraceSource = (TraceSource)hostProvidedServices.GetService(typeof(TraceSource)); + var serverConnection = descriptor.WithTraceSource(serviceHubTraceSource).ConstructRpcConnection(pipe); var args = new ServiceConstructionArguments(hostProvidedServices, serviceBroker, new CancellationTokenSource()); var service = CreateService(args, descriptor, serverConnection, serviceActivationOptions.ClientRpcTarget); @@ -81,6 +88,11 @@ internal abstract class FactoryBase : FactoryBase where TService : class where TCallback : class { + static FactoryBase() + { + Debug.Assert(typeof(TCallback).IsInterface); + } + protected abstract TService CreateService(in ServiceConstructionArguments arguments, RemoteCallback callback); protected sealed override TService CreateService(in ServiceConstructionArguments arguments) diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService.cs deleted file mode 100644 index 07b304b534255..0000000000000 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService.cs +++ /dev/null @@ -1,23 +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. - -using System; -using System.IO; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.ServiceHub.Framework; -using Microsoft.ServiceHub.Framework.Services; - -namespace Microsoft.CodeAnalysis.Remote -{ - // root level service for all Roslyn services - internal partial class CodeAnalysisService : ServiceBase - { - public CodeAnalysisService(Stream stream, IServiceProvider serviceProvider) - : base(serviceProvider, stream) - { - StartService(); - } - } -} diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_AddImport.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_AddImport.cs deleted file mode 100644 index bf164fb7dd028..0000000000000 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_AddImport.cs +++ /dev/null @@ -1,91 +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. - -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.AddImport; -using Microsoft.CodeAnalysis.Packaging; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.SymbolSearch; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.Remote -{ - internal partial class CodeAnalysisService : IRemoteAddImportFeatureService - { - public Task> GetFixesAsync( - PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan span, string diagnosticId, int maxResults, bool placeSystemNamespaceFirst, - bool searchReferenceAssemblies, IList packageSources, CancellationToken cancellationToken) - { - return RunServiceAsync(async () => - { - using (UserOperationBooster.Boost()) - { - var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); - var document = solution.GetDocument(documentId); - - var service = document.GetLanguageService(); - - var symbolSearchService = new SymbolSearchService(EndPoint); - - var result = await service.GetFixesAsync( - document, span, diagnosticId, maxResults, placeSystemNamespaceFirst, - symbolSearchService, searchReferenceAssemblies, - packageSources.ToImmutableArray(), cancellationToken).ConfigureAwait(false); - - return (IList)result; - } - }, cancellationToken); - } - - /// - /// Provides an implementation of the ISymbolSearchService on the remote side so that - /// Add-Import can find results in nuget packages/reference assemblies. This works - /// by remoting *from* the OOP server back to the host, which can then forward this - /// appropriately to wherever the real ISymbolSearchService is running. This is necessary - /// because it's not guaranteed that the real ISymbolSearchService will be running in - /// the same process that is supplying the . - /// - /// Ideally we would not need to bounce back to the host for this. - /// - private sealed class SymbolSearchService : ISymbolSearchService - { - private readonly RemoteEndPoint _endPoint; - - public SymbolSearchService(RemoteEndPoint endPoint) - { - _endPoint = endPoint; - } - - public async Task> FindPackagesWithTypeAsync( - string source, string name, int arity, CancellationToken cancellationToken) - { - return await _endPoint.InvokeAsync>( - nameof(IRemoteSymbolSearchUpdateEngine.FindPackagesWithTypeAsync), - new object[] { source, name, arity }, - cancellationToken).ConfigureAwait(false); - } - - public async Task> FindPackagesWithAssemblyAsync( - string source, string assemblyName, CancellationToken cancellationToken) - { - return await _endPoint.InvokeAsync>( - nameof(IRemoteSymbolSearchUpdateEngine.FindPackagesWithAssemblyAsync), - new object[] { source, assemblyName }, - cancellationToken).ConfigureAwait(false); - } - - public async Task> FindReferenceAssembliesWithTypeAsync( - string name, int arity, CancellationToken cancellationToken) - { - return await _endPoint.InvokeAsync>( - nameof(IRemoteSymbolSearchUpdateEngine.FindReferenceAssembliesWithTypeAsync), - new object[] { name, arity }, - cancellationToken).ConfigureAwait(false); - } - } - } -} diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_CodeLens.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_CodeLens.cs deleted file mode 100644 index 915be2110b99e..0000000000000 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_CodeLens.cs +++ /dev/null @@ -1,108 +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. - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeLens; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.Remote -{ - internal partial class CodeAnalysisService : IRemoteCodeLensReferencesService - { - public Task GetReferenceCountAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, int maxResultCount, CancellationToken cancellationToken) - { - return RunServiceAsync(async () => - { - using (Internal.Log.Logger.LogBlock(FunctionId.CodeAnalysisService_GetReferenceCountAsync, documentId.ProjectId.DebugName, cancellationToken)) - { - var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); - - var document = solution.GetDocument(documentId); - if (document == null) - { - return null; - } - - var syntaxNode = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)).FindNode(textSpan); - - return await CodeLensReferencesServiceFactory.Instance.GetReferenceCountAsync( - solution, - documentId, - syntaxNode, - maxResultCount, - cancellationToken).ConfigureAwait(false); - } - }, cancellationToken); - } - - public Task> FindReferenceLocationsAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken) - { - return RunServiceAsync(async () => - { - using (Internal.Log.Logger.LogBlock(FunctionId.CodeAnalysisService_FindReferenceLocationsAsync, documentId.ProjectId.DebugName, cancellationToken)) - { - var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); - var document = solution.GetDocument(documentId); - if (document == null) - { - return null; - } - - var syntaxNode = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)).FindNode(textSpan); - - return await CodeLensReferencesServiceFactory.Instance.FindReferenceLocationsAsync( - solution, - documentId, - syntaxNode, - cancellationToken).ConfigureAwait(false); - } - }, cancellationToken); - } - - public Task> FindReferenceMethodsAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken) - { - return RunServiceAsync(async () => - { - using (Internal.Log.Logger.LogBlock(FunctionId.CodeAnalysisService_FindReferenceMethodsAsync, documentId.ProjectId.DebugName, cancellationToken)) - { - var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); - var document = solution.GetDocument(documentId); - if (document == null) - { - return null; - } - - var syntaxNode = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)).FindNode(textSpan); - - return await CodeLensReferencesServiceFactory.Instance.FindReferenceMethodsAsync(solution, documentId, - syntaxNode, cancellationToken).ConfigureAwait(false); - } - }, cancellationToken); - } - - public Task GetFullyQualifiedNameAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken) - { - return RunServiceAsync(async () => - { - using (Internal.Log.Logger.LogBlock(FunctionId.CodeAnalysisService_GetFullyQualifiedName, documentId.ProjectId.DebugName, cancellationToken)) - { - var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); - var document = solution.GetDocument(documentId); - if (document == null) - { - return null; - } - - var syntaxNode = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)).FindNode(textSpan); - - return await CodeLensReferencesServiceFactory.Instance.GetFullyQualifiedNameAsync(solution, documentId, - syntaxNode, cancellationToken).ConfigureAwait(false); - } - }, cancellationToken); - } - } -} diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_DependentTypeFinder.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_DependentTypeFinder.cs deleted file mode 100644 index 771c89b83b303..0000000000000 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_DependentTypeFinder.cs +++ /dev/null @@ -1,84 +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. - -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.FindSymbols; - -namespace Microsoft.CodeAnalysis.Remote -{ - // root level service for all Roslyn services - internal partial class CodeAnalysisService : IRemoteDependentTypeFinder - { - private Task> FindTypesAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId typeAndProjectId, - ProjectId[] projectIds, - Func, Task>> func, - CancellationToken cancellationToken) - { - return RunServiceAsync(async () => - { - using (UserOperationBooster.Boost()) - { - var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); - - var symbol = await typeAndProjectId.TryRehydrateAsync( - solution, cancellationToken).ConfigureAwait(false); - - if (!(symbol is INamedTypeSymbol namedType)) - return ImmutableArray.Empty; - - var projects = projectIds?.Select(id => solution.GetProject(id)).ToImmutableHashSet(); - var types = await func(namedType, solution, projects).ConfigureAwait(false); - - return types.SelectAsArray( - t => SerializableSymbolAndProjectId.Dehydrate(solution, t, cancellationToken)); - } - }, cancellationToken); - } - - public Task> FindDerivedClassesAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId typeAndProjectId, - ProjectId[] projectIds, - bool transitive, - CancellationToken cancellationToken) - { - return FindTypesAsync( - solutionInfo, typeAndProjectId, projectIds, - (nt, s, ps) => DependentTypeFinder.FindDerivedClassesAsync(nt, s, ps, transitive, cancellationToken), - cancellationToken); - } - - public Task> FindDerivedInterfacesAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId typeAndProjectId, - ProjectId[] projectIds, - bool transitive, - CancellationToken cancellationToken) - { - return FindTypesAsync( - solutionInfo, typeAndProjectId, projectIds, - (nt, s, ps) => DependentTypeFinder.FindDerivedInterfacesAsync(nt, s, ps, transitive, cancellationToken), - cancellationToken); - } - - public Task> FindImplementingTypesAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId typeAndProjectId, - ProjectId[] projectIds, - bool transitive, - CancellationToken cancellationToken) - { - return FindTypesAsync( - solutionInfo, typeAndProjectId, projectIds, - (nt, s, ps) => DependentTypeFinder.FindImplementingTypesAsync(nt, s, ps, transitive, cancellationToken), - cancellationToken); - } - } -} diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_FindUsages.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_FindUsages.cs deleted file mode 100644 index 91e02fdfbd304..0000000000000 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_FindUsages.cs +++ /dev/null @@ -1,145 +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. - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Editor.FindUsages; -using Microsoft.CodeAnalysis.FindUsages; -using Microsoft.CodeAnalysis.Shared.Utilities; - -namespace Microsoft.CodeAnalysis.Remote -{ - // root level service for all Roslyn services - internal partial class CodeAnalysisService : IRemoteFindUsagesService - { - public Task FindReferencesAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId symbolAndProjectIdArg, - SerializableFindReferencesSearchOptions options, - CancellationToken cancellationToken) - { - return RunServiceAsync(async () => - { - using (UserOperationBooster.Boost()) - { - var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); - var project = solution.GetProject(symbolAndProjectIdArg.ProjectId); - - var symbol = await symbolAndProjectIdArg.TryRehydrateAsync( - solution, cancellationToken).ConfigureAwait(false); - - if (symbol == null) - return; - - var context = new RemoteFindUsageContext(solution, EndPoint, cancellationToken); - await AbstractFindUsagesService.FindReferencesAsync( - context, symbol, project, options.Rehydrate()).ConfigureAwait(false); - } - }, cancellationToken); - } - - public Task FindImplementationsAsync( - PinnedSolutionInfo solutionInfo, - SerializableSymbolAndProjectId symbolAndProjectIdArg, - CancellationToken cancellationToken) - { - return RunServiceAsync(async () => - { - using (UserOperationBooster.Boost()) - { - var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); - var project = solution.GetProject(symbolAndProjectIdArg.ProjectId); - - var symbol = await symbolAndProjectIdArg.TryRehydrateAsync( - solution, cancellationToken).ConfigureAwait(false); - if (symbol == null) - return; - - var context = new RemoteFindUsageContext(solution, EndPoint, cancellationToken); - await AbstractFindUsagesService.FindImplementationsAsync( - symbol, project, context).ConfigureAwait(false); - } - }, cancellationToken); - } - - private class RemoteFindUsageContext : IFindUsagesContext, IStreamingProgressTracker - { - private readonly Solution _solution; - private readonly RemoteEndPoint _endPoint; - private readonly Dictionary _definitionItemToId = new Dictionary(); - - public CancellationToken CancellationToken { get; } - - public RemoteFindUsageContext(Solution solution, RemoteEndPoint endPoint, CancellationToken cancellationToken) - { - _solution = solution; - _endPoint = endPoint; - CancellationToken = cancellationToken; - } - - #region IStreamingProgressTracker - - public Task AddItemsAsync(int count) - => _endPoint.InvokeAsync(nameof(AddItemsAsync), new object[] { count }, CancellationToken); - - public Task ItemCompletedAsync() - => _endPoint.InvokeAsync(nameof(ItemCompletedAsync), Array.Empty(), CancellationToken); - - #endregion - - #region IFindUsagesContext - - public IStreamingProgressTracker ProgressTracker => this; - - public Task ReportMessageAsync(string message) - => _endPoint.InvokeAsync(nameof(ReportMessageAsync), new object[] { message }, CancellationToken); - - public Task ReportProgressAsync(int current, int maximum) - => _endPoint.InvokeAsync(nameof(ReportProgressAsync), new object[] { current, maximum }, CancellationToken); - - public Task SetSearchTitleAsync(string title) - => _endPoint.InvokeAsync(nameof(SetSearchTitleAsync), new object[] { title }, CancellationToken); - - public Task OnDefinitionFoundAsync(DefinitionItem definition) - { - var id = GetOrAddDefinitionItemId(definition); - return _endPoint.InvokeAsync(nameof(OnDefinitionFoundAsync), - new object[] - { - SerializableDefinitionItem.Dehydrate(id, definition), - }, - CancellationToken); - } - - private int GetOrAddDefinitionItemId(DefinitionItem item) - { - lock (_definitionItemToId) - { - if (!_definitionItemToId.TryGetValue(item, out var id)) - { - id = _definitionItemToId.Count; - _definitionItemToId.Add(item, id); - } - - return id; - } - } - - public Task OnReferenceFoundAsync(SourceReferenceItem reference) - { - var definitionItem = GetOrAddDefinitionItemId(reference.Definition); - return _endPoint.InvokeAsync(nameof(OnReferenceFoundAsync), - new object[] - { - SerializableSourceReferenceItem.Dehydrate(definitionItem, reference), - }, - CancellationToken); - } - - #endregion - } - } -} diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeLensReferences/RemoteCodeLensReferencesService.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeLensReferences/RemoteCodeLensReferencesService.cs new file mode 100644 index 0000000000000..2b074d0bfe9a8 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/CodeLensReferences/RemoteCodeLensReferencesService.cs @@ -0,0 +1,122 @@ +// 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 enable + +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeLens; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Remote +{ + internal sealed class RemoteCodeLensReferencesService : BrokeredServiceBase, IRemoteCodeLensReferencesService + { + internal sealed class Factory : FactoryBase + { + protected override IRemoteCodeLensReferencesService CreateService(in ServiceConstructionArguments arguments) + => new RemoteCodeLensReferencesService(arguments); + } + + public RemoteCodeLensReferencesService(in ServiceConstructionArguments arguments) + : base(arguments) + { + } + + private static async ValueTask TryFindNodeAsync(Solution solution, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken) + { + var document = solution.GetDocument(documentId); + if (document == null) + { + return null; + } + + var syntaxRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + return syntaxRoot.FindNode(textSpan); + } + + public ValueTask GetReferenceCountAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, int maxResultCount, CancellationToken cancellationToken) + { + return RunServiceAsync(async cancellationToken => + { + using (Logger.LogBlock(FunctionId.CodeAnalysisService_GetReferenceCountAsync, documentId.ProjectId.DebugName, cancellationToken)) + { + var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); + var syntaxNode = await TryFindNodeAsync(solution, documentId, textSpan, cancellationToken).ConfigureAwait(false); + if (syntaxNode == null) + { + return null; + } + + return await CodeLensReferencesServiceFactory.Instance.GetReferenceCountAsync( + solution, + documentId, + syntaxNode, + maxResultCount, + cancellationToken).ConfigureAwait(false); + } + }, cancellationToken); + } + + public ValueTask?> FindReferenceLocationsAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken) + { + return RunServiceAsync(async cancellationToken => + { + using (Logger.LogBlock(FunctionId.CodeAnalysisService_FindReferenceLocationsAsync, documentId.ProjectId.DebugName, cancellationToken)) + { + var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); + var syntaxNode = await TryFindNodeAsync(solution, documentId, textSpan, cancellationToken).ConfigureAwait(false); + if (syntaxNode == null) + { + return null; + } + + return await CodeLensReferencesServiceFactory.Instance.FindReferenceLocationsAsync( + solution, documentId, syntaxNode, cancellationToken).ConfigureAwait(false); + } + }, cancellationToken); + } + + public ValueTask?> FindReferenceMethodsAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken) + { + return RunServiceAsync(async cancellationToken => + { + using (Logger.LogBlock(FunctionId.CodeAnalysisService_FindReferenceMethodsAsync, documentId.ProjectId.DebugName, cancellationToken)) + { + var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); + var syntaxNode = await TryFindNodeAsync(solution, documentId, textSpan, cancellationToken).ConfigureAwait(false); + if (syntaxNode == null) + { + return null; + } + + return await CodeLensReferencesServiceFactory.Instance.FindReferenceMethodsAsync( + solution, documentId, syntaxNode, cancellationToken).ConfigureAwait(false); + } + }, cancellationToken); + } + + public ValueTask GetFullyQualifiedNameAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken) + { + return RunServiceAsync(async cancellationToken => + { + using (Logger.LogBlock(FunctionId.CodeAnalysisService_GetFullyQualifiedName, documentId.ProjectId.DebugName, cancellationToken)) + { + var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); + var syntaxNode = await TryFindNodeAsync(solution, documentId, textSpan, cancellationToken).ConfigureAwait(false); + if (syntaxNode == null) + { + return null; + } + + return await CodeLensReferencesServiceFactory.Instance.GetFullyQualifiedNameAsync( + solution, documentId, syntaxNode, cancellationToken).ConfigureAwait(false); + } + }, cancellationToken); + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_ConvertTupleToStruct.cs b/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs similarity index 79% rename from src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_ConvertTupleToStruct.cs rename to src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs index c4ce24152653f..bcd14f8fd13be 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_ConvertTupleToStruct.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs @@ -12,17 +12,27 @@ namespace Microsoft.CodeAnalysis.Remote { - // root level service for all Roslyn services - internal partial class CodeAnalysisService : IRemoteConvertTupleToStructCodeRefactoringProvider + internal sealed class RemoteConvertTupleToStructCodeRefactoringService : BrokeredServiceBase, IRemoteConvertTupleToStructCodeRefactoringService { - public Task ConvertToStructAsync( + internal sealed class Factory : FactoryBase + { + protected override IRemoteConvertTupleToStructCodeRefactoringService CreateService(in ServiceConstructionArguments arguments) + => new RemoteConvertTupleToStructCodeRefactoringService(arguments); + } + + public RemoteConvertTupleToStructCodeRefactoringService(in ServiceConstructionArguments arguments) + : base(arguments) + { + } + + public ValueTask ConvertToStructAsync( PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan span, Scope scope, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -39,11 +49,7 @@ public Task ConvertToStructAsync( var renamedToken = await GetRenamedTokenAsync( solution, cleanedSolution, cancellationToken).ConfigureAwait(false); - return new SerializableConvertTupleToStructResult - { - DocumentTextChanges = documentTextChanges, - RenamedToken = renamedToken, - }; + return new SerializableConvertTupleToStructResult(documentTextChanges, renamedToken); } }, cancellationToken); } diff --git a/src/Workspaces/Remote/ServiceHub/Services/DependentTypeFinder/RemoteDependentTypeFinderService.cs b/src/Workspaces/Remote/ServiceHub/Services/DependentTypeFinder/RemoteDependentTypeFinderService.cs new file mode 100644 index 0000000000000..04b9d7636f733 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/DependentTypeFinder/RemoteDependentTypeFinderService.cs @@ -0,0 +1,60 @@ +// 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 enable + +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Remote +{ + internal sealed class RemoteDependentTypeFinderService : BrokeredServiceBase, IRemoteDependentTypeFinderService + { + internal sealed class Factory : FactoryBase + { + protected override IRemoteDependentTypeFinderService CreateService(in ServiceConstructionArguments arguments) + => new RemoteDependentTypeFinderService(arguments); + } + + public RemoteDependentTypeFinderService(in ServiceConstructionArguments arguments) + : base(arguments) + { + } + + public ValueTask> FindTypesAsync( + PinnedSolutionInfo solutionInfo, + SerializableSymbolAndProjectId typeAndProjectId, + ImmutableArray projectIdsOpt, + bool transitive, + DependentTypesKind kind, + CancellationToken cancellationToken) + { + return RunServiceAsync(async cancellationToken => + { + using (UserOperationBooster.Boost()) + { + var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); + + var symbol = await typeAndProjectId.TryRehydrateAsync(solution, cancellationToken).ConfigureAwait(false); + + if (symbol is not INamedTypeSymbol namedType) + return ImmutableArray.Empty; + + var projects = projectIdsOpt.IsDefault ? null : projectIdsOpt.Select(id => solution.GetRequiredProject(id)).ToImmutableHashSet(); + + var types = await DependentTypeFinder.FindTypesInCurrentProcessAsync(namedType, solution, projects, transitive, kind, cancellationToken).ConfigureAwait(false); + + return types.SelectAsArray( + t => SerializableSymbolAndProjectId.Dehydrate(solution, t, cancellationToken)); + } + }, cancellationToken); + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeService.cs b/src/Workspaces/Remote/ServiceHub/Services/DesignerAttributeDiscovery/RemoteDesignerAttributeDiscoveryService.cs similarity index 71% rename from src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeService.cs rename to src/Workspaces/Remote/ServiceHub/Services/DesignerAttributeDiscovery/RemoteDesignerAttributeDiscoveryService.cs index c5cadee93feb3..7f82046e5801c 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DesignerAttributeDiscovery/RemoteDesignerAttributeDiscoveryService.cs @@ -11,17 +11,17 @@ namespace Microsoft.CodeAnalysis.Remote { - internal sealed class RemoteDesignerAttributeService : BrokeredServiceBase, IRemoteDesignerAttributeService + internal sealed class RemoteDesignerAttributeDiscoveryService : BrokeredServiceBase, IRemoteDesignerAttributeDiscoveryService { - internal sealed class Factory : FactoryBase + internal sealed class Factory : FactoryBase { - protected override IRemoteDesignerAttributeService CreateService(in ServiceConstructionArguments arguments, RemoteCallback callback) - => new RemoteDesignerAttributeService(arguments, callback); + protected override IRemoteDesignerAttributeDiscoveryService CreateService(in ServiceConstructionArguments arguments, RemoteCallback callback) + => new RemoteDesignerAttributeDiscoveryService(arguments, callback); } private readonly RemoteCallback _callback; - public RemoteDesignerAttributeService(in ServiceConstructionArguments arguments, RemoteCallback callback) + public RemoteDesignerAttributeDiscoveryService(in ServiceConstructionArguments arguments, RemoteCallback callback) : base(arguments) { _callback = callback; diff --git a/src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeIncrementalAnalyzer.cs b/src/Workspaces/Remote/ServiceHub/Services/DesignerAttributeDiscovery/RemoteDesignerAttributeIncrementalAnalyzer.cs similarity index 100% rename from src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeIncrementalAnalyzer.cs rename to src/Workspaces/Remote/ServiceHub/Services/DesignerAttributeDiscovery/RemoteDesignerAttributeIncrementalAnalyzer.cs diff --git a/src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeIncrementalAnalyzerProvider.cs b/src/Workspaces/Remote/ServiceHub/Services/DesignerAttributeDiscovery/RemoteDesignerAttributeIncrementalAnalyzerProvider.cs similarity index 100% rename from src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeIncrementalAnalyzerProvider.cs rename to src/Workspaces/Remote/ServiceHub/Services/DesignerAttributeDiscovery/RemoteDesignerAttributeIncrementalAnalyzerProvider.cs diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/Diagnostics/DiagnosticComputer.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs similarity index 81% rename from src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/Diagnostics/DiagnosticComputer.cs rename to src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs index a92a030e50822..9789ab33305e2 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/Diagnostics/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs @@ -61,7 +61,7 @@ public DiagnosticComputer( _performanceTracker = project.IsFromPrimaryBranch() ? project.Solution.Workspace.Services.GetService() : null; } - public async Task> GetDiagnosticsAsync( + public async Task GetDiagnosticsAsync( IEnumerable analyzerIds, bool reportSuppressedDiagnostics, bool logPerformanceInfo, @@ -74,12 +74,13 @@ public async Task.Empty; + return SerializableDiagnosticAnalysisResults.Empty; } var cacheService = _project.Solution.Workspace.Services.GetRequiredService(); using var cache = cacheService.EnableCaching(_project.Id); var skippedAnalyzersInfo = _project.GetSkippedAnalyzersInfo(_analyzerInfoCache); + try { return await AnalyzeAsync(compilationWithAnalyzers, analyzerToIdMap, analyzers, skippedAnalyzersInfo, @@ -93,7 +94,7 @@ public async Task> AnalyzeAsync( + private async Task AnalyzeAsync( CompilationWithAnalyzers compilationWithAnalyzers, BidirectionalMap analyzerToIdMap, ImmutableArray analyzers, @@ -123,43 +124,64 @@ private async Task GetAnalyzerId(analyzerToIdMap, kv.Key), kv => kv.Value); var telemetry = getTelemetryInfo ? GetTelemetryInfo(analysisResult, analyzers, analyzerToIdMap) - : ImmutableDictionary.Empty; - return DiagnosticAnalysisResultMap.Create(result, telemetry); + : ImmutableArray<(string analyzerId, AnalyzerTelemetryInfo)>.Empty; - static ImmutableDictionary GetTelemetryInfo( - AnalysisResult analysisResult, - ImmutableArray analyzers, - BidirectionalMap analyzerToIdMap) + return new SerializableDiagnosticAnalysisResults(Dehydrate(builderMap, analyzerToIdMap), telemetry); + } + + private static ImmutableArray<(string analyzerId, SerializableDiagnosticMap diagnosticMap)> Dehydrate( + ImmutableDictionary builderMap, + BidirectionalMap analyzerToIdMap) + { + using var _ = ArrayBuilder<(string analyzerId, SerializableDiagnosticMap diagnosticMap)>.GetInstance(out var diagnostics); + + foreach (var (analyzer, analyzerResults) in builderMap) { - Func shouldInclude; - if (analyzers.Length < analysisResult.AnalyzerTelemetryInfo.Count) - { - // Filter the telemetry info to the executed analyzers. - using var _ = PooledHashSet.GetInstance(out var analyzersSet); - analyzersSet.AddRange(analyzers); + var analyzerId = GetAnalyzerId(analyzerToIdMap, analyzer); + + diagnostics.Add((analyzerId, + new SerializableDiagnosticMap( + analyzerResults.SyntaxLocals.SelectAsArray(entry => (entry.Key, entry.Value)), + analyzerResults.SemanticLocals.SelectAsArray(entry => (entry.Key, entry.Value)), + analyzerResults.NonLocals.SelectAsArray(entry => (entry.Key, entry.Value)), + analyzerResults.Others))); + } - shouldInclude = analyzer => analyzersSet.Contains(analyzer); - } - else - { - shouldInclude = _ => true; - } + return diagnostics.ToImmutable(); + } - var telemetryBuilder = ImmutableDictionary.CreateBuilder(); - foreach (var (analyzer, analyzerTelemetry) in analysisResult.AnalyzerTelemetryInfo) + private static ImmutableArray<(string analyzerId, AnalyzerTelemetryInfo)> GetTelemetryInfo( + AnalysisResult analysisResult, + ImmutableArray analyzers, + BidirectionalMap analyzerToIdMap) + { + Func shouldInclude; + if (analyzers.Length < analysisResult.AnalyzerTelemetryInfo.Count) + { + // Filter the telemetry info to the executed analyzers. + using var _1 = PooledHashSet.GetInstance(out var analyzersSet); + analyzersSet.AddRange(analyzers); + + shouldInclude = analyzer => analyzersSet.Contains(analyzer); + } + else + { + shouldInclude = _ => true; + } + + using var _2 = ArrayBuilder<(string analyzerId, AnalyzerTelemetryInfo)>.GetInstance(out var telemetryBuilder); + foreach (var (analyzer, analyzerTelemetry) in analysisResult.AnalyzerTelemetryInfo) + { + if (shouldInclude(analyzer)) { - if (shouldInclude(analyzer)) - { - var analyzerId = GetAnalyzerId(analyzerToIdMap, analyzer); - telemetryBuilder.Add(analyzerId, analyzerTelemetry); - } + var analyzerId = GetAnalyzerId(analyzerToIdMap, analyzer); + telemetryBuilder.Add((analyzerId, analyzerTelemetry)); } - - return telemetryBuilder.ToImmutable(); } + + return telemetryBuilder.ToImmutable(); } private static string GetAnalyzerId(BidirectionalMap analyzerMap, DiagnosticAnalyzer analyzer) diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/Diagnostics/IPerformanceTrackerService.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/IPerformanceTrackerService.cs similarity index 100% rename from src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/Diagnostics/IPerformanceTrackerService.cs rename to src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/IPerformanceTrackerService.cs diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/Diagnostics/PerformanceQueue.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs similarity index 100% rename from src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/Diagnostics/PerformanceQueue.cs rename to src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/Diagnostics/PerformanceTrackerService.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs similarity index 100% rename from src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/Diagnostics/PerformanceTrackerService.cs rename to src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs index 7b7fae029c22a..db6e958bb1924 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs @@ -5,6 +5,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; @@ -35,12 +36,12 @@ public RemoteDiagnosticAnalyzerService(in ServiceConstructionArguments arguments /// since in proc and out of proc runs quite differently due to concurrency and due to possible amount of data /// that needs to pass through between processes /// - public ValueTask CalculateDiagnosticsAsync(PinnedSolutionInfo solutionInfo, DiagnosticArguments arguments, Stream outputStream, CancellationToken cancellationToken) + public ValueTask CalculateDiagnosticsAsync(PinnedSolutionInfo solutionInfo, DiagnosticArguments arguments, CancellationToken cancellationToken) { // Complete RPC right away so the client can start reading from the stream. // The fire-and forget task starts writing to the output stream and the client will read it until it reads all expected data. - _ = RunServiceAsync(async cancellationToken => + return RunServiceAsync(async cancellationToken => { using (RoslynLogger.LogBlock(FunctionId.CodeAnalysisService_CalculateDiagnosticsAsync, arguments.ProjectId.DebugName, cancellationToken)) using (arguments.IsHighPriority ? UserOperationBooster.Boost() : default) @@ -61,15 +62,15 @@ public ValueTask CalculateDiagnosticsAsync(PinnedSolutionInfo solutionInfo, Diag getTelemetryInfo: arguments.GetTelemetryInfo, cancellationToken).ConfigureAwait(false); - using var writer = new ObjectWriter(outputStream, leaveOpen: false, cancellationToken); - var (diagnostics, telemetry) = DiagnosticResultSerializer.WriteDiagnosticAnalysisResults(writer, documentAnalysisKind, result, cancellationToken); - // save log for debugging - Log(TraceEventType.Information, $"diagnostics: {diagnostics}, telemetry: {telemetry}"); + var diagnosticCount = result.Diagnostics.Sum( + entry => entry.diagnosticMap.Syntax.Length + entry.diagnosticMap.Semantic.Length + entry.diagnosticMap.NonLocal.Length + entry.diagnosticMap.Other.Length); + + Log(TraceEventType.Information, $"diagnostics: {diagnosticCount}, telemetry: {result.Telemetry.Length}"); + + return result; } }, cancellationToken); - - return default; } public ValueTask ReportAnalyzerPerformanceAsync(ImmutableArray snapshot, int unitCount, CancellationToken cancellationToken) diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_DocumentHighlights.cs b/src/Workspaces/Remote/ServiceHub/Services/DocumentHighlights/RemoteDocumentHighlightsService.cs similarity index 64% rename from src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_DocumentHighlights.cs rename to src/Workspaces/Remote/ServiceHub/Services/DocumentHighlights/RemoteDocumentHighlightsService.cs index a2b4601e6c50f..d4caad616c126 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_DocumentHighlights.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DocumentHighlights/RemoteDocumentHighlightsService.cs @@ -13,13 +13,23 @@ namespace Microsoft.CodeAnalysis.Remote { - // root level service for all Roslyn services - internal partial class CodeAnalysisService : IRemoteDocumentHighlights + internal sealed class RemoteDocumentHighlightsService : BrokeredServiceBase, IRemoteDocumentHighlightsService { - public Task> GetDocumentHighlightsAsync( - PinnedSolutionInfo solutionInfo, DocumentId documentId, int position, DocumentId[] documentIdsToSearch, CancellationToken cancellationToken) + internal sealed class Factory : FactoryBase { - return RunServiceAsync(async () => + protected override IRemoteDocumentHighlightsService CreateService(in ServiceConstructionArguments arguments) + => new RemoteDocumentHighlightsService(arguments); + } + + public RemoteDocumentHighlightsService(in ServiceConstructionArguments arguments) + : base(arguments) + { + } + + public ValueTask> GetDocumentHighlightsAsync( + PinnedSolutionInfo solutionInfo, DocumentId documentId, int position, ImmutableArray documentIdsToSearch, CancellationToken cancellationToken) + { + return RunServiceAsync(async cancellationToken => { // NOTE: In projection scenarios, we might get a set of documents to search // that are not all the same language and might not exist in the OOP process @@ -34,7 +44,7 @@ public Task> GetDocumentHighlightsAsync( var result = await service.GetDocumentHighlightsAsync( document, position, documentsToSearch, cancellationToken).ConfigureAwait(false); - return (IList)result.SelectAsArray(SerializableDocumentHighlights.Dehydrate); + return result.SelectAsArray(SerializableDocumentHighlights.Dehydrate); }, cancellationToken); } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_EncapsulateField.cs b/src/Workspaces/Remote/ServiceHub/Services/EncapsulateField/RemoteEncapsulateFieldService.cs similarity index 72% rename from src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_EncapsulateField.cs rename to src/Workspaces/Remote/ServiceHub/Services/EncapsulateField/RemoteEncapsulateFieldService.cs index d309993ea20de..c3fff04d4c712 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_EncapsulateField.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/EncapsulateField/RemoteEncapsulateFieldService.cs @@ -13,16 +13,27 @@ namespace Microsoft.CodeAnalysis.Remote { - internal partial class CodeAnalysisService : IRemoteEncapsulateFieldService + internal sealed class RemoteEncapsulateFieldService : BrokeredServiceBase, IRemoteEncapsulateFieldService { - public Task<(DocumentId, TextChange[])[]> EncapsulateFieldsAsync( + internal sealed class Factory : FactoryBase + { + protected override IRemoteEncapsulateFieldService CreateService(in ServiceConstructionArguments arguments) + => new RemoteEncapsulateFieldService(arguments); + } + + public RemoteEncapsulateFieldService(in ServiceConstructionArguments arguments) + : base(arguments) + { + } + + public ValueTask)>> EncapsulateFieldsAsync( PinnedSolutionInfo solutionInfo, DocumentId documentId, ImmutableArray fieldSymbolKeys, bool updateReferences, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -36,7 +47,7 @@ internal partial class CodeAnalysisService : IRemoteEncapsulateFieldService { var resolved = SymbolKey.ResolveString(key, compilation, cancellationToken: cancellationToken).GetAnySymbol() as IFieldSymbol; if (resolved == null) - return Array.Empty<(DocumentId, TextChange[])>(); + return ImmutableArray<(DocumentId, ImmutableArray)>.Empty; fields.Add(resolved); } diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_ExtensionMethodFiltering.cs b/src/Workspaces/Remote/ServiceHub/Services/ExtensionMethodImportCompletion/RemoteExtensionMethodImportCompletionService.cs similarity index 60% rename from src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_ExtensionMethodFiltering.cs rename to src/Workspaces/Remote/ServiceHub/Services/ExtensionMethodImportCompletion/RemoteExtensionMethodImportCompletionService.cs index cdf9ef9bf5cda..bff5264a82c83 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_ExtensionMethodFiltering.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ExtensionMethodImportCompletion/RemoteExtensionMethodImportCompletionService.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion.Providers; @@ -14,18 +15,29 @@ namespace Microsoft.CodeAnalysis.Remote { - internal partial class CodeAnalysisService : IRemoteExtensionMethodImportCompletionService + internal sealed class RemoteExtensionMethodImportCompletionService : BrokeredServiceBase, IRemoteExtensionMethodImportCompletionService { - public Task<(IList, StatisticCounter)> GetUnimportedExtensionMethodsAsync( + internal sealed class Factory : FactoryBase + { + protected override IRemoteExtensionMethodImportCompletionService CreateService(in ServiceConstructionArguments arguments) + => new RemoteExtensionMethodImportCompletionService(arguments); + } + + public RemoteExtensionMethodImportCompletionService(in ServiceConstructionArguments arguments) + : base(arguments) + { + } + + public ValueTask GetUnimportedExtensionMethodsAsync( PinnedSolutionInfo solutionInfo, DocumentId documentId, int position, string receiverTypeSymbolKeyData, - string[] namespaceInScope, + ImmutableArray namespaceInScope, bool forceIndexCreation, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -39,12 +51,11 @@ internal partial class CodeAnalysisService : IRemoteExtensionMethodImportComplet var syntaxFacts = document.GetRequiredLanguageService(); var namespaceInScopeSet = new HashSet(namespaceInScope, syntaxFacts.StringComparer); - var (items, counter) = await ExtensionMethodImportCompletionHelper.GetUnimportedExtensionMethodsInCurrentProcessAsync( + return await ExtensionMethodImportCompletionHelper.GetUnimportedExtensionMethodsInCurrentProcessAsync( document, position, receiverTypeSymbol, namespaceInScopeSet, forceIndexCreation, cancellationToken).ConfigureAwait(false); - return ((IList)items, counter); } - return (Array.Empty(), new StatisticCounter()); + return new SerializableUnimportedExtensionMethods(ImmutableArray.Empty, isPartialResult: false, getSymbolsTicks: 0, createItemsTicks: 0); } }, cancellationToken); } diff --git a/src/Workspaces/Remote/ServiceHub/Services/FindUsages/RemoteFindUsagesService.cs b/src/Workspaces/Remote/ServiceHub/Services/FindUsages/RemoteFindUsagesService.cs new file mode 100644 index 0000000000000..1290119034611 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/FindUsages/RemoteFindUsagesService.cs @@ -0,0 +1,150 @@ +// 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.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.FindUsages; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Shared.Utilities; + +namespace Microsoft.CodeAnalysis.Remote +{ + internal sealed class RemoteFindUsagesService : BrokeredServiceBase, IRemoteFindUsagesService + { + internal sealed class Factory : FactoryBase + { + protected override IRemoteFindUsagesService CreateService(in ServiceConstructionArguments arguments, RemoteCallback callback) + => new RemoteFindUsagesService(arguments, callback); + } + + private readonly RemoteCallback _callback; + + public RemoteFindUsagesService(in ServiceConstructionArguments arguments, RemoteCallback callback) + : base(arguments) + { + _callback = callback; + } + + public ValueTask FindReferencesAsync( + PinnedSolutionInfo solutionInfo, + SerializableSymbolAndProjectId symbolAndProjectId, + FindReferencesSearchOptions options, + CancellationToken cancellationToken) + { + return RunServiceAsync(async cancellationToken => + { + using (UserOperationBooster.Boost()) + { + var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); + var project = solution.GetProject(symbolAndProjectId.ProjectId); + + var symbol = await symbolAndProjectId.TryRehydrateAsync( + solution, cancellationToken).ConfigureAwait(false); + + if (symbol == null) + return; + + var context = new RemoteFindUsageContext(_callback, cancellationToken); + await AbstractFindUsagesService.FindReferencesAsync( + context, symbol, project, options).ConfigureAwait(false); + } + }, cancellationToken); + } + + public ValueTask FindImplementationsAsync( + PinnedSolutionInfo solutionInfo, + SerializableSymbolAndProjectId symbolAndProjectId, + CancellationToken cancellationToken) + { + return RunServiceAsync(async cancellationToken => + { + using (UserOperationBooster.Boost()) + { + var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); + var project = solution.GetProject(symbolAndProjectId.ProjectId); + + var symbol = await symbolAndProjectId.TryRehydrateAsync( + solution, cancellationToken).ConfigureAwait(false); + if (symbol == null) + return; + + var context = new RemoteFindUsageContext(_callback, cancellationToken); + await AbstractFindUsagesService.FindImplementationsAsync( + symbol, project, context).ConfigureAwait(false); + } + }, cancellationToken); + } + + private sealed class RemoteFindUsageContext : IFindUsagesContext, IStreamingProgressTracker + { + private readonly RemoteCallback _callback; + private readonly Dictionary _definitionItemToId = new Dictionary(); + + public CancellationToken CancellationToken { get; } + + public RemoteFindUsageContext(RemoteCallback callback, CancellationToken cancellationToken) + { + _callback = callback; + CancellationToken = cancellationToken; + } + + #region IStreamingProgressTracker + + public ValueTask AddItemsAsync(int count) + => _callback.InvokeAsync((callback, cancellationToken) => callback.AddItemsAsync(count), CancellationToken); + + public ValueTask ItemCompletedAsync() + => _callback.InvokeAsync((callback, cancellationToken) => callback.ItemCompletedAsync(), CancellationToken); + + #endregion + + #region IFindUsagesContext + + public IStreamingProgressTracker ProgressTracker => this; + + public ValueTask ReportMessageAsync(string message) + => _callback.InvokeAsync((callback, cancellationToken) => callback.ReportMessageAsync(message), CancellationToken); + + [Obsolete] + public ValueTask ReportProgressAsync(int current, int maximum) + => _callback.InvokeAsync((callback, cancellationToken) => callback.ReportProgressAsync(current, maximum), CancellationToken); + + public ValueTask SetSearchTitleAsync(string title) + => _callback.InvokeAsync((callback, cancellationToken) => callback.SetSearchTitleAsync(title), CancellationToken); + + public ValueTask OnDefinitionFoundAsync(DefinitionItem definition) + { + var id = GetOrAddDefinitionItemId(definition); + var dehydratedDefinition = SerializableDefinitionItem.Dehydrate(id, definition); + return _callback.InvokeAsync((callback, cancellationToken) => callback.OnDefinitionFoundAsync(dehydratedDefinition), CancellationToken); + } + + private int GetOrAddDefinitionItemId(DefinitionItem item) + { + lock (_definitionItemToId) + { + if (!_definitionItemToId.TryGetValue(item, out var id)) + { + id = _definitionItemToId.Count; + _definitionItemToId.Add(item, id); + } + + return id; + } + } + + public ValueTask OnReferenceFoundAsync(SourceReferenceItem reference) + { + var definitionItem = GetOrAddDefinitionItemId(reference.Definition); + var dehydratedReference = SerializableSourceReferenceItem.Dehydrate(definitionItem, reference); + return _callback.InvokeAsync((callback, cancellationToken) => callback.OnReferenceFoundAsync(dehydratedReference), CancellationToken); + } + + #endregion + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_GlobalNotifications.cs b/src/Workspaces/Remote/ServiceHub/Services/GlobalNotificationDelivery/RemoteGlobalNotificationDeliveryService.cs similarity index 52% rename from src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_GlobalNotifications.cs rename to src/Workspaces/Remote/ServiceHub/Services/GlobalNotificationDelivery/RemoteGlobalNotificationDeliveryService.cs index 49b801e4d9b6d..c46db0ab9dccd 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_GlobalNotifications.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/GlobalNotificationDelivery/RemoteGlobalNotificationDeliveryService.cs @@ -6,35 +6,49 @@ using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Remote.Services; namespace Microsoft.CodeAnalysis.Remote { - internal partial class CodeAnalysisService : IRemoteGlobalNotificationDeliveryService + internal sealed class RemoteGlobalNotificationDeliveryService : BrokeredServiceBase, IRemoteGlobalNotificationDeliveryService { + internal sealed class Factory : FactoryBase + { + protected override IRemoteGlobalNotificationDeliveryService CreateService(in ServiceConstructionArguments arguments) + => new RemoteGlobalNotificationDeliveryService(arguments); + } + + public RemoteGlobalNotificationDeliveryService(in ServiceConstructionArguments arguments) + : base(arguments) + { + } + /// /// Remote API. /// - public void OnGlobalOperationStarted() + public ValueTask OnGlobalOperationStartedAsync(CancellationToken cancellationToken) { - RunService(() => + return RunServiceAsync(cancellationToken => { var globalOperationNotificationService = GetGlobalOperationNotificationService(); globalOperationNotificationService?.OnStarted(); - }, CancellationToken.None); + return default; + }, cancellationToken); } /// /// Remote API. /// - public void OnGlobalOperationStopped(IReadOnlyList operations, bool cancelled) + public ValueTask OnGlobalOperationStoppedAsync(IReadOnlyList operations, bool cancelled, CancellationToken cancellationToken) { - RunService(() => + return RunServiceAsync(cancellationToken => { var globalOperationNotificationService = GetGlobalOperationNotificationService(); globalOperationNotificationService?.OnStopped(operations, cancelled); - }, CancellationToken.None); + return default; + }, cancellationToken); } private RemoteGlobalOperationNotificationService? GetGlobalOperationNotificationService() diff --git a/src/Workspaces/Remote/ServiceHub/Services/MissingImportDiscovery/RemoteMissingImportDiscoveryService.cs b/src/Workspaces/Remote/ServiceHub/Services/MissingImportDiscovery/RemoteMissingImportDiscoveryService.cs new file mode 100644 index 0000000000000..40812914f3741 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/MissingImportDiscovery/RemoteMissingImportDiscoveryService.cs @@ -0,0 +1,86 @@ +// 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.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.AddImport; +using Microsoft.CodeAnalysis.Packaging; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.SymbolSearch; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Remote +{ + internal sealed class RemoteMissingImportDiscoveryService : BrokeredServiceBase, IRemoteMissingImportDiscoveryService + { + internal sealed class Factory : FactoryBase + { + protected override IRemoteMissingImportDiscoveryService CreateService(in ServiceConstructionArguments arguments, RemoteCallback callback) + => new RemoteMissingImportDiscoveryService(arguments, callback); + } + + private readonly RemoteCallback _callback; + + public RemoteMissingImportDiscoveryService(in ServiceConstructionArguments arguments, RemoteCallback callback) + : base(arguments) + { + _callback = callback; + } + + public ValueTask> GetFixesAsync( + PinnedSolutionInfo solutionInfo, DocumentId documentId, TextSpan span, string diagnosticId, int maxResults, bool placeSystemNamespaceFirst, + bool searchReferenceAssemblies, ImmutableArray packageSources, CancellationToken cancellationToken) + { + return RunServiceAsync(async cancellationToken => + { + using (UserOperationBooster.Boost()) + { + var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); + var document = solution.GetDocument(documentId); + + var service = document.GetLanguageService(); + + var symbolSearchService = new SymbolSearchService(_callback); + + var result = await service.GetFixesAsync( + document, span, diagnosticId, maxResults, placeSystemNamespaceFirst, + symbolSearchService, searchReferenceAssemblies, + packageSources, cancellationToken).ConfigureAwait(false); + + return result; + } + }, cancellationToken); + } + + /// + /// Provides an implementation of the on the remote side so that + /// Add-Import can find results in nuget packages/reference assemblies. This works + /// by remoting *from* the OOP server back to the host, which can then forward this + /// appropriately to wherever the real is running. This is necessary + /// because it's not guaranteed that the real will be running in + /// the same process that is supplying the . + /// + /// Ideally we would not need to bounce back to the host for this. + /// + private sealed class SymbolSearchService : ISymbolSearchService + { + private readonly RemoteCallback _callback; + + public SymbolSearchService(RemoteCallback callback) + { + _callback = callback; + } + + public ValueTask> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken) + => _callback.InvokeAsync((callback, cancellationToken) => callback.FindPackagesWithTypeAsync(source, name, arity, cancellationToken), cancellationToken); + + public ValueTask> FindPackagesWithAssemblyAsync(string source, string assemblyName, CancellationToken cancellationToken) + => _callback.InvokeAsync((callback, cancellationToken) => callback.FindPackagesWithAssemblyAsync(source, assemblyName, cancellationToken), cancellationToken); + + public ValueTask> FindReferenceAssembliesWithTypeAsync(string name, int arity, CancellationToken cancellationToken) + => _callback.InvokeAsync((callback, cancellationToken) => callback.FindReferenceAssembliesWithTypeAsync(name, arity, cancellationToken), cancellationToken); + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_NavigateTo.cs b/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs similarity index 59% rename from src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_NavigateTo.cs rename to src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs index 911084e0b4277..5d86eacbc63eb 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_NavigateTo.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs @@ -11,12 +11,27 @@ namespace Microsoft.CodeAnalysis.Remote { - internal partial class CodeAnalysisService : IRemoteNavigateToSearchService + internal sealed class RemoteNavigateToSearchService : BrokeredServiceBase, IRemoteNavigateToSearchService { - public Task> SearchDocumentAsync( - PinnedSolutionInfo solutionInfo, DocumentId documentId, string searchPattern, string[] kinds, CancellationToken cancellationToken) + internal sealed class Factory : FactoryBase { - return RunServiceAsync(async () => + protected override IRemoteNavigateToSearchService CreateService(in ServiceConstructionArguments arguments) + => new RemoteNavigateToSearchService(arguments); + } + + public RemoteNavigateToSearchService(in ServiceConstructionArguments arguments) + : base(arguments) + { + } + + public ValueTask> SearchDocumentAsync( + PinnedSolutionInfo solutionInfo, + DocumentId documentId, + string searchPattern, + ImmutableArray kinds, + CancellationToken cancellationToken) + { + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -31,10 +46,15 @@ public Task> SearchDocumentAsync( }, cancellationToken); } - public Task> SearchProjectAsync( - PinnedSolutionInfo solutionInfo, ProjectId projectId, DocumentId[] priorityDocumentIds, string searchPattern, string[] kinds, CancellationToken cancellationToken) + public ValueTask> SearchProjectAsync( + PinnedSolutionInfo solutionInfo, + ProjectId projectId, + ImmutableArray priorityDocumentIds, + string searchPattern, + ImmutableArray kinds, + CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -52,7 +72,7 @@ public Task> SearchProjectAsync( }, cancellationToken); } - private static IList Convert( + private static ImmutableArray Convert( ImmutableArray result) { return result.SelectAsArray(SerializableNavigateToSearchResult.Dehydrate); diff --git a/src/Workspaces/Remote/ServiceHub/Services/ProjectTelemetry/RemoteProjectTelemetryIncrementalAnalyzer.cs b/src/Workspaces/Remote/ServiceHub/Services/ProjectTelemetry/RemoteProjectTelemetryIncrementalAnalyzer.cs index 87aac48c8ec9a..b5a01c19f82d3 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/ProjectTelemetry/RemoteProjectTelemetryIncrementalAnalyzer.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ProjectTelemetry/RemoteProjectTelemetryIncrementalAnalyzer.cs @@ -46,16 +46,14 @@ public override async Task AnalyzeProjectAsync(Project project, bool semanticsCh var documentsCount = project.DocumentIds.Count; var additionalDocumentsCount = project.AdditionalDocumentIds.Count; - var info = new ProjectTelemetryData - { - ProjectId = projectId, - Language = language, - AnalyzerReferencesCount = analyzerReferencesCount, - ProjectReferencesCount = projectReferencesCount, - MetadataReferencesCount = metadataReferencesCount, - DocumentsCount = documentsCount, - AdditionalDocumentsCount = additionalDocumentsCount, - }; + var info = new ProjectTelemetryData( + projectId: projectId, + language: language, + analyzerReferencesCount: analyzerReferencesCount, + projectReferencesCount: projectReferencesCount, + metadataReferencesCount: metadataReferencesCount, + documentsCount: documentsCount, + additionalDocumentsCount: additionalDocumentsCount); lock (_gate) { diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_Renamer.cs b/src/Workspaces/Remote/ServiceHub/Services/Renamer/RemoteRenamerService.cs similarity index 77% rename from src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_Renamer.cs rename to src/Workspaces/Remote/ServiceHub/Services/Renamer/RemoteRenamerService.cs index 7fd96b6e85c1d..ac299ba7c92e8 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_Renamer.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/Renamer/RemoteRenamerService.cs @@ -13,18 +13,28 @@ namespace Microsoft.CodeAnalysis.Remote { - // root level service for all Roslyn services - internal partial class CodeAnalysisService : IRemoteRenamer + internal sealed class RemoteRenamerService : BrokeredServiceBase, IRemoteRenamerService { - public Task RenameSymbolAsync( + internal sealed class Factory : FactoryBase + { + protected override IRemoteRenamerService CreateService(in ServiceConstructionArguments arguments) + => new RemoteRenamerService(arguments); + } + + public RemoteRenamerService(in ServiceConstructionArguments arguments) + : base(arguments) + { + } + + public ValueTask RenameSymbolAsync( PinnedSolutionInfo solutionInfo, SerializableSymbolAndProjectId symbolAndProjectId, string newName, SerializableRenameOptionSet options, - SerializableSymbolAndProjectId[] nonConflictSymbolIds, + ImmutableArray nonConflictSymbolIds, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -46,13 +56,13 @@ internal partial class CodeAnalysisService : IRemoteRenamer }, cancellationToken); } - public Task FindRenameLocationsAsync( + public ValueTask FindRenameLocationsAsync( PinnedSolutionInfo solutionInfo, SerializableSymbolAndProjectId symbolAndProjectId, SerializableRenameOptionSet options, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -71,14 +81,14 @@ internal partial class CodeAnalysisService : IRemoteRenamer }, cancellationToken); } - public Task ResolveConflictsAsync( + public ValueTask ResolveConflictsAsync( PinnedSolutionInfo solutionInfo, SerializableRenameLocations renameLocationSet, string replacementText, - SerializableSymbolAndProjectId[] nonConflictSymbolIds, + ImmutableArray nonConflictSymbolIds, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -100,10 +110,12 @@ internal partial class CodeAnalysisService : IRemoteRenamer } [return: NotNullIfNotNull("nonConflictSymbolIds")] - private static async Task?> GetNonConflictSymbolsAsync(Solution solution, SerializableSymbolAndProjectId[]? nonConflictSymbolIds, CancellationToken cancellationToken) + private static async Task?> GetNonConflictSymbolsAsync(Solution solution, ImmutableArray nonConflictSymbolIds, CancellationToken cancellationToken) { - if (nonConflictSymbolIds == null) + if (nonConflictSymbolIds.IsDefault) + { return null; + } var builder = ImmutableHashSet.CreateBuilder(); foreach (var id in nonConflictSymbolIds) diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_SymbolFinder.cs b/src/Workspaces/Remote/ServiceHub/Services/SymbolFinder/RemoteSymbolFinderService.cs similarity index 58% rename from src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_SymbolFinder.cs rename to src/Workspaces/Remote/ServiceHub/Services/SymbolFinder/RemoteSymbolFinderService.cs index b48c9310ccefa..94097cbd89237 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysis/CodeAnalysisService_SymbolFinder.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SymbolFinder/RemoteSymbolFinderService.cs @@ -15,17 +15,30 @@ namespace Microsoft.CodeAnalysis.Remote { - // root level service for all Roslyn services - internal partial class CodeAnalysisService : IRemoteSymbolFinder + internal sealed class RemoteSymbolFinderService : BrokeredServiceBase, IRemoteSymbolFinderService { - public Task FindReferencesAsync( + internal sealed class Factory : FactoryBase + { + protected override IRemoteSymbolFinderService CreateService(in ServiceConstructionArguments arguments, RemoteCallback callback) + => new RemoteSymbolFinderService(arguments, callback); + } + + private readonly RemoteCallback _callback; + + public RemoteSymbolFinderService(in ServiceConstructionArguments arguments, RemoteCallback callback) + : base(arguments) + { + _callback = callback; + } + + public ValueTask FindReferencesAsync( PinnedSolutionInfo solutionInfo, SerializableSymbolAndProjectId symbolAndProjectIdArg, - DocumentId[] documentArgs, - SerializableFindReferencesSearchOptions options, + ImmutableArray documentArgs, + FindReferencesSearchOptions options, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -34,7 +47,7 @@ public Task FindReferencesAsync( var symbol = await symbolAndProjectIdArg.TryRehydrateAsync( solution, cancellationToken).ConfigureAwait(false); - var progressCallback = new FindReferencesProgressCallback(solution, EndPoint, cancellationToken); + var progressCallback = new FindReferencesProgressCallback(solution, _callback, cancellationToken); if (symbol == null) { @@ -47,27 +60,26 @@ public Task FindReferencesAsync( // that are not all the same language and might not exist in the OOP process // (like the JS parts of a .cshtml file). Filter them out here. This will // need to be revisited if we someday support FAR between these languages. - var documents = documentArgs?.Select(solution.GetDocument) - .WhereNotNull() - .ToImmutableHashSet(); + var documents = documentArgs.IsDefault ? null : + documentArgs.Select(solution.GetDocument).WhereNotNull().ToImmutableHashSet(); await SymbolFinder.FindReferencesInCurrentProcessAsync( symbol, solution, progressCallback, - documents, options.Rehydrate(), cancellationToken).ConfigureAwait(false); + documents, options, cancellationToken).ConfigureAwait(false); } }, cancellationToken); } - public Task FindLiteralReferencesAsync(PinnedSolutionInfo solutionInfo, object value, TypeCode typeCode, CancellationToken cancellationToken) + public ValueTask FindLiteralReferencesAsync(PinnedSolutionInfo solutionInfo, object value, TypeCode typeCode, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { var convertedType = System.Convert.ChangeType(value, typeCode); var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false); - var progressCallback = new FindLiteralReferencesProgressCallback(EndPoint, cancellationToken); + var progressCallback = new FindLiteralReferencesProgressCallback(_callback, cancellationToken); await SymbolFinder.FindLiteralReferencesInCurrentProcessAsync( convertedType, solution, progressCallback, cancellationToken).ConfigureAwait(false); } @@ -84,7 +96,7 @@ private static ImmutableArray Convert(ImmutableA return result.ToImmutable(); } - public Task> FindAllDeclarationsWithNormalQueryAsync( + public ValueTask> FindAllDeclarationsWithNormalQueryAsync( PinnedSolutionInfo solutionInfo, ProjectId projectId, string name, @@ -92,7 +104,7 @@ public Task> FindAllDeclarationsW SymbolFilter criteria, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -109,14 +121,14 @@ public Task> FindAllDeclarationsW }, cancellationToken); } - public Task> FindSolutionSourceDeclarationsWithNormalQueryAsync( + public ValueTask> FindSolutionSourceDeclarationsWithNormalQueryAsync( PinnedSolutionInfo solutionInfo, string name, bool ignoreCase, SymbolFilter criteria, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -129,7 +141,7 @@ public Task> FindSolutionSourceDe }, cancellationToken); } - public Task> FindProjectSourceDeclarationsWithNormalQueryAsync( + public ValueTask> FindProjectSourceDeclarationsWithNormalQueryAsync( PinnedSolutionInfo solutionInfo, ProjectId projectId, string name, @@ -137,7 +149,7 @@ public Task> FindProjectSourceDec SymbolFilter criteria, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -152,10 +164,10 @@ public Task> FindProjectSourceDec }, cancellationToken); } - public Task> FindSolutionSourceDeclarationsWithPatternAsync( + public ValueTask> FindSolutionSourceDeclarationsWithPatternAsync( PinnedSolutionInfo solutionInfo, string pattern, SymbolFilter criteria, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -169,10 +181,10 @@ public Task> FindSolutionSourceDe }, cancellationToken); } - public Task> FindProjectSourceDeclarationsWithPatternAsync( + public ValueTask> FindProjectSourceDeclarationsWithPatternAsync( PinnedSolutionInfo solutionInfo, ProjectId projectId, string pattern, SymbolFilter criteria, CancellationToken cancellationToken) { - return RunServiceAsync(async () => + return RunServiceAsync(async cancellationToken => { using (UserOperationBooster.Boost()) { @@ -189,76 +201,75 @@ public Task> FindProjectSourceDec private sealed class FindLiteralReferencesProgressCallback : IStreamingFindLiteralReferencesProgress, IStreamingProgressTracker { - private readonly RemoteEndPoint _endPoint; + private readonly RemoteCallback _callback; private readonly CancellationToken _cancellationToken; public IStreamingProgressTracker ProgressTracker { get; } - public FindLiteralReferencesProgressCallback(RemoteEndPoint endPoint, CancellationToken cancellationToken) + public FindLiteralReferencesProgressCallback(RemoteCallback callback, CancellationToken cancellationToken) { - _endPoint = endPoint; + _callback = callback; _cancellationToken = cancellationToken; ProgressTracker = this; } - public Task OnReferenceFoundAsync(Document document, TextSpan span) - => _endPoint.InvokeAsync(nameof(SymbolFinder.FindLiteralsServerCallback.OnReferenceFoundAsync), new object[] { document.Id, span }, _cancellationToken); + public ValueTask OnReferenceFoundAsync(Document document, TextSpan span) + => _callback.InvokeAsync((callback, cancellationToken) => callback.OnLiteralReferenceFoundAsync(document.Id, span), _cancellationToken); - public Task AddItemsAsync(int count) - => _endPoint.InvokeAsync(nameof(SymbolFinder.FindLiteralsServerCallback.AddItemsAsync), new object[] { count }, _cancellationToken); + public ValueTask AddItemsAsync(int count) + => _callback.InvokeAsync((callback, cancellationToken) => callback.AddItemsAsync(count), _cancellationToken); - public Task ItemCompletedAsync() - => _endPoint.InvokeAsync(nameof(SymbolFinder.FindLiteralsServerCallback.ItemCompletedAsync), new object[] { }, _cancellationToken); + public ValueTask ItemCompletedAsync() + => _callback.InvokeAsync((callback, cancellationToken) => callback.ItemCompletedAsync(), _cancellationToken); } private sealed class FindReferencesProgressCallback : IStreamingFindReferencesProgress, IStreamingProgressTracker { private readonly Solution _solution; - private readonly RemoteEndPoint _endPoint; + private readonly RemoteCallback _callback; private readonly CancellationToken _cancellationToken; public IStreamingProgressTracker ProgressTracker { get; } - public FindReferencesProgressCallback(Solution solution, RemoteEndPoint endPoint, CancellationToken cancellationToken) + public FindReferencesProgressCallback(Solution solution, RemoteCallback callback, CancellationToken cancellationToken) { _solution = solution; - _endPoint = endPoint; + _callback = callback; _cancellationToken = cancellationToken; ProgressTracker = this; } - public Task OnStartedAsync() - => _endPoint.InvokeAsync(nameof(SymbolFinder.FindReferencesServerCallback.OnStartedAsync), Array.Empty(), _cancellationToken); + public ValueTask OnStartedAsync() + => _callback.InvokeAsync((callback, cancellationToken) => callback.OnStartedAsync(), _cancellationToken); - public Task OnCompletedAsync() - => _endPoint.InvokeAsync(nameof(SymbolFinder.FindReferencesServerCallback.OnCompletedAsync), Array.Empty(), _cancellationToken); + public ValueTask OnCompletedAsync() + => _callback.InvokeAsync((callback, cancellationToken) => callback.OnCompletedAsync(), _cancellationToken); - public Task OnFindInDocumentStartedAsync(Document document) - => _endPoint.InvokeAsync(nameof(SymbolFinder.FindReferencesServerCallback.OnFindInDocumentStartedAsync), new object[] { document.Id }, _cancellationToken); + public ValueTask OnFindInDocumentStartedAsync(Document document) + => _callback.InvokeAsync((callback, cancellationToken) => callback.OnFindInDocumentStartedAsync(document.Id), _cancellationToken); - public Task OnFindInDocumentCompletedAsync(Document document) - => _endPoint.InvokeAsync(nameof(SymbolFinder.FindReferencesServerCallback.OnFindInDocumentCompletedAsync), new object[] { document.Id }, _cancellationToken); + public ValueTask OnFindInDocumentCompletedAsync(Document document) + => _callback.InvokeAsync((callback, cancellationToken) => callback.OnFindInDocumentCompletedAsync(document.Id), _cancellationToken); - public Task OnDefinitionFoundAsync(ISymbol definition) - => _endPoint.InvokeAsync( - nameof(SymbolFinder.FindReferencesServerCallback.OnDefinitionFoundAsync), - new object[] { SerializableSymbolAndProjectId.Dehydrate(_solution, definition, _cancellationToken) }, _cancellationToken); + public ValueTask OnDefinitionFoundAsync(ISymbol definition) + { + var dehydratedDefinition = SerializableSymbolAndProjectId.Dehydrate(_solution, definition, _cancellationToken); + return _callback.InvokeAsync((callback, cancellationToken) => callback.OnDefinitionFoundAsync(dehydratedDefinition), _cancellationToken); + } - public Task OnReferenceFoundAsync(ISymbol definition, ReferenceLocation reference) - => _endPoint.InvokeAsync( - nameof(SymbolFinder.FindReferencesServerCallback.OnReferenceFoundAsync), - new object[] - { - SerializableSymbolAndProjectId.Dehydrate(_solution, definition, _cancellationToken), - SerializableReferenceLocation.Dehydrate(reference, _cancellationToken), - }, - _cancellationToken); + public ValueTask OnReferenceFoundAsync(ISymbol definition, ReferenceLocation reference) + { + var dehydratedDefinition = SerializableSymbolAndProjectId.Dehydrate(_solution, definition, _cancellationToken); + var dehydratedReference = SerializableReferenceLocation.Dehydrate(reference, _cancellationToken); + + return _callback.InvokeAsync((callback, cancellationToken) => callback.OnReferenceFoundAsync(dehydratedDefinition, dehydratedReference), _cancellationToken); + } - public Task AddItemsAsync(int count) - => _endPoint.InvokeAsync(nameof(SymbolFinder.FindReferencesServerCallback.AddItemsAsync), new object[] { count }, _cancellationToken); + public ValueTask AddItemsAsync(int count) + => _callback.InvokeAsync((callback, cancellationToken) => callback.AddItemsAsync(count), _cancellationToken); - public Task ItemCompletedAsync() - => _endPoint.InvokeAsync(nameof(SymbolFinder.FindReferencesServerCallback.ItemCompletedAsync), Array.Empty(), _cancellationToken); + public ValueTask ItemCompletedAsync() + => _callback.InvokeAsync((callback, cancellationToken) => callback.ItemCompletedAsync(), _cancellationToken); } } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/SymbolSearchUpdate/RemoteSymbolSearchUpdateService.cs b/src/Workspaces/Remote/ServiceHub/Services/SymbolSearchUpdate/RemoteSymbolSearchUpdateService.cs new file mode 100644 index 0000000000000..c5e035dd80867 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/SymbolSearchUpdate/RemoteSymbolSearchUpdateService.cs @@ -0,0 +1,73 @@ +// 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.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.SymbolSearch; + +namespace Microsoft.CodeAnalysis.Remote +{ + internal sealed class RemoteSymbolSearchUpdateService : BrokeredServiceBase, IRemoteSymbolSearchUpdateService + { + internal sealed class Factory : FactoryBase + { + protected override IRemoteSymbolSearchUpdateService CreateService(in ServiceConstructionArguments arguments, RemoteCallback callback) + => new RemoteSymbolSearchUpdateService(arguments, callback); + } + + private sealed class LogService : ISymbolSearchLogService + { + private readonly RemoteCallback _callback; + + public LogService(RemoteCallback callback) + => _callback = callback; + + public ValueTask LogExceptionAsync(string exception, string text, CancellationToken cancellationToken) + => _callback.InvokeAsync((callback, cancellationToken) => callback.LogExceptionAsync(exception, text, cancellationToken), cancellationToken); + + public ValueTask LogInfoAsync(string text, CancellationToken cancellationToken) + => _callback.InvokeAsync((callback, cancellationToken) => callback.LogInfoAsync(text, cancellationToken), cancellationToken); + } + + private readonly ISymbolSearchUpdateEngine _updateEngine; + + public RemoteSymbolSearchUpdateService(in ServiceConstructionArguments arguments, RemoteCallback callback) + : base(arguments) + { + _updateEngine = SymbolSearchUpdateEngineFactory.CreateEngineInProcess(new LogService(callback)); + } + + public ValueTask UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory, CancellationToken cancellationToken) + { + return RunServiceAsync(cancellationToken => + _updateEngine.UpdateContinuouslyAsync(sourceName, localSettingsDirectory, cancellationToken), + cancellationToken); + } + + public ValueTask> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken) + { + return RunServiceAsync(cancellationToken => + _updateEngine.FindPackagesWithTypeAsync(source, name, arity, cancellationToken), + cancellationToken); + } + + public ValueTask> FindPackagesWithAssemblyAsync(string source, string assemblyName, CancellationToken cancellationToken) + { + return RunServiceAsync(cancallationToken => + _updateEngine.FindPackagesWithAssemblyAsync(source, assemblyName, cancellationToken), + cancellationToken); + } + + public ValueTask> FindReferenceAssembliesWithTypeAsync(string name, int arity, CancellationToken cancellationToken) + { + return RunServiceAsync(cancallationToken => + _updateEngine.FindReferenceAssembliesWithTypeAsync(name, arity, cancellationToken), + cancellationToken); + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/SymbolSearchUpdateEngine/RemoteSymbolSearchUpdateEngine.cs b/src/Workspaces/Remote/ServiceHub/Services/SymbolSearchUpdateEngine/RemoteSymbolSearchUpdateEngine.cs deleted file mode 100644 index 4b9c0b2718b27..0000000000000 --- a/src/Workspaces/Remote/ServiceHub/Services/SymbolSearchUpdateEngine/RemoteSymbolSearchUpdateEngine.cs +++ /dev/null @@ -1,93 +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. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.SymbolSearch; - -namespace Microsoft.CodeAnalysis.Remote -{ - internal partial class RemoteSymbolSearchUpdateEngine : ServiceBase, IRemoteSymbolSearchUpdateEngine, ISymbolSearchLogService - { - private readonly ISymbolSearchUpdateEngine _updateEngine; - - public RemoteSymbolSearchUpdateEngine( - Stream stream, IServiceProvider serviceProvider) - : base(serviceProvider, stream) - { - _updateEngine = SymbolSearchUpdateEngineFactory.CreateEngineInProcess(logService: this); - - StartService(); - } - - public Task UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory) - { - return RunServiceAsync(() => - { - // In non-test scenarios, we're not cancellable. Our lifetime will simply be that - // of the OOP process itself. i.e. when it goes away, it will just tear down our - // update-loop itself. So we don't need any additional controls over it. - return _updateEngine.UpdateContinuouslyAsync(sourceName, localSettingsDirectory); - }, CancellationToken.None); - } - - public Task> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken) - { - return RunServiceAsync(async () => - { - var results = await _updateEngine.FindPackagesWithTypeAsync( - source, name, arity, cancellationToken).ConfigureAwait(false); - - return (IList)results; - }, cancellationToken); - } - - public Task> FindPackagesWithAssemblyAsync(string source, string assemblyName, CancellationToken cancellationToken) - { - return RunServiceAsync(async () => - { - var results = await _updateEngine.FindPackagesWithAssemblyAsync( - source, assemblyName, cancellationToken).ConfigureAwait(false); - - return (IList)results; - }, cancellationToken); - } - - public Task> FindReferenceAssembliesWithTypeAsync(string name, int arity, CancellationToken cancellationToken) - { - return RunServiceAsync(async () => - { - var results = await _updateEngine.FindReferenceAssembliesWithTypeAsync( - name, arity, cancellationToken).ConfigureAwait(false); - - return (IList)results; - }, cancellationToken); - } - - #region Messages to forward from here to VS - - public Task LogExceptionAsync(string exception, string text) - => EndPoint.InvokeAsync(nameof(LogExceptionAsync), new object[] { exception, text }, CancellationToken.None); - - public Task LogInfoAsync(string text) - => EndPoint.InvokeAsync(nameof(LogInfoAsync), new object[] { text }, CancellationToken.None); - - public Task OnDownloadFullDatabaseStartedAsync(string title) - => EndPoint.InvokeAsync(nameof(OnDownloadFullDatabaseStartedAsync), new object[] { title }, CancellationToken.None); - - public Task OnDownloadFullDatabaseSucceededAsync() - => EndPoint.InvokeAsync(nameof(OnDownloadFullDatabaseSucceededAsync), Array.Empty(), CancellationToken.None); - - public Task OnDownloadFullDatabaseCanceledAsync() - => EndPoint.InvokeAsync(nameof(OnDownloadFullDatabaseCanceledAsync), Array.Empty(), CancellationToken.None); - - public Task OnDownloadFullDatabaseFailedAsync(string message) - => EndPoint.InvokeAsync(nameof(OnDownloadFullDatabaseFailedAsync), new object[] { message }, CancellationToken.None); - - #endregion - } -} diff --git a/src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsService.cs b/src/Workspaces/Remote/ServiceHub/Services/TodoCommentsDiscovery/RemoteTodoCommentsDiscoveryService.cs similarity index 73% rename from src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsService.cs rename to src/Workspaces/Remote/ServiceHub/Services/TodoCommentsDiscovery/RemoteTodoCommentsDiscoveryService.cs index 2dd83884f2eda..ee1e1beb0cae8 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/TodoCommentsDiscovery/RemoteTodoCommentsDiscoveryService.cs @@ -11,17 +11,17 @@ namespace Microsoft.CodeAnalysis.Remote { - internal partial class RemoteTodoCommentsService : BrokeredServiceBase, IRemoteTodoCommentsService + internal partial class RemoteTodoCommentsDiscoveryService : BrokeredServiceBase, IRemoteTodoCommentsDiscoveryService { - internal sealed class Factory : FactoryBase + internal sealed class Factory : FactoryBase { - protected override IRemoteTodoCommentsService CreateService(in ServiceConstructionArguments arguments, RemoteCallback callback) - => new RemoteTodoCommentsService(arguments, callback); + protected override IRemoteTodoCommentsDiscoveryService CreateService(in ServiceConstructionArguments arguments, RemoteCallback callback) + => new RemoteTodoCommentsDiscoveryService(arguments, callback); } private readonly RemoteCallback _callback; - public RemoteTodoCommentsService(in ServiceConstructionArguments arguments, RemoteCallback callback) + public RemoteTodoCommentsDiscoveryService(in ServiceConstructionArguments arguments, RemoteCallback callback) : base(arguments) { _callback = callback; diff --git a/src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsIncrementalAnalyzer.cs b/src/Workspaces/Remote/ServiceHub/Services/TodoCommentsDiscovery/RemoteTodoCommentsIncrementalAnalyzer.cs similarity index 100% rename from src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsIncrementalAnalyzer.cs rename to src/Workspaces/Remote/ServiceHub/Services/TodoCommentsDiscovery/RemoteTodoCommentsIncrementalAnalyzer.cs diff --git a/src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsIncrementalAnalyzerProvider.cs b/src/Workspaces/Remote/ServiceHub/Services/TodoCommentsDiscovery/RemoteTodoCommentsIncrementalAnalyzerProvider.cs similarity index 100% rename from src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsIncrementalAnalyzerProvider.cs rename to src/Workspaces/Remote/ServiceHub/Services/TodoCommentsDiscovery/RemoteTodoCommentsIncrementalAnalyzerProvider.cs diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SymbolUsageInfo.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SymbolUsageInfo.cs index 73fe9fe4fc435..af8f764eecbd3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SymbolUsageInfo.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SymbolUsageInfo.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Runtime.Serialization; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -13,14 +14,19 @@ namespace Microsoft.CodeAnalysis /// For namespaces and types, this corresponds to values from . /// For methods, fields, properties, events, locals and parameters, this corresponds to values from . /// + [DataContract] internal readonly struct SymbolUsageInfo : IEquatable { public static readonly SymbolUsageInfo None = Create(ValueUsageInfo.None); + [DataMember(Order = 0)] public ValueUsageInfo? ValueUsageInfoOpt { get; } + + [DataMember(Order = 1)] public TypeOrNamespaceUsageInfo? TypeOrNamespaceUsageInfoOpt { get; } - private SymbolUsageInfo(ValueUsageInfo? valueUsageInfoOpt, TypeOrNamespaceUsageInfo? typeOrNamespaceUsageInfoOpt) + // Must be public since it's used for deserialization. + public SymbolUsageInfo(ValueUsageInfo? valueUsageInfoOpt, TypeOrNamespaceUsageInfo? typeOrNamespaceUsageInfoOpt) { Debug.Assert(valueUsageInfoOpt.HasValue ^ typeOrNamespaceUsageInfoOpt.HasValue); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index 6019bc17ecc6d..369a97c942601 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -495,5 +495,6 @@ internal enum FunctionId DependentTypeFinder_FindAndCacheImplementingTypesAsync = 432, RemoteSemanticClassificationCacheService_ExceptionInCacheRead = 440, + FeatureNotAvailable = 441, } }