Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge release/dev16.7-preview1 to master #43922

Merged
merged 17 commits into from
May 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1940,6 +1940,58 @@ public bool Equals(Program other)
parameters: CSharp6Implicit);
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateEqualsAndGetHashCode)]
[WorkItem(25708, "https://github.com/dotnet/roslyn/issues/25708")]
public async Task TestOverrideEqualsOnRefStructReturnsFalse()
{
await TestWithPickMembersDialogAsync(
@"
ref struct Program
{
public string s;
[||]
}",
@"
ref struct Program
{
public string s;

public override bool Equals(object obj)
{
return false;
}
}",
chosenSymbols: null);
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateEqualsAndGetHashCode)]
[WorkItem(25708, "https://github.com/dotnet/roslyn/issues/25708")]
public async Task TestImplementIEquatableOnRefStructSkipsIEquatable()
{
await TestWithPickMembersDialogAsync(
@"
ref struct Program
{
public string s;
[||]
}",
@"
ref struct Program
{
public string s;

public override bool Equals(object obj)
{
return false;
}
}",
chosenSymbols: null,
// We are forcefully enabling the ImplementIEquatable option, as that is our way
// to test that the option does nothing. The VS mode will ensure if the option
// is not available it will not be shown.
optionsCallback: options => EnableOption(options, ImplementIEquatableId));
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateEqualsAndGetHashCode)]
public async Task TestImplementIEquatableOnStructInNullableContextWithUnannotatedMetadata()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static async Task<RemoteHostClient> CreateAsync(Workspace workspace, bool
{
var inprocServices = new InProcRemoteServices(runCacheCleanup);

var remoteHostStream = await inprocServices.RequestServiceAsync(WellKnownRemoteHostServices.RemoteHostService).ConfigureAwait(false);
var remoteHostStream = await inprocServices.RequestServiceAsync(WellKnownServiceHubServices.RemoteHostService).ConfigureAwait(false);

var current = CreateClientId(Process.GetCurrentProcess().Id.ToString());
var instance = new InProcRemoteHostClient(current, workspace, inprocServices, remoteHostStream);
Expand Down Expand Up @@ -98,7 +98,7 @@ public Task<Stream> RequestServiceAsync(string serviceName)
public override string ClientId { get; }
public override bool IsRemoteHost64Bit => IntPtr.Size == 8;

public override async Task<Connection?> TryCreateConnectionAsync(
protected override async Task<Connection?> TryCreateConnectionAsync(
string serviceName, object? callbackTarget, CancellationToken cancellationToken)
{
// get stream from service hub to communicate service specific information
Expand All @@ -108,10 +108,6 @@ public Task<Stream> RequestServiceAsync(string serviceName)
return new JsonRpcConnection(Workspace, _inprocServices.Logger, callbackTarget, serviceStream);
}

protected override void OnStarted()
{
}

public override void Dispose()
{
// we are asked to disconnect. unsubscribe and dispose to disconnect
Expand Down Expand Up @@ -165,7 +161,7 @@ public InProcRemoteServices(bool runCacheCleanup)
_serviceProvider = new ServiceProvider(runCacheCleanup);
_creatorMap = new Dictionary<string, Func<Stream, IServiceProvider, ServiceBase>>();

RegisterService(WellKnownRemoteHostServices.RemoteHostService, (s, p) => new RemoteHostService(s, p));
RegisterService(WellKnownServiceHubServices.RemoteHostService, (s, p) => new RemoteHostService(s, p));
RegisterService(WellKnownServiceHubServices.CodeAnalysisService, (s, p) => new CodeAnalysisService(s, p));
RegisterService(WellKnownServiceHubServices.RemoteSymbolSearchUpdateEngine, (s, p) => new RemoteSymbolSearchUpdateEngine(s, p));
RegisterService(WellKnownServiceHubServices.RemoteDesignerAttributeService, (s, p) => new RemoteDesignerAttributeService(s, p));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.Collections.Immutable;
using System.Composition;
Expand Down Expand Up @@ -35,7 +37,7 @@ internal partial class GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringPro
private const string EqualsName = nameof(object.Equals);
private const string GetHashCodeName = nameof(object.GetHashCode);

private readonly IPickMembersService _pickMembersService_forTestingPurposes;
private readonly IPickMembersService? _pickMembersService_forTestingPurposes;

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
Expand All @@ -45,7 +47,7 @@ public GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider()
}

[SuppressMessage("RoslynDiagnosticsReliability", "RS0034:Exported parts should have [ImportingConstructor]", Justification = "Used incorrectly by tests")]
public GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider(IPickMembersService pickMembersService)
public GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider(IPickMembersService? pickMembersService)
=> _pickMembersService_forTestingPurposes = pickMembersService;

public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
Expand All @@ -69,9 +71,9 @@ private async Task HandleNonSelectionAsync(CodeRefactoringContext context)
{
var (document, textSpan, cancellationToken) = context;

var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

// We offer the refactoring when the user is either on the header of a class/struct,
// or if they're between any members of a class/struct and are on a blank line.
Expand All @@ -81,7 +83,7 @@ private async Task HandleNonSelectionAsync(CodeRefactoringContext context)
return;
}

var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

// Only supported on classes/structs.
var containingType = semanticModel.GetDeclaredSymbol(typeDeclaration) as INamedTypeSymbol;
Expand Down Expand Up @@ -134,13 +136,21 @@ private bool HasOperator(INamedTypeSymbol containingType, string operatorName)

private bool CanImplementIEquatable(
SemanticModel semanticModel, INamedTypeSymbol containingType,
[NotNullWhen(true)] out INamedTypeSymbol constructedType)
[NotNullWhen(true)] out INamedTypeSymbol? constructedType)
{
var equatableTypeOpt = semanticModel.Compilation.GetTypeByMetadataName(typeof(IEquatable<>).FullName);
if (equatableTypeOpt != null)
// A ref struct can never implement an interface, therefore never add IEquatable to the selection
// options if the type is a ref struct.
if (!containingType.IsRefLikeType)
{
constructedType = equatableTypeOpt.Construct(containingType);
return !containingType.AllInterfaces.Contains(constructedType);
var equatableTypeOpt = semanticModel.Compilation.GetTypeByMetadataName(typeof(IEquatable<>).FullName!);
if (equatableTypeOpt != null)
{
constructedType = equatableTypeOpt.Construct(containingType);

// A ref struct can never implement an interface, therefore never add IEquatable to the selection
// options if the type is a ref struct.
return !containingType.AllInterfaces.Contains(constructedType);
}
}

constructedType = null;
Expand Down Expand Up @@ -174,8 +184,8 @@ public async Task<ImmutableArray<CodeAction>> GenerateEqualsAndGetHashCodeFromMe
GetExistingMemberInfo(
info.ContainingType, out var hasEquals, out var hasGetHashCode);

var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var typeDeclaration = syntaxFacts.GetContainingTypeDeclaration(root, textSpan.Start);

return await CreateActionsAsync(
Expand Down Expand Up @@ -240,15 +250,12 @@ private async Task<CodeAction> CreateCodeActionWithDialogAsync(
Document document, SyntaxNode typeDeclaration, INamedTypeSymbol containingType, ImmutableArray<ISymbol> members,
bool generateEquals, bool generateGetHashCode, CancellationToken cancellationToken)
{
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);

using var _ = ArrayBuilder<PickMembersOption>.GetInstance(out var pickMembersOptions);

var canImplementIEquatable = CanImplementIEquatable(semanticModel, containingType, out var equatableTypeOpt);
var hasExistingOperators = HasOperators(containingType);

if (canImplementIEquatable)
if (CanImplementIEquatable(semanticModel, containingType, out var equatableTypeOpt))
{
var value = options.GetOption(GenerateEqualsAndGetHashCodeFromMembersOptions.ImplementIEquatable);

Expand All @@ -262,7 +269,7 @@ private async Task<CodeAction> CreateCodeActionWithDialogAsync(
value));
}

if (!hasExistingOperators)
if (!HasOperators(containingType))
{
var value = options.GetOption(GenerateEqualsAndGetHashCodeFromMembersOptions.GenerateOperators);
pickMembersOptions.Add(new PickMembersOption(
Expand All @@ -287,7 +294,7 @@ private async Task<CodeAction> CreateCodeActionWithoutDialogAsync(
{
// if we're generating equals for a struct, then also add IEquatable<S> support as
// well as operators (as long as the struct does not already have them).
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
implementIEquatable = CanImplementIEquatable(semanticModel, containingType, out _);
generateOperators = !HasOperators(containingType);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Remote;
using Roslyn.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.Remote
{
/// <summary>
/// Delivers global notifications to remote services.
/// </summary>
internal sealed class GlobalNotificationRemoteDeliveryService : IDisposable
{
private enum GlobalNotificationState
{
NotStarted,
Started
}

/// <summary>
/// Lock for the <see cref="_globalNotificationsTask"/> task chain. Each time we hear
/// about a global operation starting or stopping (i.e. a build) we will '.ContinueWith'
/// this task chain with a new notification to the OOP side. This way all the messages
/// are properly serialized and appear in the right order (i.e. we don't hear about a
/// stop prior to hearing about the relevant start).
/// </summary>
private readonly object _globalNotificationsGate = new object();
private Task<GlobalNotificationState> _globalNotificationsTask = Task.FromResult(GlobalNotificationState.NotStarted);

private readonly HostWorkspaceServices _services;
private readonly CancellationToken _cancellationToken;

public GlobalNotificationRemoteDeliveryService(HostWorkspaceServices services, CancellationToken cancellationToken)
{
_services = services;
_cancellationToken = cancellationToken;

RegisterGlobalOperationNotifications();
}

public void Dispose()
{
UnregisterGlobalOperationNotifications();
}

private void RegisterGlobalOperationNotifications()
{
var globalOperationService = _services.GetService<IGlobalOperationNotificationService>();
if (globalOperationService != null)
{
globalOperationService.Started += OnGlobalOperationStarted;
globalOperationService.Stopped += OnGlobalOperationStopped;
}
}

private void UnregisterGlobalOperationNotifications()
{
var globalOperationService = _services.GetService<IGlobalOperationNotificationService>();
if (globalOperationService != null)
{
globalOperationService.Started -= OnGlobalOperationStarted;
globalOperationService.Stopped -= OnGlobalOperationStopped;
}
}

private void OnGlobalOperationStarted(object sender, EventArgs e)
{
lock (_globalNotificationsGate)
{
_globalNotificationsTask = _globalNotificationsTask.SafeContinueWithFromAsync(
SendStartNotificationAsync, _cancellationToken, TaskContinuationOptions.None, TaskScheduler.Default);
}
}

private async Task<GlobalNotificationState> SendStartNotificationAsync(Task<GlobalNotificationState> previousTask)
{
// Can only transition from NotStarted->Started. If we hear about
// anything else, do nothing.
if (previousTask.Result != GlobalNotificationState.NotStarted)
{
return previousTask.Result;
}

var client = await RemoteHostClient.TryGetClientAsync(_services, _cancellationToken).ConfigureAwait(false);
if (client == null)
{
return previousTask.Result;
}

_ = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.CodeAnalysisService,
nameof(IRemoteGlobalNotificationDeliveryService.OnGlobalOperationStarted),
solution: null,
Array.Empty<object>(),
callbackTarget: null,
_cancellationToken).ConfigureAwait(false);

return GlobalNotificationState.Started;
}

private void OnGlobalOperationStopped(object sender, GlobalOperationEventArgs e)
{
lock (_globalNotificationsGate)
{
_globalNotificationsTask = _globalNotificationsTask.SafeContinueWithFromAsync(
previous => SendStoppedNotificationAsync(previous, e), _cancellationToken, TaskContinuationOptions.None, TaskScheduler.Default);
}
}

private async Task<GlobalNotificationState> SendStoppedNotificationAsync(Task<GlobalNotificationState> previousTask, GlobalOperationEventArgs e)
{
// Can only transition from Started->NotStarted. If we hear about
// anything else, do nothing.
if (previousTask.Result != GlobalNotificationState.Started)
{
return previousTask.Result;
}

var client = await RemoteHostClient.TryGetClientAsync(_services, _cancellationToken).ConfigureAwait(false);
if (client == null)
{
return previousTask.Result;
}

_ = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.CodeAnalysisService,
nameof(IRemoteGlobalNotificationDeliveryService.OnGlobalOperationStopped),
solution: null,
new object[] { e.Operations, e.Cancelled },
callbackTarget: null,
_cancellationToken).ConfigureAwait(false);

// Mark that we're stopped now.
return GlobalNotificationState.NotStarted;
}
}
}
Loading