提交 cc1186ba 编写于 作者: C CyrusNajmabadi

Fix crash with using 'replce property with method' with interfaces in VB.

上级 732a69f1
......@@ -576,5 +576,37 @@ Structure AStruct
Private X As Integer
End Structure", ignoreTrivia:=False)
End Function
<WorkItem(440371, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/440371")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)>
Public Async Function TestInterfaceReplacement1() As Task
Await TestInRegularAndScriptAsync(
"Interface IFoo
Property [||]Foo As Integer
End Interface
Class C
Implements IFoo
Public Property Foo As Integer Implements IFoo.Foo
End Class",
"Interface IFoo
Property Foo As Integer
End Interface
Class C
Implements IFoo
Private _Foo As Integer
Public Function GetFoo() As Integer Implements IFoo.GetFoo
Return _Foo
End Function
Public Sub SetFoo(AutoPropertyValue As Integer) Implements IFoo.SetFoo
_Foo = AutoPropertyValue
End Sub
End Class")
End Function
End Class
End Namespace
\ No newline at end of file
......@@ -45,7 +45,7 @@ protected static SyntaxNode GetFieldReference(SyntaxGenerator generator, IFieldS
public async Task ReplaceReferenceAsync(
Document document,
SyntaxEditor editor, SyntaxToken nameToken,
SyntaxEditor editor, SyntaxNode identifierName,
IPropertySymbol property, IFieldSymbol propertyBackingField,
string desiredGetMethodName, string desiredSetMethodName,
CancellationToken cancellationToken)
......@@ -55,10 +55,9 @@ protected static SyntaxNode GetFieldReference(SyntaxGenerator generator, IFieldS
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var referenceReplacer = new ReferenceReplacer(
this, semanticModel, syntaxFacts, semanticFacts,
editor, nameToken, property, propertyBackingField,
desiredGetMethodName, desiredSetMethodName,
cancellationToken);
this, semanticModel, syntaxFacts, semanticFacts, editor,
(TIdentifierNameSyntax)identifierName, property, propertyBackingField,
desiredGetMethodName, desiredSetMethodName, cancellationToken);
referenceReplacer.Do();
}
......@@ -71,7 +70,6 @@ private struct ReferenceReplacer
private readonly ISyntaxFactsService _syntaxFacts;
private readonly ISemanticFactsService _semanticFacts;
private readonly SyntaxEditor _editor;
private readonly SyntaxToken _nameToken;
private readonly IPropertySymbol _property;
private readonly IFieldSymbol _propertyBackingField;
private readonly string _desiredGetMethodName;
......@@ -87,7 +85,8 @@ private struct ReferenceReplacer
SemanticModel semanticModel,
ISyntaxFactsService syntaxFacts,
ISemanticFactsService semanticFacts,
SyntaxEditor editor, SyntaxToken nameToken,
SyntaxEditor editor,
TIdentifierNameSyntax identifierName,
IPropertySymbol property, IFieldSymbol propertyBackingField,
string desiredGetMethodName,
string desiredSetMethodName,
......@@ -98,14 +97,13 @@ private struct ReferenceReplacer
_syntaxFacts = syntaxFacts;
_semanticFacts = semanticFacts;
_editor = editor;
_nameToken = nameToken;
_identifierName = identifierName;
_property = property;
_propertyBackingField = propertyBackingField;
_desiredGetMethodName = desiredGetMethodName;
_desiredSetMethodName = desiredSetMethodName;
_cancellationToken = cancellationToken;
_identifierName = (TIdentifierNameSyntax)nameToken.Parent;
_expression = _identifierName;
_cref = _service.TryGetCrefSyntax(_identifierName);
if (_syntaxFacts.IsNameOfMemberAccessExpression(_expression))
......@@ -252,6 +250,13 @@ public void Do()
_editor.ReplaceNode(declarator, newDeclarator);
}
else if (_syntaxFacts.IsRightSideOfQualifiedName(_identifierName))
{
// Found a reference in a qualified name. This happens for VB explicit interface
// names. We don't want to update this. (The "Implement IFoo.Bar" clause will be
// updated when we generate the actual Get/Set methods.
return;
}
else
{
// No writes. Replace this with an appropriate read.
......
......@@ -14,7 +14,7 @@ internal interface IReplacePropertyWithMethodsService : ILanguageService
Task ReplaceReferenceAsync(
Document document,
SyntaxEditor editor, SyntaxToken nameToken,
SyntaxEditor editor, SyntaxNode identifierName,
IPropertySymbol property, IFieldSymbol propertyBackingField,
string desiredGetMethodName, string desiredSetMethodName,
CancellationToken cancellationToken);
......
......@@ -13,6 +13,7 @@
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
......@@ -91,12 +92,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
// Get the warnings we'd like to put at the definition site.
var definitionWarning = GetDefinitionIssues(propertyReferences);
var equalityComparer = (IEqualityComparer<IPropertySymbol>)SymbolEquivalenceComparer.Instance;
var definitionToBackingField =
propertyReferences.Select(r => r.Definition)
.OfType<IPropertySymbol>()
.ToDictionary(d => d, GetBackingField, equalityComparer);
var definitionToBackingField = CreateDefinitionToBackingFieldMap(propertyReferences);
var q = from r in propertyReferences
where r.Definition is IPropertySymbol
......@@ -112,16 +108,32 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
var updatedSolution = originalSolution;
updatedSolution = await UpdateReferencesAsync(
updatedSolution, referencesByDocument, definitionToBackingField,
updatedSolution, referencesByDocument, definitionToBackingField,
desiredGetMethodName, desiredSetMethodName, cancellationToken).ConfigureAwait(false);
updatedSolution = await ReplaceDefinitionsWithMethodsAsync(
originalSolution, updatedSolution, propertyReferences, definitionToBackingField,
originalSolution, updatedSolution, propertyReferences, definitionToBackingField,
desiredGetMethodName, desiredSetMethodName, cancellationToken).ConfigureAwait(false);
return updatedSolution;
}
private static Dictionary<IPropertySymbol, IFieldSymbol> CreateDefinitionToBackingFieldMap(IEnumerable<ReferencedSymbol> propertyReferences)
{
var definitionToBackingField = new Dictionary<IPropertySymbol, IFieldSymbol>(SymbolEquivalenceComparer.Instance);
foreach (var reference in propertyReferences)
{
if (reference.Definition is IPropertySymbol property)
{
var backingField = GetBackingField(property);
definitionToBackingField[property] = backingField;
}
}
return definitionToBackingField;
}
private bool HasAnyMatchingGetOrSetMethods(IPropertySymbol property, string name)
{
return HasAnyMatchingGetMethods(property, name) ||
......@@ -238,6 +250,8 @@ private string GetDefinitionIssues(IEnumerable<ReferencedSymbol> getMethodRefere
{
if (references != null)
{
var syntaxFacts = originalDocument.GetLanguageService<ISyntaxFactsService>();
foreach (var tuple in references)
{
cancellationToken.ThrowIfCancellationRequested();
......@@ -247,17 +261,19 @@ private string GetDefinitionIssues(IEnumerable<ReferencedSymbol> getMethodRefere
var location = referenceLocation.Location;
var nameToken = root.FindToken(location.SourceSpan.Start, findInsideTrivia: true);
if (referenceLocation.IsImplicit)
var parent = nameToken.Parent;
if (referenceLocation.IsImplicit || !syntaxFacts.IsIdentifierName(parent))
{
// Warn the user that we can't properly replace this property with a method.
editor.ReplaceNode(nameToken.Parent, nameToken.Parent.WithAdditionalAnnotations(
editor.ReplaceNode(parent, nameToken.Parent.WithAdditionalAnnotations(
ConflictAnnotation.Create(FeaturesResources.Property_referenced_implicitly)));
}
else
{
var fieldSymbol = propertyToBackingField.GetValueOrDefault(tuple.property);
await service.ReplaceReferenceAsync(
originalDocument, editor, nameToken,
originalDocument, editor, parent,
property, fieldSymbol,
desiredGetMethodName, desiredSetMethodName,
cancellationToken).ConfigureAwait(false);
......
......@@ -2,6 +2,7 @@
Imports System.Composition
Imports System.Threading
Imports Microsoft.CodeAnalysis.CodeGeneration
Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.Host.Mef
......@@ -82,17 +83,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
Dim getMethod = [property].GetMethod
If getMethod IsNot Nothing Then
result.Add(GetGetMethod(
generator, propertyStatement, propertyBackingField,
getMethod, desiredGetMethodName,
cancellationToken:=cancellationToken))
generator, [property], propertyStatement, propertyBackingField,
getMethod, desiredGetMethodName, cancellationToken:=cancellationToken))
End If
Dim setMethod = [property].SetMethod
If setMethod IsNot Nothing Then
result.Add(GetSetMethod(
generator, propertyStatement, propertyBackingField,
setMethod, desiredSetMethodName,
cancellationToken:=cancellationToken))
generator, [property], propertyStatement, propertyBackingField,
setMethod, desiredSetMethodName, cancellationToken:=cancellationToken))
End If
Return result
......@@ -100,6 +99,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
Private Function GetGetMethod(
generator As SyntaxGenerator,
[property] As IPropertySymbol,
propertyStatement As PropertyStatementSyntax,
propertyBackingField As IFieldSymbol,
getMethod As IMethodSymbol,
......@@ -119,7 +119,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
statements.Add(generator.ReturnStatement(fieldReference))
End If
getMethod = UpdateExplicitInterfaceImplementations([property], getMethod, desiredGetMethodName)
Dim methodDeclaration = generator.MethodDeclaration(getMethod, desiredGetMethodName, statements)
methodDeclaration = CopyLeadingTriviaOver(propertyStatement, methodDeclaration, ConvertValueToReturnsRewriter.instance)
Return methodDeclaration
End Function
......@@ -130,6 +132,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
Private Function GetSetMethod(
generator As SyntaxGenerator,
[property] As IPropertySymbol,
propertyStatement As PropertyStatementSyntax,
propertyBackingField As IFieldSymbol,
setMethod As IMethodSymbol,
......@@ -150,11 +153,43 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
fieldReference, generator.IdentifierName(setMethod.Parameters(0).Name)))
End If
setMethod = UpdateExplicitInterfaceImplementations([property], setMethod, desiredSetMethodName)
Dim methodDeclaration = generator.MethodDeclaration(setMethod, desiredSetMethodName, statements)
methodDeclaration = CopyLeadingTriviaOver(propertyStatement, methodDeclaration, ConvertValueToParamRewriter.instance)
Return methodDeclaration
End Function
Private Function UpdateExplicitInterfaceImplementations(
[property] As IPropertySymbol,
method As IMethodSymbol,
desiredName As String) As IMethodSymbol
Dim updatedExplicitImpl = UpdateExplicitInterfaceImplementation(
[property], method.ExplicitInterfaceImplementations.FirstOrDefault(), desiredName)
Return If(updatedExplicitImpl Is method.ExplicitInterfaceImplementations.FirstOrDefault(),
method,
CodeGenerationSymbolFactory.CreateMethodSymbol(
method, explicitInterfaceSymbol:=updatedExplicitImpl))
End Function
Private Function UpdateExplicitInterfaceImplementation(
[property] As IPropertySymbol,
methodSymbol As IMethodSymbol,
desiredName As String) As IMethodSymbol
If methodSymbol IsNot Nothing Then
If methodSymbol.Name = "get_" + [property].Name OrElse
methodSymbol.Name = "set_" + [property].Name Then
Return CodeGenerationSymbolFactory.CreateMethodSymbol(
methodSymbol, name:=desiredName, containingType:=methodSymbol.ContainingType)
End If
End If
Return methodSymbol
End Function
Private Function CopyLeadingTriviaOver(propertyStatement As PropertyStatementSyntax,
methodDeclaration As SyntaxNode,
documentationCommentRewriter As VisualBasicSyntaxRewriter) As SyntaxNode
......
......@@ -1903,6 +1903,21 @@ public override SyntaxNode WithTypeParameters(SyntaxNode declaration, IEnumerabl
}
}
internal override SyntaxNode WithExplicitInterfaceImplementations(SyntaxNode declaration, ImmutableArray<IMethodSymbol> explicitInterfaceImplementations)
{
switch (declaration)
{
case MethodDeclarationSyntax methodDeclaration:
return methodDeclaration.WithExplicitInterfaceSpecifier(CreateExplicitInterfaceSpecifier(explicitInterfaceImplementations));
}
return declaration;
}
private ExplicitInterfaceSpecifierSyntax CreateExplicitInterfaceSpecifier(ImmutableArray<IMethodSymbol> explicitInterfaceImplementations)
=> SyntaxFactory.ExplicitInterfaceSpecifier(
explicitInterfaceImplementations[0].ContainingType.GenerateNameSyntax());
public override SyntaxNode WithTypeConstraint(SyntaxNode declaration, string typeParameterName, SpecialTypeConstraintKind kinds, IEnumerable<SyntaxNode> types)
{
switch (declaration.Kind())
......
......@@ -414,9 +414,11 @@ public static INamespaceSymbol CreateNamespaceSymbol(string name, IList<ISymbol>
DeclarationModifiers? modifiers = null,
IMethodSymbol explicitInterfaceSymbol = null,
string name = null,
ImmutableArray<SyntaxNode> statements = default(ImmutableArray<SyntaxNode>))
ImmutableArray<SyntaxNode> statements = default(ImmutableArray<SyntaxNode>),
INamedTypeSymbol containingType = null)
{
return CodeGenerationSymbolFactory.CreateMethodSymbol(
return CreateMethodSymbol(
containingType,
attributes,
accessibility ?? method.DeclaredAccessibility,
modifiers ?? method.GetSymbolModifiers(),
......
......@@ -172,6 +172,11 @@ internal SyntaxNode MethodDeclaration(IMethodSymbol method, string name, IEnumer
decl = this.WithTypeParametersAndConstraints(decl, method.TypeParameters);
}
if (method.ExplicitInterfaceImplementations.Length > 0)
{
decl = this.WithExplicitInterfaceImplementations(decl, method.ExplicitInterfaceImplementations);
}
return decl;
}
......@@ -665,6 +670,8 @@ private SyntaxNode WithTypeParametersAndConstraints(SyntaxNode declaration, Immu
return declaration;
}
internal abstract SyntaxNode WithExplicitInterfaceImplementations(SyntaxNode declaration, ImmutableArray<IMethodSymbol> explicitInterfaceImplementations);
/// <summary>
/// Converts a declaration (method, class, etc) into a declaration with type parameters.
/// </summary>
......
......@@ -2863,6 +2863,30 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
Return declaration
End Function
Friend Overrides Function WithExplicitInterfaceImplementations(declaration As SyntaxNode, explicitInterfaceImplementations As ImmutableArray(Of IMethodSymbol)) As SyntaxNode
If TypeOf declaration Is MethodStatementSyntax Then
Dim methodStatement = DirectCast(declaration, MethodStatementSyntax)
Dim interfaceMembers = explicitInterfaceImplementations.Select(AddressOf GenerateInterfaceMember)
Return methodStatement.WithImplementsClause(
SyntaxFactory.ImplementsClause(SyntaxFactory.SeparatedList(interfaceMembers)))
ElseIf TypeOf declaration Is MethodBlockSyntax Then
Dim methodBlock = DirectCast(declaration, MethodBlockSyntax)
Return methodBlock.WithSubOrFunctionStatement(
DirectCast(WithExplicitInterfaceImplementations(methodBlock.SubOrFunctionStatement, explicitInterfaceImplementations), MethodStatementSyntax))
End If
Return declaration
End Function
Private Function GenerateInterfaceMember(method As IMethodSymbol) As QualifiedNameSyntax
Dim interfaceName = method.ContainingType.GenerateTypeSyntax()
Return SyntaxFactory.QualifiedName(
DirectCast(interfaceName, NameSyntax),
SyntaxFactory.IdentifierName(method.Name))
End Function
Public Overrides Function WithTypeConstraint(declaration As SyntaxNode, typeParameterName As String, kinds As SpecialTypeConstraintKind, Optional types As IEnumerable(Of SyntaxNode) = Nothing) As SyntaxNode
Dim constraints = SyntaxFactory.SeparatedList(Of ConstraintSyntax)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册