diff --git a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj
index 34e8c3bdb72343022ed364ddc8e7956baef0fc11..25206c95466ec52ae9e85a813f93a8edfbee8d92 100644
--- a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj
+++ b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj
@@ -614,6 +614,7 @@
PreserveNewest
+
PreserveNewest
diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.vb
new file mode 100644
index 0000000000000000000000000000000000000000..5cb8e7321489a135d345b2492a217c1245505d90
--- /dev/null
+++ b/src/EditorFeatures/VisualBasicTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.vb
@@ -0,0 +1,276 @@
+' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings
+Imports Microsoft.CodeAnalysis.ReplaceMethodWithProperty
+Imports Roslyn.Test.Utilities
+Imports Xunit
+
+Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeActions.ReplaceMethodWithProperty
+ Public Class ReplaceMethodWithPropertyTests
+ Inherits AbstractVisualBasicCodeActionTest
+
+ Protected Overrides Function CreateCodeRefactoringProvider(workspace As Workspace) As Object
+ Return New ReplaceMethodWithPropertyCodeRefactoringProvider()
+ End Function
+
+
+ Public Sub TestMethodWithGetName()
+ Test(
+NewLines("class C \n function [||]GetFoo() as integer \n End function \n End class"),
+NewLines("class C \n ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n End class"))
+ End Sub
+
+
+ Public Sub TestMethodWithoutGetName()
+ Test(
+NewLines("class C \n function [||]Foo() as integer \n End function \n End class"),
+NewLines("class C \n ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n End class"))
+ End Sub
+
+
+ Public Sub TestMethodWithoutBody()
+ Test(
+NewLines("mustinherit class C \n MustOverride function [||]GetFoo() as integer \n End class"),
+NewLines("mustinherit class C \n MustOverride ReadOnly Property Foo as integer \n End class"))
+ End Sub
+
+
+ Public Sub TestMethodWithModifiers()
+ Test(
+NewLines("class C \n public shared function [||]GetFoo() as integer \n End function \n End class"),
+NewLines("class C \n public shared ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n End class"))
+ End Sub
+
+
+ Public Sub TestMethodWithAttributes()
+ Test(
+NewLines("class C \n function [||]GetFoo() as integer \n End function \n End class"),
+NewLines("class C \n ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n End class"))
+ End Sub
+
+
+ Public Sub TestMethodWithTrivia_1()
+ Test(
+"class C
+ ' Foo
+ function [||]GetFoo() as integer
+ End function
+End class",
+"class C
+ ' Foo
+ ReadOnly Property Foo as integer
+ Get
+ End Get
+ End Property
+End class",
+compareTokens:=False)
+ End Sub
+
+
+ Public Sub TestIfDefMethod()
+ Test(
+"class C
+#if true
+ function [||]GetFoo() as integer
+ End function
+#End if
+End class",
+"class C
+#if true
+ ReadOnly Property Foo as integer
+ Get
+ End Get
+ End Property
+#End if
+End class")
+ End Sub
+
+
+ Public Sub TestMethodWithTrivia_2()
+ Test(
+"class C
+ ' Foo
+ function [||]GetFoo() as integer
+ End function
+ ' SetFoo
+ sub SetFoo(i as integer)
+ End sub
+End class",
+"class C
+ ' Foo
+ ' SetFoo
+ Property Foo as integer
+ Get
+ End Get
+ Set(i as integer)
+ End Set
+ End Property
+End class",
+index:=1,
+compareTokens:=False)
+ End Sub
+
+
+ Public Sub TestExplicitInterfaceMethod_2()
+ Test(
+NewLines("interface I \n function GetFoo() as integer \n End interface \n class C \n implements I \n function [||]GetFoo() as integer implements I.GetFoo \n End function \n End class"),
+NewLines("interface I \n ReadOnly Property Foo as integer \n End interface \n class C \n implements I \n ReadOnly Property Foo as integer implements I.Foo \n Get \n End Get \n End Property \n End class"))
+ End Sub
+
+
+ Public Sub TestExplicitInterfaceMethod_3()
+ Test(
+NewLines("interface I \n function [||]GetFoo() as integer \n End interface \n class C \n implements I \n function GetFoo() as integer implements I.GetFoo \n End function \n End class"),
+NewLines("interface I \n ReadOnly Property Foo as integer \n End interface \n class C \n implements I \n ReadOnly Property Foo as integer implements I.Foo \n Get \n End Get \n End Property \n End class"))
+ End Sub
+
+
+ Public Sub TestInAttribute()
+ TestMissing(
+NewLines("class C \n function GetFoo() as integer \n End function \n End class"))
+ End Sub
+
+
+ Public Sub TestInMethod()
+ TestMissing(
+NewLines("class C \n function GetFoo() as integer \n [||] \n End function \n End class"))
+ End Sub
+
+
+ Public Sub TestSubMethod()
+ TestMissing(
+NewLines("class C \n sub [||]GetFoo() \n End sub \n End class"))
+ End Sub
+
+
+ Public Sub TestAsyncMethod()
+ TestMissing(
+NewLines("class C \n async function [||]GetFoo() as Task \n End function \n End class"))
+ End Sub
+
+
+ Public Sub TestGenericMethod()
+ TestMissing(
+NewLines("class C \n function [||]GetFoo(of T)() as integer \n End function \n End class"))
+ End Sub
+
+
+
+ Public Sub TestExtensionMethod()
+ TestMissing(
+NewLines("module C \n function [||]GetFoo(i as integer) as integer \n End function \n End module"))
+ End Sub
+
+
+ Public Sub TestMethodWithParameters_1()
+ TestMissing(
+NewLines("class C \n function [||]GetFoo(i as integer) as integer \n End function \n End class"))
+ End Sub
+
+
+ Public Sub TestUpdateGetReferenceNotInMethod()
+ Test(
+NewLines("class C \n function [||]GetFoo() as integer \n End function \n sub Bar() \n dim x = GetFoo() \n End sub \n End class"),
+NewLines("class C \n ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n sub Bar() \n dim x = Foo \n End sub \n End class"))
+ End Sub
+
+
+ Public Sub TestUpdateGetReferenceMemberAccessInvocation()
+ Test(
+NewLines("class C \n function [||]GetFoo() as integer \n End function \n sub Bar() \n dim x = me.GetFoo() \n End sub \n End class"),
+NewLines("class C \n ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n sub Bar() \n dim x = me.Foo \n End sub \n End class"))
+ End Sub
+
+
+ Public Sub TestUpdateGetReferenceBindingMemberInvocation()
+ Test(
+NewLines("class C \n function [||]GetFoo() as integer \n End function \n sub Bar() \n dim x as C \n dim v = x?.GetFoo() \n End sub \n End class"),
+NewLines("class C \n ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n sub Bar() \n dim x as C \n dim v = x?.Foo \n End sub \n End class"))
+ End Sub
+
+
+ Public Sub TestUpdateGetReferenceInMethod()
+ Test(
+NewLines("class C \n function [||]GetFoo() as integer \n return GetFoo() \n End function \n End class"),
+NewLines("class C \n ReadOnly Property Foo as integer \n Get \n return Foo \n End Get \n End Property \n End class"))
+ End Sub
+
+
+ Public Sub TestOverride()
+ Test(
+NewLines("class C \n public overridable function [||]GetFoo() as integer \n End function \n End class \n class D \n inherits C \n public overrides function GetFoo() as integer \n End function \n End class"),
+NewLines("class C \n public overridable ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n End class \n class D \n inherits C \n public overrides ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n End class"))
+ End Sub
+
+
+ Public Sub TestUpdateGetReference_NonInvoked()
+ Test(
+NewLines("class C \n function [||]GetFoo() as integer \n End function \n sub Bar() \n dim i = GetFoo \n End sub \n End class"),
+NewLines("class C \n ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n sub Bar() \n dim i = Foo \n End sub \n End class"))
+ End Sub
+
+
+ Public Sub TestUpdateGetSet()
+ Test(
+NewLines("class C \n function [||]GetFoo() as integer \n End function \n sub SetFoo(i as integer) \n End sub \n End class"),
+NewLines("class C \n Property Foo as integer \n Get \n End Get \n Set(i as integer) \n End Set \n End Property \n End class"),
+index:=1)
+ End Sub
+
+
+ Public Sub TestUpdateGetSetReference_NonInvoked()
+ Test(
+NewLines("Imports System \n class C \n function [||]GetFoo() as integer \n End function \n sub SetFoo(i as integer) \n End sub \n sub Bar() \n dim i as Action(of integer) = addressof SetFoo \n End sub \n End class"),
+NewLines("Imports System \n class C \n Property Foo as integer \n Get \n End Get \n Set(i as integer) \n End Set \n End Property \n sub Bar() \n dim i as Action(of integer) = addressof {|Conflict:Foo|} \n End sub \n End class"),
+index:=1)
+ End Sub
+
+
+ Public Sub TestUpdateGetSet_SetterAccessibility()
+ Test(
+NewLines("class C \n public function [||]GetFoo() as integer \n End function \n private sub SetFoo(i as integer) \n End sub \n End class"),
+NewLines("class C \n public Property Foo as integer \n Get End Get \n Private Set(i as integer) \n End Set \n End Property \n End class"),
+index:=1)
+ End Sub
+
+
+ Public Sub TestUpdateGetSet_GetInSetReference()
+ Test(
+NewLines("class C \n function [||]GetFoo() as integer \n End function \n sub SetFoo(i as integer) \n End sub \n sub Bar() \n SetFoo(GetFoo() + 1) \n End sub \n End class"),
+NewLines("class C \n Property Foo as integer \n Get \n End Get \n Set(i as integer) \n End Set \n End Property \n sub Bar() \n Foo = Foo + 1 \n End sub \n End class"),
+index:=1)
+ End Sub
+
+
+ Public Sub TestUpdateGetSet_SetReferenceInSetter()
+ Test(
+NewLines("class C \n function [||]GetFoo() as integer \n End function \n sub SetFoo(i as integer) \n SetFoo(i - 1) \n End sub \n End class"),
+NewLines("class C \n Property Foo as integer \n Get \n End Get \n Set(i as integer) \n Foo = i - 1 \n End Set \n End Property \n End class"),
+index:=1)
+ End Sub
+
+
+ Public Sub TestVirtualGetWithOverride_1()
+ Test(
+NewLines("class C \n protected overridable function [||]GetFoo() as integer \n End function \n End class \n class D \n inherits C \n protected overrides function GetFoo() as integer \n End function \n End class"),
+NewLines("class C \n protected overridable ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n End class \n class D \n inherits C \n protected overrides ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n End class"),
+index:=0)
+ End Sub
+
+
+ Public Sub TestVirtualGetWithOverride_2()
+ Test(
+NewLines("class C \n protected overridable function [||]GetFoo() as integer \n End function \n End class \n class D \n inherits C \n protected overrides function GetFoo() as integer \n return mybase.GetFoo() \n End function \n End class"),
+NewLines("class C \n protected overridable ReadOnly Property Foo as integer \n Get \n End Get \n End Property \n End class \n class D \n inherits C \n protected overrides ReadOnly Property Foo as integer \n Get \n return mybase.Foo \n End Get \n End Property \n End class"),
+index:=0)
+ End Sub
+
+
+ Public Sub TestWithPartialClasses()
+ Test(
+NewLines("partial class C \n function [||]GetFoo() as integer \n End function \n End class \n partial class C \n sub SetFoo(i as integer) \n End sub \n End class"),
+NewLines("partial class C \n Property Foo as integer \n Get \n End Get \n Set(i as integer) \n End Set \n End Property \n End class \n partial class C \n End class"),
+index:=1)
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs b/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs
index e83483f889ce57e53a892de6148788fc6a32be9c..6795361f436c80f73ff919e5d3a1fdfe8031e22d 100644
--- a/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs
+++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs
@@ -565,15 +565,6 @@ internal class CSharpFeaturesResources {
}
}
- ///
- /// Looks up a localized string similar to Non-invoked method cannot be replaced with property..
- ///
- internal static string NonInvokedMethodCannotBeReplacedWithProperty {
- get {
- return ResourceManager.GetString("NonInvokedMethodCannotBeReplacedWithProperty", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Not all code paths return.
///
@@ -601,15 +592,6 @@ internal class CSharpFeaturesResources {
}
}
- ///
- /// Looks up a localized string similar to Only methods with a single argument can be replaced with a property..
- ///
- internal static string OnlyMethodsWithASingleArgumentCanBeReplacedWithAProperty {
- get {
- return ResourceManager.GetString("OnlyMethodsWithASingleArgumentCanBeReplacedWithAProperty", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to orderby clause.
///
diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx
index 0215b179cc6be935f0707fb7dbd7abc4a7633786..c323779c2013ec7f0ad261ac192d339a7095f2c7 100644
--- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx
+++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx
@@ -434,10 +434,4 @@
The name '{0}' does not exist in the current context.
-
- Non-invoked method cannot be replaced with property.
-
-
- Only methods with a single argument can be replaced with a property.
-
\ No newline at end of file
diff --git a/src/Features/CSharp/Portable/ReplaceMethodWithProperty/CSharpReplaceMethodWithPropertyService.cs b/src/Features/CSharp/Portable/ReplaceMethodWithProperty/CSharpReplaceMethodWithPropertyService.cs
index 346eb79b8835e8de9a877ff9a7e325120f774677..f97222f7928098f6eb5b5061567a3c9493724ce3 100644
--- a/src/Features/CSharp/Portable/ReplaceMethodWithProperty/CSharpReplaceMethodWithPropertyService.cs
+++ b/src/Features/CSharp/Portable/ReplaceMethodWithProperty/CSharpReplaceMethodWithPropertyService.cs
@@ -44,17 +44,33 @@ public SyntaxNode GetMethodDeclaration(SyntaxToken token)
return containingMethod;
}
- public SyntaxNode ConvertMethodsToProperty(
+ public void RemoveSetMethod(SyntaxEditor editor, SyntaxNode setMethodDeclaration)
+ {
+ editor.RemoveNode(setMethodDeclaration);
+ }
+
+ public void ReplaceGetMethodWithProperty(
+ SyntaxEditor editor,
SemanticModel semanticModel,
- SyntaxGenerator generator, GetAndSetMethods getAndSetMethods,
+ GetAndSetMethods getAndSetMethods,
string propertyName, bool nameChanged)
{
var getMethodDeclaration = getAndSetMethods.GetMethodDeclaration as MethodDeclarationSyntax;
if (getMethodDeclaration == null)
{
- return getAndSetMethods.GetMethodDeclaration;
+ return;
}
+ editor.ReplaceNode(getMethodDeclaration,
+ ConvertMethodsToProperty(semanticModel, editor.Generator, getAndSetMethods, propertyName, nameChanged));
+ }
+
+ public SyntaxNode ConvertMethodsToProperty(
+ SemanticModel semanticModel,
+ SyntaxGenerator generator, GetAndSetMethods getAndSetMethods,
+ string propertyName, bool nameChanged)
+ {
+ var getMethodDeclaration = getAndSetMethods.GetMethodDeclaration as MethodDeclarationSyntax;
var getAccessor = CreateGetAccessor(getAndSetMethods);
var setAccessor = CreateSetAccessor(semanticModel, generator, getAndSetMethods);
@@ -195,7 +211,7 @@ public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node)
{
if (invocation.ArgumentList?.Arguments.Count != 1)
{
- var annotation = ConflictAnnotation.Create(CSharpFeaturesResources.OnlyMethodsWithASingleArgumentCanBeReplacedWithAProperty);
+ var annotation = ConflictAnnotation.Create(FeaturesResources.OnlyMethodsWithASingleArgumentCanBeReplacedWithAProperty);
editor.ReplaceNode(nameNode, newName.WithIdentifier(newName.Identifier.WithAdditionalAnnotations(annotation)));
return;
}
@@ -255,7 +271,7 @@ public void ReplaceSetReference(SyntaxEditor editor, SyntaxToken nameToken, stri
if (!IsInvocationName(nameNode, invocationExpression))
{
// Wasn't invoked. Change the name, but report a conflict.
- var annotation = ConflictAnnotation.Create(CSharpFeaturesResources.NonInvokedMethodCannotBeReplacedWithProperty);
+ var annotation = ConflictAnnotation.Create(FeaturesResources.NonInvokedMethodCannotBeReplacedWithProperty);
editor.ReplaceNode(nameNode, newName.WithIdentifier(newName.Identifier.WithAdditionalAnnotations(annotation)));
return;
}
diff --git a/src/Features/Core/Portable/FeaturesResources.Designer.cs b/src/Features/Core/Portable/FeaturesResources.Designer.cs
index 487b7939f315bfb7f01a8b54f97c964ec561cef6..f9c3584b86c74f962c9c7317615a1b248c4bae15 100644
--- a/src/Features/Core/Portable/FeaturesResources.Designer.cs
+++ b/src/Features/Core/Portable/FeaturesResources.Designer.cs
@@ -422,7 +422,7 @@ internal class FeaturesResources {
}
///
- /// Looks up a localized string similar to >>>>>>> master.
+ /// Looks up a localized string similar to Change '{0}' to '{1}'..
///
internal static string ChangeTo {
get {
@@ -1501,6 +1501,15 @@ internal class FeaturesResources {
}
}
+ ///
+ /// Looks up a localized string similar to Non-invoked method cannot be replaced with property..
+ ///
+ internal static string NonInvokedMethodCannotBeReplacedWithProperty {
+ get {
+ return ResourceManager.GetString("NonInvokedMethodCannotBeReplacedWithProperty", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Ceasing to access captured variable '{0}' in {1} will prevent the debug session from continuing..
///
@@ -1537,6 +1546,15 @@ internal class FeaturesResources {
}
}
+ ///
+ /// Looks up a localized string similar to Only methods with a single argument can be replaced with a property..
+ ///
+ internal static string OnlyMethodsWithASingleArgumentCanBeReplacedWithAProperty {
+ get {
+ return ResourceManager.GetString("OnlyMethodsWithASingleArgumentCanBeReplacedWithAProperty", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to operator.
///
diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx
index 4cc165517ff60f1ba5279a3c18377bfe47a84e60..33cb88d710dc86904f891d39af2e92a64d02ca93 100644
--- a/src/Features/Core/Portable/FeaturesResources.resx
+++ b/src/Features/Core/Portable/FeaturesResources.resx
@@ -851,6 +851,11 @@ Do you want to continue?
Change '{0}' to '{1}'.
->>>>>>> master
+
+
+ Non-invoked method cannot be replaced with property.
+
+
+ Only methods with a single argument can be replaced with a property.
\ No newline at end of file
diff --git a/src/Features/Core/Portable/ReplaceMethodWithProperty/IReplaceMethodWithPropertyService.cs b/src/Features/Core/Portable/ReplaceMethodWithProperty/IReplaceMethodWithPropertyService.cs
index 5e96b21bac7ef782b44a2ae562451a9e52ba3c8d..2af45e8efc612f2ee61a1e46c899ec1808abb2a2 100644
--- a/src/Features/Core/Portable/ReplaceMethodWithProperty/IReplaceMethodWithPropertyService.cs
+++ b/src/Features/Core/Portable/ReplaceMethodWithProperty/IReplaceMethodWithPropertyService.cs
@@ -8,10 +8,12 @@ namespace Microsoft.CodeAnalysis.ReplaceMethodWithProperty
interface IReplaceMethodWithPropertyService : ILanguageService
{
SyntaxNode GetMethodDeclaration(SyntaxToken token);
- SyntaxNode ConvertMethodsToProperty(SemanticModel semanticModel, SyntaxGenerator generator, GetAndSetMethods getAndSetMethods, string propertyName, bool nameChanged);
string GetMethodName(SyntaxNode methodDeclaration);
void ReplaceGetReference(SyntaxEditor editor, SyntaxToken nameToken, string propertyName, bool nameChanged);
void ReplaceSetReference(SyntaxEditor editor, SyntaxToken nameToken, string propertyName, bool nameChanged);
+
+ void ReplaceGetMethodWithProperty(SyntaxEditor editor, SemanticModel semanticModel, GetAndSetMethods getAndSetMethods, string propertyName, bool nameChanged);
+ void RemoveSetMethod(SyntaxEditor editor, SyntaxNode setMethodDeclaration);
}
internal struct GetAndSetMethods
diff --git a/src/Features/Core/Portable/ReplaceMethodWithProperty/ReplaceMethodWithPropertyCodeRefactoringProvider.cs b/src/Features/Core/Portable/ReplaceMethodWithProperty/ReplaceMethodWithPropertyCodeRefactoringProvider.cs
index d8f0eb154926624629c265c31fb46bae3bdb53b6..b5ec421eb48f30e7167ffd2e076fa66214cd9ff8 100644
--- a/src/Features/Core/Portable/ReplaceMethodWithProperty/ReplaceMethodWithPropertyCodeRefactoringProvider.cs
+++ b/src/Features/Core/Portable/ReplaceMethodWithProperty/ReplaceMethodWithPropertyCodeRefactoringProvider.cs
@@ -321,16 +321,13 @@ private async Task UpdateReferencesAsync(Solution updatedSolution, str
var root = await updatedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var editor = new SyntaxEditor(root, updatedSolution.Workspace);
- var generator = SyntaxGenerator.GetGenerator(updatedDocument);
// First replace all the get methods with properties.
foreach (var getSetPair in getSetPairs)
{
cancellationToken.ThrowIfCancellationRequested();
- var getMethod = getSetPair.GetMethodDeclaration;
- editor.ReplaceNode(getMethod, service.ConvertMethodsToProperty(
- semanticModel, generator, getSetPair, propertyName, nameChanged));
+ service.ReplaceGetMethodWithProperty(editor, semanticModel, getSetPair, propertyName, nameChanged);
}
// Then remove all the set methods.
@@ -344,7 +341,7 @@ private async Task UpdateReferencesAsync(Solution updatedSolution, str
var setMethodDocument = updatedSolution.GetDocument(setMethodDeclaration?.SyntaxTree);
if (setMethodDocument?.Id == documentId)
{
- editor.RemoveNode(setMethodDeclaration);
+ service.RemoveSetMethod(editor, setMethodDeclaration);
}
}
diff --git a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj
index 0c212078b956b16812a9a3565061d26015965468..ca05d422d146b132b50bdfaffbdd4cf9d0265f72 100644
--- a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj
+++ b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj
@@ -401,6 +401,9 @@
+
+
+
diff --git a/src/Features/VisualBasic/Portable/ReplaceMethodWithProperty/VisualBasicReplaceMethodWithPropertyService.vb b/src/Features/VisualBasic/Portable/ReplaceMethodWithProperty/VisualBasicReplaceMethodWithPropertyService.vb
new file mode 100644
index 0000000000000000000000000000000000000000..6a7dfad798fbecc211bedb7dbf7577c859f1a9c1
--- /dev/null
+++ b/src/Features/VisualBasic/Portable/ReplaceMethodWithProperty/VisualBasicReplaceMethodWithPropertyService.vb
@@ -0,0 +1,233 @@
+' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+Imports System
+Imports System.Collections.Generic
+Imports System.Composition
+Imports System.Linq
+Imports Microsoft.CodeAnalysis.CodeActions
+Imports Microsoft.CodeAnalysis.VisualBasic.Extensions
+Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
+Imports Microsoft.CodeAnalysis.Editing
+Imports Microsoft.CodeAnalysis.Formatting
+Imports Microsoft.CodeAnalysis.Host.Mef
+Imports Microsoft.CodeAnalysis.ReplaceMethodWithProperty
+
+Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithProperty
+
+ Friend Class VisualBasicReplaceMethodWithPropertyService
+ Implements IReplaceMethodWithPropertyService
+
+ Public Function GetMethodName(methodNode As SyntaxNode) As String Implements IReplaceMethodWithPropertyService.GetMethodName
+ Return DirectCast(methodNode, MethodStatementSyntax).Identifier.ValueText
+ End Function
+
+ Public Function GetMethodDeclaration(token As SyntaxToken) As SyntaxNode Implements IReplaceMethodWithPropertyService.GetMethodDeclaration
+ Dim containingMethod = token.Parent.FirstAncestorOrSelf(Of MethodStatementSyntax)
+ If containingMethod Is Nothing Then
+ Return Nothing
+ End If
+
+ Dim start = If(containingMethod.AttributeLists.Count > 0,
+ containingMethod.AttributeLists.Last().GetLastToken().GetNextToken().SpanStart,
+ containingMethod.SpanStart)
+
+ ' Offer this refactoring anywhere in the signature of the method.
+ Dim position = token.SpanStart
+ If position < start OrElse position > containingMethod.ParameterList.Span.End Then
+ Return Nothing
+ End If
+
+ Return containingMethod
+ End Function
+
+ Public Sub RemoveSetMethod(editor As SyntaxEditor, setMethodDeclaration As SyntaxNode) Implements IReplaceMethodWithPropertyService.RemoveSetMethod
+ Dim setMethodStatement = TryCast(setMethodDeclaration, MethodStatementSyntax)
+ If setMethodStatement Is Nothing Then
+ Return
+ End If
+
+ Dim methodOrBlock = GetParentIfBlock(setMethodStatement)
+ editor.RemoveNode(methodOrBlock)
+ End Sub
+
+ Public Sub ReplaceGetMethodWithProperty(
+ editor As SyntaxEditor,
+ semanticModel As SemanticModel,
+ getAndSetMethods As GetAndSetMethods,
+ propertyName As String, nameChanged As Boolean) Implements IReplaceMethodWithPropertyService.ReplaceGetMethodWithProperty
+
+ Dim getMethodDeclaration = TryCast(getAndSetMethods.GetMethodDeclaration, MethodStatementSyntax)
+ If getMethodDeclaration Is Nothing Then
+ Return
+ End If
+
+ Dim methodBlockOrStatement = GetParentIfBlock(getMethodDeclaration)
+ editor.ReplaceNode(methodBlockOrStatement,
+ ConvertMethodsToProperty(editor, semanticModel, getAndSetMethods, propertyName, nameChanged))
+ End Sub
+
+ Private Function GetParentIfBlock(declaration As MethodStatementSyntax) As DeclarationStatementSyntax
+ If declaration.IsParentKind(SyntaxKind.FunctionBlock) OrElse declaration.IsParentKind(SyntaxKind.SubBlock) Then
+ Return DirectCast(declaration.Parent, DeclarationStatementSyntax)
+ End If
+
+ Return declaration
+ End Function
+
+ Private Function ConvertMethodsToProperty(
+ editor As SyntaxEditor,
+ semanticModel As SemanticModel,
+ getAndSetMethods As GetAndSetMethods,
+ propertyName As String, nameChanged As Boolean) As DeclarationStatementSyntax
+
+ Dim generator = editor.Generator
+
+ Dim getMethodStatement = DirectCast(getAndSetMethods.GetMethodDeclaration, MethodStatementSyntax)
+ Dim setMethodStatement = TryCast(getAndSetMethods.SetMethodDeclaration, MethodStatementSyntax)
+
+ Dim propertyNameToken = GetPropertyName(getMethodStatement.Identifier, propertyName, nameChanged)
+
+ Dim newPropertyDeclaration As DeclarationStatementSyntax
+ If getAndSetMethods.SetMethod Is Nothing Then
+ Dim modifiers = getMethodStatement.Modifiers.Add(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword))
+ Dim propertyStatement = SyntaxFactory.PropertyStatement(
+ getMethodStatement.AttributeLists, modifiers, propertyNameToken, Nothing,
+ getMethodStatement.AsClause, initializer:=Nothing, implementsClause:=getMethodStatement.ImplementsClause)
+
+ If getAndSetMethods.GetMethodDeclaration.IsParentKind(SyntaxKind.FunctionBlock) Then
+ ' Get method has no body, and we have no setter. Just make a readonly property block
+ Dim accessor = SyntaxFactory.GetAccessorBlock(SyntaxFactory.GetAccessorStatement(),
+ DirectCast(getAndSetMethods.GetMethodDeclaration.Parent, MethodBlockBaseSyntax).Statements)
+ Dim accessors = SyntaxFactory.SingletonList(accessor)
+ newPropertyDeclaration = SyntaxFactory.PropertyBlock(propertyStatement, accessors)
+ Else
+ ' Get method has no body, and we have no setter. Just make a readonly property statement
+ newPropertyDeclaration = propertyStatement
+ End If
+ Else
+ Dim propertyStatement = SyntaxFactory.PropertyStatement(
+ getMethodStatement.AttributeLists, getMethodStatement.Modifiers, propertyNameToken, Nothing,
+ getMethodStatement.AsClause, initializer:=Nothing, implementsClause:=getMethodStatement.ImplementsClause)
+
+ If getAndSetMethods.GetMethodDeclaration.IsParentKind(SyntaxKind.FunctionBlock) AndAlso
+ getAndSetMethods.SetMethodDeclaration.IsParentKind(SyntaxKind.SubBlock) Then
+
+ Dim getAccessor = SyntaxFactory.GetAccessorBlock(SyntaxFactory.GetAccessorStatement(),
+ DirectCast(getAndSetMethods.GetMethodDeclaration.Parent, MethodBlockBaseSyntax).Statements)
+
+ Dim setAccessorStatement = SyntaxFactory.SetAccessorStatement()
+ setAccessorStatement = setAccessorStatement.WithParameterList(setMethodStatement?.ParameterList)
+
+ If getAndSetMethods.GetMethod.DeclaredAccessibility <> getAndSetMethods.SetMethod.DeclaredAccessibility Then
+ setAccessorStatement = DirectCast(generator.WithAccessibility(setAccessorStatement, getAndSetMethods.SetMethod.DeclaredAccessibility), AccessorStatementSyntax)
+ End If
+
+ Dim setAccessor = SyntaxFactory.SetAccessorBlock(setAccessorStatement,
+ DirectCast(getAndSetMethods.SetMethodDeclaration.Parent, MethodBlockBaseSyntax).Statements)
+
+ Dim accessors = SyntaxFactory.List({getAccessor, setAccessor})
+ newPropertyDeclaration = SyntaxFactory.PropertyBlock(propertyStatement, accessors)
+ Else
+ ' Methods don't have bodies. Just make a property statement
+ newPropertyDeclaration = propertyStatement
+ End If
+ End If
+
+ Dim trivia As IEnumerable(Of SyntaxTrivia) = getMethodStatement.GetLeadingTrivia()
+ If setMethodStatement IsNot Nothing Then
+ trivia = trivia.Concat(setMethodStatement.GetLeadingTrivia())
+ End If
+
+ newPropertyDeclaration = newPropertyDeclaration.WithLeadingTrivia(trivia)
+
+ Return newPropertyDeclaration.WithAdditionalAnnotations(Formatter.Annotation)
+ End Function
+
+ Private Function GetPropertyName(identifier As SyntaxToken, propertyName As String, nameChanged As Boolean) As SyntaxToken
+ Return If(nameChanged, SyntaxFactory.Identifier(propertyName), identifier)
+ End Function
+
+ Public Sub ReplaceGetReference(editor As SyntaxEditor, nameToken As SyntaxToken, propertyName As String, nameChanged As Boolean) Implements IReplaceMethodWithPropertyService.ReplaceGetReference
+ If nameToken.Kind() <> SyntaxKind.IdentifierToken Then
+ Return
+ End If
+
+ Dim nameNode = TryCast(nameToken.Parent, IdentifierNameSyntax)
+ If nameNode Is Nothing Then
+ Return
+ End If
+
+ Dim newName = If(nameChanged,
+ SyntaxFactory.IdentifierName(SyntaxFactory.Identifier(propertyName).WithTriviaFrom(nameToken)),
+ nameNode)
+
+ Dim parentExpression = If(nameNode.IsRightSideOfDot(), DirectCast(nameNode.Parent, ExpressionSyntax), nameNode)
+ Dim root = If(parentExpression.IsParentKind(SyntaxKind.InvocationExpression), parentExpression.Parent, parentExpression)
+
+ editor.ReplaceNode(root, parentExpression.ReplaceNode(nameNode, newName))
+ End Sub
+
+ Public Sub ReplaceSetReference(editor As SyntaxEditor, nameToken As SyntaxToken, propertyName As String, nameChanged As Boolean) Implements IReplaceMethodWithPropertyService.ReplaceSetReference
+ If nameToken.Kind() <> SyntaxKind.IdentifierToken Then
+ Return
+ End If
+
+ Dim nameNode = TryCast(nameToken.Parent, IdentifierNameSyntax)
+ If nameNode Is Nothing Then
+ Return
+ End If
+
+ Dim newName = If(nameChanged,
+ SyntaxFactory.IdentifierName(SyntaxFactory.Identifier(propertyName).WithTriviaFrom(nameToken)),
+ nameNode)
+
+ Dim parentExpression = If(nameNode.IsRightSideOfDot(), DirectCast(nameNode.Parent, ExpressionSyntax), nameNode)
+ If Not parentExpression.IsParentKind(SyntaxKind.InvocationExpression) OrElse
+ Not parentExpression.Parent.IsParentKind(SyntaxKind.ExpressionStatement) Then
+
+
+ ' Wasn't invoked. Change the name, but report a conflict.
+ Dim annotation = ConflictAnnotation.Create(FeaturesResources.NonInvokedMethodCannotBeReplacedWithProperty)
+ editor.ReplaceNode(nameNode, Function(n, g) newName.WithIdentifier(newName.Identifier.WithAdditionalAnnotations(annotation)))
+ Return
+ End If
+
+ editor.ReplaceNode(
+ parentExpression.Parent.Parent,
+ Function(statement, generator)
+ Dim expressionStatement = DirectCast(statement, ExpressionStatementSyntax)
+ Dim invocationExpression = DirectCast(expressionStatement.Expression, InvocationExpressionSyntax)
+ Dim expression = invocationExpression.Expression
+ Dim name = If(expression.Kind() = SyntaxKind.SimpleMemberAccessExpression,
+ DirectCast(expression, MemberAccessExpressionSyntax).Name,
+ If(expression.Kind() = SyntaxKind.IdentifierName, DirectCast(expression, IdentifierNameSyntax), Nothing))
+
+ If name Is Nothing Then
+ Return statement
+ End If
+
+ If invocationExpression.ArgumentList?.Arguments.Count <> 1 Then
+ Return statement
+ End If
+
+ Dim result As SyntaxNode = SyntaxFactory.SimpleAssignmentStatement(
+ expression.ReplaceNode(name, newName),
+ invocationExpression.ArgumentList.Arguments(0).GetExpression())
+
+ Return result
+ End Function)
+ End Sub
+
+ Private Shared Function IsInvocationName(nameNode As IdentifierNameSyntax, invocationExpression As ExpressionSyntax) As Boolean
+ If invocationExpression Is nameNode Then
+ Return True
+ End If
+
+ If nameNode.IsAnyMemberAccessExpressionName() AndAlso nameNode.Parent Is invocationExpression Then
+ Return True
+ End If
+
+ Return False
+ End Function
+ End Class
+End Namespace
\ No newline at end of file