From fc3724ba88c0347b211f2bd1c96022179f97869e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 22 Mar 2020 17:51:48 -0700 Subject: [PATCH] Fix genarate-equals failing in partial types. --- ...ateEqualsAndGetHashCodeFromMembersTests.cs | 172 ++++++++++++++++++ ...ateEqualsAndGetHashCodeFromMembersTests.vb | 2 - .../GenerateEqualsAndGetHashCodeAction.cs | 30 +-- 3 files changed, 189 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/GenerateFromMembers/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs b/src/EditorFeatures/CSharpTest/GenerateFromMembers/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs index 97cd3580fac..15802a64a0d 100644 --- a/src/EditorFeatures/CSharpTest/GenerateFromMembers/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs +++ b/src/EditorFeatures/CSharpTest/GenerateFromMembers/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs @@ -2695,5 +2695,177 @@ public override bool Equals(object? obj) optionsCallback: options => EnableOption(options, GenerateOperatorsId), parameters: CSharpLatestImplicit); } + + [WorkItem(42574, "https://github.com/dotnet/roslyn/issues/42574")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateEqualsAndGetHashCode)] + public async Task TestPartialTypes1() + { + await TestWithPickMembersDialogAsync( +@" + + +partial class Goo +{ + int bar; + [||] +} + + +partial class Goo +{ + + +} + + +", +@" +partial class Goo +{ + int bar; + + public override bool Equals(object obj) + { + return obj is Goo goo && + bar == goo.bar; + } + + public override int GetHashCode() + { + return 999205674 + bar.GetHashCode(); + } +} + ", +chosenSymbols: new[] { "bar" }, +index: 1); + } + + [WorkItem(42574, "https://github.com/dotnet/roslyn/issues/42574")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateEqualsAndGetHashCode)] + public async Task TestPartialTypes2() + { + await TestWithPickMembersDialogAsync( +@" + + +partial class Goo +{ + int bar; + +} + + +partial class Goo +{ + +[||] +} + + +", +@" +partial class Goo +{ + public override bool Equals(object obj) + { + return obj is Goo goo && + bar == goo.bar; + } + + public override int GetHashCode() + { + return 999205674 + bar.GetHashCode(); + } +} + ", +chosenSymbols: new[] { "bar" }, +index: 1); + } + + [WorkItem(42574, "https://github.com/dotnet/roslyn/issues/42574")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateEqualsAndGetHashCode)] + public async Task TestPartialTypes3() + { + await TestWithPickMembersDialogAsync( +@" + + +partial class Goo +{ + +[||] +} + + +partial class Goo +{ + int bar; + +} + + +", +@" +partial class Goo +{ + public override bool Equals(object obj) + { + return obj is Goo goo && + bar == goo.bar; + } + + public override int GetHashCode() + { + return 999205674 + bar.GetHashCode(); + } +} + ", +chosenSymbols: new[] { "bar" }, +index: 1); + } + + [WorkItem(42574, "https://github.com/dotnet/roslyn/issues/42574")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateEqualsAndGetHashCode)] + public async Task TestPartialTypes4() + { + await TestWithPickMembersDialogAsync( +@" + + +partial class Goo +{ + + +} + + +partial class Goo +{ + int bar; +[||] +} + + +", +@" +partial class Goo +{ + int bar; + + public override bool Equals(object obj) + { + return obj is Goo goo && + bar == goo.bar; + } + + public override int GetHashCode() + { + return 999205674 + bar.GetHashCode(); + } +} + ", +chosenSymbols: new[] { "bar" }, +index: 1); + } } } diff --git a/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb b/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb index 6446fd3fba9..afac1c71d20 100644 --- a/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb +++ b/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb @@ -2,12 +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. -Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.CodeRefactorings Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings Imports Microsoft.CodeAnalysis.GenerateEqualsAndGetHashCodeFromMembers Imports Microsoft.CodeAnalysis.PickMembers -Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.GenerateConstructorFromMembers Public Class GenerateEqualsAndGetHashCodeFromMembersTests diff --git a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeAction.cs b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeAction.cs index 11d84cd85c4..15e7b20bdec 100644 --- a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeAction.cs +++ b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeAction.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.Collections.Generic; using System.Collections.Immutable; @@ -88,7 +90,7 @@ protected override async Task GetChangedDocumentAsync(CancellationToke if (constructedTypeToImplement is object) { - var generator = _document.GetLanguageService(); + var generator = _document.GetRequiredLanguageService(); newType = generator.AddInterfaceType(newType, generator.TypeExpression(constructedTypeToImplement)); @@ -97,20 +99,22 @@ protected override async Task GetChangedDocumentAsync(CancellationToke var newDocument = await UpdateDocumentAndAddImportsAsync( oldType, newType, cancellationToken).ConfigureAwait(false); - var service = _document.GetLanguageService(); + var service = _document.GetRequiredLanguageService(); var formattedDocument = await service.FormatDocumentAsync( newDocument, cancellationToken).ConfigureAwait(false); return formattedDocument; } - private async Task GetConstructedTypeToImplementAsync(CancellationToken cancellationToken) + private async Task GetConstructedTypeToImplementAsync(CancellationToken cancellationToken) { if (!_implementIEquatable) return null; - var semanticModel = await _document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await _document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var equatableType = semanticModel.Compilation.GetTypeByMetadataName(typeof(IEquatable<>).FullName); + if (equatableType == null) + return null; var useNullableTypeArgument = !_containingType.IsValueType @@ -123,7 +127,7 @@ private async Task GetConstructedTypeToImplementAsync(Cancella private async Task UpdateDocumentAndAddImportsAsync(SyntaxNode oldType, SyntaxNode newType, CancellationToken cancellationToken) { - var oldRoot = await _document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var oldRoot = await _document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var newDocument = _document.WithSyntaxRoot( oldRoot.ReplaceNode(oldType, newType)); newDocument = await ImportAdder.AddImportsFromSymbolAnnotationAsync( @@ -137,12 +141,12 @@ private async Task UpdateDocumentAndAddImportsAsync(SyntaxNode oldType CancellationToken cancellationToken) { var workspace = _document.Project.Solution.Workspace; - var syntaxTree = await _document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + var syntaxTree = await _document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - var declarationService = _document.GetLanguageService(); + var declarationService = _document.GetRequiredLanguageService(); var typeDeclaration = declarationService.GetDeclarations(_containingType) .Select(r => r.GetSyntax(cancellationToken)) - .First(s => s.FullSpan.IntersectsWith(_textSpan.Start)); + .First(s => s.SyntaxTree == syntaxTree && s.FullSpan.IntersectsWith(_textSpan.Start)); var newTypeDeclaration = CodeGenerator.AddMemberDeclarations( typeDeclaration, methods, workspace, @@ -153,9 +157,9 @@ private async Task UpdateDocumentAndAddImportsAsync(SyntaxNode oldType private async Task AddOperatorsAsync(List members, CancellationToken cancellationToken) { - var compilation = await _document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); + var compilation = await _document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - var generator = _document.GetLanguageService(); + var generator = _document.GetRequiredLanguageService(); // add nullable annotation to the parameter reference type, so that (in)equality operator implementations allow comparison against null var parameters = ImmutableArray.Create( @@ -210,13 +214,13 @@ private IMethodSymbol CreateInequalityOperator(Compilation compilation, SyntaxGe private Task CreateGetHashCodeMethodAsync(CancellationToken cancellationToken) { - var service = _document.GetLanguageService(); + var service = _document.GetRequiredLanguageService(); return service.GenerateGetHashCodeMethodAsync(_document, _containingType, _selectedMembers, cancellationToken); } private Task CreateEqualsMethodAsync(CancellationToken cancellationToken) { - var service = _document.GetLanguageService(); + var service = _document.GetRequiredLanguageService(); return _implementIEquatable ? service.GenerateEqualsMethodThroughIEquatableEqualsAsync(_document, _containingType, cancellationToken) : service.GenerateEqualsMethodAsync(_document, _containingType, _selectedMembers, cancellationToken); @@ -224,7 +228,7 @@ private Task CreateEqualsMethodAsync(CancellationToken cancellati private async Task CreateIEquatableEqualsMethodAsync(INamedTypeSymbol constructedEquatableType, CancellationToken cancellationToken) { - var service = _document.GetLanguageService(); + var service = _document.GetRequiredLanguageService(); return await service.GenerateIEquatableEqualsMethodAsync( _document, _containingType, _selectedMembers, constructedEquatableType, cancellationToken).ConfigureAwait(false); } -- GitLab