提交 fc3724ba 编写于 作者: C Cyrus Najmabadi

Fix genarate-equals failing in partial types.

上级 423c52f2
...@@ -2695,5 +2695,177 @@ public override bool Equals(object? obj) ...@@ -2695,5 +2695,177 @@ public override bool Equals(object? obj)
optionsCallback: options => EnableOption(options, GenerateOperatorsId), optionsCallback: options => EnableOption(options, GenerateOperatorsId),
parameters: CSharpLatestImplicit); parameters: CSharpLatestImplicit);
} }
[WorkItem(42574, "https://github.com/dotnet/roslyn/issues/42574")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateEqualsAndGetHashCode)]
public async Task TestPartialTypes1()
{
await TestWithPickMembersDialogAsync(
@"<Workspace>
<Project Language=""C#"" CommonReferences=""true"">
<Document>
partial class Goo
{
int bar;
[||]
}
</Document>
<Document>
partial class Goo
{
}
</Document>
</Project>
</Workspace>",
@"
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(
@"<Workspace>
<Project Language=""C#"" CommonReferences=""true"">
<Document>
partial class Goo
{
int bar;
}
</Document>
<Document>
partial class Goo
{
[||]
}
</Document>
</Project>
</Workspace>",
@"
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(
@"<Workspace>
<Project Language=""C#"" CommonReferences=""true"">
<Document>
partial class Goo
{
[||]
}
</Document>
<Document>
partial class Goo
{
int bar;
}
</Document>
</Project>
</Workspace>",
@"
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(
@"<Workspace>
<Project Language=""C#"" CommonReferences=""true"">
<Document>
partial class Goo
{
}
</Document>
<Document>
partial class Goo
{
int bar;
[||]
}
</Document>
</Project>
</Workspace>",
@"
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);
}
} }
} }
...@@ -2,12 +2,10 @@ ...@@ -2,12 +2,10 @@
' The .NET Foundation licenses this file to you under the MIT license. ' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information. ' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis.CodeRefactorings Imports Microsoft.CodeAnalysis.CodeRefactorings
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings
Imports Microsoft.CodeAnalysis.GenerateEqualsAndGetHashCodeFromMembers Imports Microsoft.CodeAnalysis.GenerateEqualsAndGetHashCodeFromMembers
Imports Microsoft.CodeAnalysis.PickMembers Imports Microsoft.CodeAnalysis.PickMembers
Imports Microsoft.CodeAnalysis.Text
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.GenerateConstructorFromMembers Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.GenerateConstructorFromMembers
Public Class GenerateEqualsAndGetHashCodeFromMembersTests Public Class GenerateEqualsAndGetHashCodeFromMembersTests
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
...@@ -88,7 +90,7 @@ protected override async Task<Document> GetChangedDocumentAsync(CancellationToke ...@@ -88,7 +90,7 @@ protected override async Task<Document> GetChangedDocumentAsync(CancellationToke
if (constructedTypeToImplement is object) if (constructedTypeToImplement is object)
{ {
var generator = _document.GetLanguageService<SyntaxGenerator>(); var generator = _document.GetRequiredLanguageService<SyntaxGenerator>();
newType = generator.AddInterfaceType(newType, newType = generator.AddInterfaceType(newType,
generator.TypeExpression(constructedTypeToImplement)); generator.TypeExpression(constructedTypeToImplement));
...@@ -97,20 +99,22 @@ protected override async Task<Document> GetChangedDocumentAsync(CancellationToke ...@@ -97,20 +99,22 @@ protected override async Task<Document> GetChangedDocumentAsync(CancellationToke
var newDocument = await UpdateDocumentAndAddImportsAsync( var newDocument = await UpdateDocumentAndAddImportsAsync(
oldType, newType, cancellationToken).ConfigureAwait(false); oldType, newType, cancellationToken).ConfigureAwait(false);
var service = _document.GetLanguageService<IGenerateEqualsAndGetHashCodeService>(); var service = _document.GetRequiredLanguageService<IGenerateEqualsAndGetHashCodeService>();
var formattedDocument = await service.FormatDocumentAsync( var formattedDocument = await service.FormatDocumentAsync(
newDocument, cancellationToken).ConfigureAwait(false); newDocument, cancellationToken).ConfigureAwait(false);
return formattedDocument; return formattedDocument;
} }
private async Task<INamedTypeSymbol> GetConstructedTypeToImplementAsync(CancellationToken cancellationToken) private async Task<INamedTypeSymbol?> GetConstructedTypeToImplementAsync(CancellationToken cancellationToken)
{ {
if (!_implementIEquatable) if (!_implementIEquatable)
return null; 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); var equatableType = semanticModel.Compilation.GetTypeByMetadataName(typeof(IEquatable<>).FullName);
if (equatableType == null)
return null;
var useNullableTypeArgument = var useNullableTypeArgument =
!_containingType.IsValueType !_containingType.IsValueType
...@@ -123,7 +127,7 @@ private async Task<INamedTypeSymbol> GetConstructedTypeToImplementAsync(Cancella ...@@ -123,7 +127,7 @@ private async Task<INamedTypeSymbol> GetConstructedTypeToImplementAsync(Cancella
private async Task<Document> UpdateDocumentAndAddImportsAsync(SyntaxNode oldType, SyntaxNode newType, CancellationToken cancellationToken) private async Task<Document> 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( var newDocument = _document.WithSyntaxRoot(
oldRoot.ReplaceNode(oldType, newType)); oldRoot.ReplaceNode(oldType, newType));
newDocument = await ImportAdder.AddImportsFromSymbolAnnotationAsync( newDocument = await ImportAdder.AddImportsFromSymbolAnnotationAsync(
...@@ -137,12 +141,12 @@ private async Task<Document> UpdateDocumentAndAddImportsAsync(SyntaxNode oldType ...@@ -137,12 +141,12 @@ private async Task<Document> UpdateDocumentAndAddImportsAsync(SyntaxNode oldType
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var workspace = _document.Project.Solution.Workspace; 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<ISymbolDeclarationService>(); var declarationService = _document.GetRequiredLanguageService<ISymbolDeclarationService>();
var typeDeclaration = declarationService.GetDeclarations(_containingType) var typeDeclaration = declarationService.GetDeclarations(_containingType)
.Select(r => r.GetSyntax(cancellationToken)) .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( var newTypeDeclaration = CodeGenerator.AddMemberDeclarations(
typeDeclaration, methods, workspace, typeDeclaration, methods, workspace,
...@@ -153,9 +157,9 @@ private async Task<Document> UpdateDocumentAndAddImportsAsync(SyntaxNode oldType ...@@ -153,9 +157,9 @@ private async Task<Document> UpdateDocumentAndAddImportsAsync(SyntaxNode oldType
private async Task AddOperatorsAsync(List<IMethodSymbol> members, CancellationToken cancellationToken) private async Task AddOperatorsAsync(List<IMethodSymbol> 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<SyntaxGenerator>(); var generator = _document.GetRequiredLanguageService<SyntaxGenerator>();
// add nullable annotation to the parameter reference type, so that (in)equality operator implementations allow comparison against null // add nullable annotation to the parameter reference type, so that (in)equality operator implementations allow comparison against null
var parameters = ImmutableArray.Create( var parameters = ImmutableArray.Create(
...@@ -210,13 +214,13 @@ private IMethodSymbol CreateInequalityOperator(Compilation compilation, SyntaxGe ...@@ -210,13 +214,13 @@ private IMethodSymbol CreateInequalityOperator(Compilation compilation, SyntaxGe
private Task<IMethodSymbol> CreateGetHashCodeMethodAsync(CancellationToken cancellationToken) private Task<IMethodSymbol> CreateGetHashCodeMethodAsync(CancellationToken cancellationToken)
{ {
var service = _document.GetLanguageService<IGenerateEqualsAndGetHashCodeService>(); var service = _document.GetRequiredLanguageService<IGenerateEqualsAndGetHashCodeService>();
return service.GenerateGetHashCodeMethodAsync(_document, _containingType, _selectedMembers, cancellationToken); return service.GenerateGetHashCodeMethodAsync(_document, _containingType, _selectedMembers, cancellationToken);
} }
private Task<IMethodSymbol> CreateEqualsMethodAsync(CancellationToken cancellationToken) private Task<IMethodSymbol> CreateEqualsMethodAsync(CancellationToken cancellationToken)
{ {
var service = _document.GetLanguageService<IGenerateEqualsAndGetHashCodeService>(); var service = _document.GetRequiredLanguageService<IGenerateEqualsAndGetHashCodeService>();
return _implementIEquatable return _implementIEquatable
? service.GenerateEqualsMethodThroughIEquatableEqualsAsync(_document, _containingType, cancellationToken) ? service.GenerateEqualsMethodThroughIEquatableEqualsAsync(_document, _containingType, cancellationToken)
: service.GenerateEqualsMethodAsync(_document, _containingType, _selectedMembers, cancellationToken); : service.GenerateEqualsMethodAsync(_document, _containingType, _selectedMembers, cancellationToken);
...@@ -224,7 +228,7 @@ private Task<IMethodSymbol> CreateEqualsMethodAsync(CancellationToken cancellati ...@@ -224,7 +228,7 @@ private Task<IMethodSymbol> CreateEqualsMethodAsync(CancellationToken cancellati
private async Task<IMethodSymbol> CreateIEquatableEqualsMethodAsync(INamedTypeSymbol constructedEquatableType, CancellationToken cancellationToken) private async Task<IMethodSymbol> CreateIEquatableEqualsMethodAsync(INamedTypeSymbol constructedEquatableType, CancellationToken cancellationToken)
{ {
var service = _document.GetLanguageService<IGenerateEqualsAndGetHashCodeService>(); var service = _document.GetRequiredLanguageService<IGenerateEqualsAndGetHashCodeService>();
return await service.GenerateIEquatableEqualsMethodAsync( return await service.GenerateIEquatableEqualsMethodAsync(
_document, _containingType, _selectedMembers, constructedEquatableType, cancellationToken).ConfigureAwait(false); _document, _containingType, _selectedMembers, constructedEquatableType, cancellationToken).ConfigureAwait(false);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册