提交 748c5e43 编写于 作者: C chborl

Incorporated new feedback

上级 90b850fe
......@@ -7,7 +7,7 @@
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertAutoPropertyToFullPropertyTests
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertAutoPropertyToFullProperty
{
public partial class ConvertAutoPropertyToFullPropertyTests : AbstractCSharpCodeActionTest
{
......@@ -705,6 +705,26 @@ public int Goo
await TestInRegularAndScriptAsync(text, expected, options: DoNotPreferExpressionBodiedAccessors);
}
[Fact, Trait(Traits.Feature, Traits.Features.ConvertAutoPropertyToFullProperty)]
public async Task GetterOnlyExpressionBodies()
{
var text = @"
class goo
{
public int G[||]oo { get;}
}
";
var expected = @"
class goo
{
private readonly int _goo;
public int Goo => _goo;
}
";
await TestInRegularAndScriptAsync(text, expected, options: PreferExpressionBodiesOnAccessorsAndMethods);
}
[Fact, Trait(Traits.Feature, Traits.Features.ConvertAutoPropertyToFullProperty)]
public async Task SetterOnly()
{
......@@ -712,7 +732,7 @@ public async Task SetterOnly()
class goo
{
public int G[||]oo
````{
{
set {}
}
}
......@@ -728,7 +748,7 @@ class goo
{
private int _testgoo;
public int testf[||]oo {get => _testgoo; set => _testgoo = value; }
public int testg[||]oo {get => _testgoo; set => _testgoo = value; }
}
";
await TestMissingAsync(text);
......@@ -926,7 +946,7 @@ public async Task InInterface()
var text = @"
interface IGoo
{
public int Goo { get; s[||]et; set; }
public int Goo { get; s[||]et; }
}
";
await TestMissingAsync(text);
......
using System;
// Copyright(c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt In the project root For license information
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.Formatting;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings;
using Microsoft.CodeAnalysis.NamingStyles;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Simplification;
using static Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles.SymbolSpecification;
using Microsoft.CodeAnalysis.Editing;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertAutoPropertyToFullPropertyTests
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertAutoPropertyToFullProperty
{
public partial class ConvertAutoPropertyToFullPropertyTests : AbstractCSharpCodeActionTest
{
private IDictionary<OptionKey, object> PreferExpressionBodiedAccessorsWhenPossible =>
OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement));
private IDictionary<OptionKey, object> PreferExpressionBodiedAccessorsWhenPossible
=> OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement));
private IDictionary<OptionKey, object> PreferExpressionBodiedAccessorsWhenOnSingleLine =>
OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenOnSingleLineWithNoneEnforcement));
private IDictionary<OptionKey, object> PreferExpressionBodiedAccessorsWhenOnSingleLine
=> OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenOnSingleLineWithNoneEnforcement));
private IDictionary<OptionKey, object> DoNotPreferExpressionBodiedAccessors =>
OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithNoneEnforcement));
private IDictionary<OptionKey, object> DoNotPreferExpressionBodiedAccessors
=> OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithNoneEnforcement));
private IDictionary<OptionKey, object> DoNotPreferExpressionBodiedAccessorsAndPropertyOpenBraceOnSameLine =>
OptionsSet(
private IDictionary<OptionKey, object> DoNotPreferExpressionBodiedAccessorsAndPropertyOpenBraceOnSameLine
=> OptionsSet(
SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithNoneEnforcement),
SingleOption(CSharpFormattingOptions.NewLinesForBracesInProperties, false));
private IDictionary<OptionKey, object> DoNotPreferExpressionBodiedAccessorsAndAccessorOpenBraceOnSameLine =>
OptionsSet(
private IDictionary<OptionKey, object> DoNotPreferExpressionBodiedAccessorsAndAccessorOpenBraceOnSameLine
=> OptionsSet(
SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithNoneEnforcement),
SingleOption(CSharpFormattingOptions.NewLinesForBracesInAccessors, false));
private IDictionary<OptionKey, object> UseCustomFieldName =>
OptionsSet(
private IDictionary<OptionKey, object> PreferExpressionBodiesOnAccessorsAndMethods
=> OptionsSet(
SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement),
SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement));
private IDictionary<OptionKey, object> UseCustomFieldName
=> OptionsSet(
SingleOption(SimplificationOptions.NamingPreferences, CreateCustomFieldNamingStylePreference()),
SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithNoneEnforcement));
private IDictionary<OptionKey, object> UseCustomStaticFieldName =>
OptionsSet(
private IDictionary<OptionKey, object> UseCustomStaticFieldName
=> OptionsSet(
SingleOption(SimplificationOptions.NamingPreferences, CreateCustomStaticFieldNamingStylePreference()),
SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithNoneEnforcement));
......
......@@ -2,7 +2,7 @@
Imports Microsoft.CodeAnalysis.CodeRefactorings
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings
Imports Microsoft.CodeAnalysis.VisualBasic.VisualBasicConvertAutoPropertyToFullPropertyCodeRefactoringProvider
Imports Microsoft.CodeAnalysis.VisualBasic.ConvertAutoPropertyToFullProperty
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ValidateFormatString
Public Class ConvertAutoPropertyToFullPropertyTests
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Composition;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.ConvertAutoPropertyToFullProperty;
......@@ -13,6 +11,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.ConvertAutoPropertyToFullProperty
......@@ -42,7 +41,9 @@ internal override SyntaxNode GetProperty(SyntaxToken token)
return containingProperty;
}
internal override async Task<(SyntaxNode newGetAccessor, SyntaxNode newSetAccessor)> GetNewAccessorsAsync(Document document, SyntaxNode property, string fieldName, SyntaxGenerator generator, CancellationToken cancellationToken)
internal override (SyntaxNode newGetAccessor, SyntaxNode newSetAccessor) GetNewAccessors(
DocumentOptionSet options, SyntaxNode property,
string fieldName, SyntaxGenerator generator)
{
// C# might have trivia with the accessors that needs to be preserved.
// so we will update the existing accessors instead of creating new ones
......@@ -50,114 +51,59 @@ internal override async Task<(SyntaxNode newGetAccessor, SyntaxNode newSetAccess
var existingAccessors = GetExistingAccessors(accessorListSyntax);
var getAccessorStatement = generator.ReturnStatement(generator.IdentifierName(fieldName));
var newGetter = await AddStatementsToAccessorAsync(
document,
existingAccessors.getAccessor,
getAccessorStatement,
generator,
cancellationToken).ConfigureAwait(false);
var newGetter = GetUpdatedAccessor(
options, existingAccessors.getAccessor,
getAccessorStatement, generator);
SyntaxNode newSetter = null;
if (existingAccessors.setAccessor != null)
{
var setAccessorStatement = generator.ExpressionStatement(generator.AssignmentStatement(
generator.IdentifierName(fieldName),
generator.IdentifierName("value")));
newSetter = await AddStatementsToAccessorAsync(
document,
existingAccessors.setAccessor,
setAccessorStatement,
generator,
cancellationToken).ConfigureAwait(false);
generator.IdentifierName(fieldName),
generator.IdentifierName("value")));
newSetter = GetUpdatedAccessor(
options, existingAccessors.setAccessor,
setAccessorStatement, generator);
}
return (newGetAccessor: newGetter, newSetAccessor: newSetter);
}
private (AccessorDeclarationSyntax getAccessor, AccessorDeclarationSyntax setAccessor) GetExistingAccessors(AccessorListSyntax accessorListSyntax)
{
AccessorDeclarationSyntax getter = null;
AccessorDeclarationSyntax setter = null;
foreach (var accessor in accessorListSyntax.Accessors)
{
if (accessor.Kind() == SyntaxKind.GetAccessorDeclaration)
{
getter = accessor;
}
else if (accessor.Kind() == SyntaxKind.SetAccessorDeclaration)
{
setter = accessor;
}
}
return (getAccessor: getter, setAccessor: setter);
}
private static void GetExistingAccessors(AccessorListSyntax accessorListSyntax, ref AccessorDeclarationSyntax getAccessor, ref AccessorDeclarationSyntax setAccessor)
{
foreach (var accessor in accessorListSyntax.Accessors)
{
if (accessor.Kind() == SyntaxKind.GetAccessorDeclaration)
{
getAccessor = accessor;
}
else if (accessor.Kind() == SyntaxKind.SetAccessorDeclaration)
{
setAccessor = accessor;
}
}
}
private (AccessorDeclarationSyntax getAccessor, AccessorDeclarationSyntax setAccessor)
GetExistingAccessors(AccessorListSyntax accessorListSyntax)
=> (accessorListSyntax.Accessors.FirstOrDefault(a => a.IsKind(SyntaxKind.GetAccessorDeclaration)),
accessorListSyntax.Accessors.FirstOrDefault(a => a.IsKind(SyntaxKind.SetAccessorDeclaration)));
private async Task<SyntaxNode> AddStatementsToAccessorAsync(
Document document,
SyntaxNode accessor,
SyntaxNode statement,
SyntaxGenerator generator,
CancellationToken cancellationToken)
private SyntaxNode GetUpdatedAccessor(DocumentOptionSet options,
SyntaxNode accessor, SyntaxNode statement, SyntaxGenerator generator)
{
var newAccessor = UpdateAccessor(accessor, statement);
newAccessor = await ConvertToExpressionBodyIfDesiredAsync(
document,
newAccessor,
cancellationToken).ConfigureAwait(false);
var newAccessor = AddStatement(accessor, statement);
var accessorDeclarationSyntax = (AccessorDeclarationSyntax)newAccessor;
return await Formatter.FormatAsync(newAccessor, document.Project.Solution.Workspace).ConfigureAwait(false);
}
internal async Task<SyntaxNode> ConvertToExpressionBodyIfDesiredAsync(
Document document,
SyntaxNode accessor,
CancellationToken cancellationToken)
{
var accessorDeclarationSyntax = (AccessorDeclarationSyntax)accessor;
var preference = await GetExpressionBodyPreferenceAsync(document, cancellationToken).ConfigureAwait(false);
var preference = GetAccessorExpressionBodyPreference(options);
if (preference == ExpressionBodyPreference.Never)
{
return accessorDeclarationSyntax.WithSemicolonToken(default);
}
var ableToConvert = accessorDeclarationSyntax.Body.TryConvertToExpressionBody(
accessorDeclarationSyntax.Kind(),
accessor.SyntaxTree.Options,
preference,
out var arrowExpression,
out var semicolonToken);
// Should always be able to convert to expression body since we are creating the accessor
// and know that it only has one statement
Debug.Assert(ableToConvert);
if (!accessorDeclarationSyntax.Body.TryConvertToExpressionBody(
accessorDeclarationSyntax.Kind(), accessor.SyntaxTree.Options, preference,
out var arrowExpression, out var semicolonToken))
{
return accessorDeclarationSyntax.WithSemicolonToken(default);
};
return accessorDeclarationSyntax
.WithExpressionBody(arrowExpression)
.WithBody(null)
.WithSemicolonToken(semicolonToken);
.WithSemicolonToken(semicolonToken)
.WithAdditionalAnnotations(Formatter.Annotation);
}
internal SyntaxNode UpdateAccessor(SyntaxNode accessor, SyntaxNode statement)
internal SyntaxNode AddStatement(SyntaxNode accessor, SyntaxNode statement)
{
var blockSyntax = SyntaxFactory.Block(
SyntaxFactory.Token(SyntaxKind.OpenBraceToken),
SyntaxFactory.Token(SyntaxKind.OpenBraceToken).WithLeadingTrivia(SyntaxFactory.CarriageReturnLineFeed),
new SyntaxList<StatementSyntax>((StatementSyntax)statement),
SyntaxFactory.Token(SyntaxKind.CloseBraceToken)
.WithTrailingTrivia(((AccessorDeclarationSyntax)accessor).SemicolonToken.TrailingTrivia));
......@@ -165,16 +111,38 @@ internal SyntaxNode UpdateAccessor(SyntaxNode accessor, SyntaxNode statement)
return ((AccessorDeclarationSyntax)accessor).WithBody(blockSyntax);
}
internal async Task<ExpressionBodyPreference> GetExpressionBodyPreferenceAsync(
Document document,
CancellationToken cancellationToken)
internal override SyntaxNode ConvertPropertyToExpressionBodyIfDesired(
DocumentOptionSet options, SyntaxNode property)
{
var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
return options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors).Value;
var propertyDeclaration = (PropertyDeclarationSyntax)property;
var preference = GetPropertyExpressionBodyPreference(options);
if (preference == ExpressionBodyPreference.Never)
{
return propertyDeclaration.WithSemicolonToken(default);
}
// if there is a get accessor only, we can move the expression body to the property
if (propertyDeclaration.AccessorList?.Accessors.Count == 1 &&
propertyDeclaration.AccessorList.Accessors[0].Kind() == SyntaxKind.GetAccessorDeclaration)
{
var getAccessor = propertyDeclaration.AccessorList.Accessors[0];
if (getAccessor.ExpressionBody != null)
{
return propertyDeclaration.WithExpressionBody(getAccessor.ExpressionBody)
.WithSemicolonToken(getAccessor.SemicolonToken)
.WithAccessorList(null);
}
}
return propertyDeclaration.WithSemicolonToken(default);
}
private bool IsEmpty(AccessorDeclarationSyntax accessor)
=> accessor.Body == null && accessor.ExpressionBody == null;
internal ExpressionBodyPreference GetAccessorExpressionBodyPreference(DocumentOptionSet options)
=> options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors).Value;
internal ExpressionBodyPreference GetPropertyExpressionBodyPreference(DocumentOptionSet options)
=> options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties).Value;
internal override string GetUniqueName(string fieldName, IPropertySymbol property)
=> NameGenerator.GenerateUniqueName(fieldName, n => !property.ContainingType.GetMembers(n).Any());
......
......@@ -11,6 +11,7 @@
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Simplification;
using static Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles.SymbolSpecification;
......@@ -23,10 +24,10 @@ internal abstract class AbstractConvertAutoPropertyToFullPropertyCodeRefactoring
internal abstract string GetUniqueName(string fieldName, IPropertySymbol property);
internal abstract SyntaxNode GetInitializerValue(SyntaxNode property);
internal abstract SyntaxNode GetPropertyWithoutInitializer(SyntaxNode property);
internal abstract Task<(SyntaxNode newGetAccessor, SyntaxNode newSetAccessor)> GetNewAccessorsAsync(
Document document, SyntaxNode property, string fieldName, SyntaxGenerator generator,
CancellationToken cancellationToken);
internal abstract (SyntaxNode newGetAccessor, SyntaxNode newSetAccessor) GetNewAccessors(
DocumentOptionSet options, SyntaxNode property, string fieldName, SyntaxGenerator generator);
internal abstract SyntaxNode GetTypeBlock(SyntaxNode syntaxNode);
internal abstract SyntaxNode ConvertPropertyToExpressionBodyIfDesired(DocumentOptionSet options, SyntaxNode fullProperty);
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
......@@ -41,7 +42,15 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
return;
}
if (!(await IsValidAutoProperty(property, document, cancellationToken).ConfigureAwait(false)))
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var propertySymbol = semanticModel.GetDeclaredSymbol(property) as IPropertySymbol;
if (propertySymbol == null)
{
return;
}
if (!(IsValidAutoProperty(property, propertySymbol)))
{
return;
}
......@@ -49,46 +58,23 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
context.RegisterRefactoring(
new ConvertAutoPropertyToFullPropertyCodeAction(
FeaturesResources.Convert_to_full_property,
c => ExpandToFullPropertyAsync(
document,
property,
root,
cancellationToken)));
c => ExpandToFullPropertyAsync(document, property, propertySymbol, root, cancellationToken)));
}
internal async Task<bool> IsValidAutoProperty(
SyntaxNode property, Document document, CancellationToken cancellationToken)
internal bool IsValidAutoProperty(SyntaxNode property, IPropertySymbol propertySymbol)
{
var semanticModel = await document.GetSemanticModelAsync(cancellationToken)
.ConfigureAwait(false);
var propertySymbol = semanticModel.GetDeclaredSymbol(property) as IPropertySymbol;
var members = propertySymbol.ContainingType.GetMembers()
.Where(n => n.Kind == SymbolKind.Field);
IFieldSymbol fieldSymbol;
foreach (var member in members)
{
fieldSymbol = (IFieldSymbol)member;
if (fieldSymbol.AssociatedSymbol?.Name == propertySymbol.Name)
{
return true;
}
}
return false;
var fields = propertySymbol.ContainingType.GetMembers().OfType<IFieldSymbol>();
var field = fields.FirstOrDefault(f => propertySymbol.Equals(f.AssociatedSymbol));
return field != null;
}
private async Task<Document> ExpandToFullPropertyAsync(
Document document,
SyntaxNode property,
IPropertySymbol propertySymbol,
SyntaxNode root,
CancellationToken cancellationToken)
{
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var propertySymbol = semanticModel.GetDeclaredSymbol(property) as IPropertySymbol;
var rules = await GetNamingRulesAsync(
document,
cancellationToken).ConfigureAwait(false);
......@@ -112,18 +98,18 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
/// for both static and non-static fields. The standard naming rules are added at the end
/// so they will only be used if the user hasn't specified a preference.
/// </summary>
/// <param name="document"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private static async Task<ImmutableArray<NamingRule>> GetNamingRulesAsync(
Document document,
CancellationToken cancellationToken)
{
const string defaultStaticFieldPrefix = "s_";
const string defaultFieldPrefix = "_";
var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
var namingPreferencesOption = optionSet.GetOption(SimplificationOptions.NamingPreferences);
var rules = namingPreferencesOption.CreateRules().NamingRules
.AddRange(GetDefaultRule(ImmutableArray.Create(new ModifierKind(ModifierKindEnum.IsStatic)), "s_"))
.AddRange(GetDefaultRule(ImmutableArray.Create<ModifierKind>(), "_"));
.AddRange(GetDefaultRule(ImmutableArray.Create(new ModifierKind(ModifierKindEnum.IsStatic)), defaultStaticFieldPrefix))
.AddRange(GetDefaultRule(ImmutableArray.Create<ModifierKind>(), defaultFieldPrefix));
return rules;
}
......@@ -145,21 +131,18 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
DiagnosticSeverity.Hidden));
}
private string GenerateFieldName(
IPropertySymbol property,
ImmutableArray<NamingRule> rules)
private string GenerateFieldName(IPropertySymbol property, ImmutableArray<NamingRule> rules)
{
var propertyName = property.Name;
var fieldName = "";
var isStatic = property.IsStatic;
foreach (var rule in rules)
{
if (rule.SymbolSpecification.AppliesTo(
new SymbolKindOrTypeKind(SymbolKind.Field),
isStatic ? DeclarationModifiers.Static : DeclarationModifiers.None,
property.IsStatic ? DeclarationModifiers.Static : DeclarationModifiers.None,
Accessibility.Private))
{
fieldName = rule.NamingStyle.MakeCompliant(propertyName).Single();
fieldName = rule.NamingStyle.MakeCompliant(propertyName).First();
break;
}
}
......@@ -168,41 +151,38 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
}
private async Task<SyntaxNode> ExpandPropertyAndAddFieldAsync(
Document document,
SyntaxNode property,
SyntaxNode root,
IPropertySymbol propertySymbol,
string fieldName,
SyntaxGenerator generator,
Document document, SyntaxNode property, SyntaxNode root,
IPropertySymbol propertySymbol, string fieldName, SyntaxGenerator generator,
CancellationToken cancellationToken)
{
var workspace = document.Project.Solution.Workspace;
var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
// Create full property. If the auto property had an initial value
// we need to remove it and later add it to the backing field
var accessorTuple = await GetNewAccessorsAsync(
document,
property,
fieldName,
generator,
cancellationToken)
.ConfigureAwait(false);
var accessorTuple = GetNewAccessors(options, property,fieldName, generator);
var fullProperty = generator
.WithAccessorDeclarations(GetPropertyWithoutInitializer(property), accessorTuple.newSetAccessor == null ? new SyntaxNode[] { accessorTuple.newGetAccessor } : new SyntaxNode[] { accessorTuple.newGetAccessor, accessorTuple.newSetAccessor })
.WithLeadingTrivia(property.GetLeadingTrivia())
.WithAdditionalAnnotations(Formatter.Annotation);
.WithAccessorDeclarations(
GetPropertyWithoutInitializer(property),
accessorTuple.newSetAccessor == null
? new SyntaxNode[] { accessorTuple.newGetAccessor }
: new SyntaxNode[] { accessorTuple.newGetAccessor, accessorTuple.newSetAccessor })
.WithLeadingTrivia(property.GetLeadingTrivia());
fullProperty = ConvertPropertyToExpressionBodyIfDesired(options,fullProperty);
var editor = new SyntaxEditor(root, workspace);
editor.ReplaceNode(property, fullProperty);
editor.ReplaceNode(property, fullProperty.WithAdditionalAnnotations(Formatter.Annotation));
// add backing field, plus initializer if it exists
var newField = CodeGenerationSymbolFactory.CreateFieldSymbol(default, Accessibility.Private, DeclarationModifiers.From(propertySymbol), propertySymbol.Type, fieldName, initializer: GetInitializerValue(property));
var containingType = GetTypeBlock(propertySymbol.ContainingType.DeclaringSyntaxReferences.FirstOrDefault().GetSyntax(cancellationToken));
editor.ReplaceNode(containingType, (currentTypeDecl, _) =>
{
return CodeGenerator.AddFieldDeclaration(currentTypeDecl, newField, workspace);
});
var newField = CodeGenerationSymbolFactory.CreateFieldSymbol(
default, Accessibility.Private,
DeclarationModifiers.From(propertySymbol),
propertySymbol.Type, fieldName,
initializer: GetInitializerValue(property));
var containingType = GetTypeBlock(
propertySymbol.ContainingType.DeclaringSyntaxReferences.FirstOrDefault().GetSyntax(cancellationToken));
editor.ReplaceNode(containingType, (currentTypeDecl, _)
=> CodeGenerator.AddFieldDeclaration(currentTypeDecl, newField, workspace)
.WithAdditionalAnnotations(Formatter.Annotation));
return editor.GetChangedRoot();
}
......
......@@ -5,16 +5,17 @@ Imports System.Threading
Imports Microsoft.CodeAnalysis.CodeRefactorings
Imports Microsoft.CodeAnalysis.ConvertAutoPropertyToFullProperty
Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.VisualBasicConvertAutoPropertyToFullPropertyCodeRefactoringProvider
Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertAutoPropertyToFullProperty
<ExportCodeRefactoringProvider(LanguageNames.VisualBasic, Name:=NameOf(VisualBasicConvertAutoPropertyToFullPropertyCodeRefactoringProvider)), [Shared]>
Friend Class VisualBasicConvertAutoPropertyToFullPropertyCodeRefactoringProvider
Inherits AbstractConvertAutoPropertyToFullPropertyCodeRefactoringProvider
Friend Overrides Function GetProperty(token As SyntaxToken) As SyntaxNode
Dim containingProperty = token.Parent.FirstAncestorOrSelf(Of PropertyStatementSyntax)
If (containingProperty Is Nothing) Then
If containingProperty Is Nothing Then
Return Nothing
End If
......@@ -24,7 +25,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VisualBasicConvertAutoPropertyToFul
' Offer this refactoring anywhere in the signature of the property.
Dim position = token.SpanStart
If (position < start) Then
If position < start Then
Return Nothing
End If
......@@ -36,17 +37,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VisualBasicConvertAutoPropertyToFul
Return containingProperty
End Function
Friend Overrides Function GetNewAccessorsAsync(
document As Document,
propertyNode As SyntaxNode,
fieldName As String,
generator As SyntaxGenerator,
cancellationToken As CancellationToken) As Task(Of (newGetAccessor As SyntaxNode, newSetAccessor As SyntaxNode))
Friend Overrides Function GetNewAccessors(options As DocumentOptionSet, propertyNode As SyntaxNode,
fieldName As String, generator As SyntaxGenerator) _
As (newGetAccessor As SyntaxNode, newSetAccessor As SyntaxNode)
Dim returnStatement = New SyntaxList(Of StatementSyntax)(DirectCast(generator.ReturnStatement(generator.IdentifierName(fieldName)), StatementSyntax))
Dim returnStatement = New SyntaxList(Of StatementSyntax)(DirectCast(generator.ReturnStatement(
generator.IdentifierName(fieldName)), StatementSyntax))
Dim getAccessor As SyntaxNode = SyntaxFactory.GetAccessorBlock(
SyntaxFactory.GetAccessorStatement(),
returnStatement)
SyntaxFactory.GetAccessorStatement(),
returnStatement)
Dim propertySyntax = DirectCast(propertyNode, PropertyStatementSyntax)
......@@ -54,19 +53,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VisualBasicConvertAutoPropertyToFul
If IsReadOnly(propertySyntax) Then
setAccessor = Nothing
Else
Dim setStatement = New SyntaxList(Of StatementSyntax)(DirectCast(generator.ExpressionStatement(generator.AssignmentStatement(
generator.IdentifierName(fieldName),
generator.IdentifierName("Value"))), StatementSyntax))
Dim setStatement = New SyntaxList(Of StatementSyntax)(DirectCast(generator.ExpressionStatement(
generator.AssignmentStatement(generator.IdentifierName(fieldName),
generator.IdentifierName("Value"))), StatementSyntax))
setAccessor = SyntaxFactory.SetAccessorBlock(
SyntaxFactory.SetAccessorStatement(),
setStatement)
SyntaxFactory.SetAccessorStatement(),
setStatement)
End If
Return Task.FromResult((getAccessor, setAccessor))
Return (getAccessor, setAccessor)
End Function
Private Function IsReadOnly(propertySyntax As PropertyStatementSyntax) As Boolean
Dim modifiers = propertySyntax.GetModifiers()
For Each modifier In modifiers
If modifier.IsKind(SyntaxKind.ReadOnlyKeyword) Then
......@@ -78,15 +76,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VisualBasicConvertAutoPropertyToFul
End Function
Friend Overrides Function GetUniqueName(fieldName As String, propertySymbol As IPropertySymbol) As String
' In VB, auto properties have a hidden backing field that is named using the property
' name preceded by an underscore. If the parameter 'fieldName' is the same as this
' hidden field, the NameGenerator.GenerateUniqueName method will incorrectly think
' there is already a member with that name. So we need to check for that case first.
If (String.Equals(fieldName.ToLower(), "_" & propertySymbol.Name.ToLower())) Then
Return fieldName
Else
Return NameGenerator.GenerateUniqueName(fieldName, Function(n) propertySymbol.ContainingType.GetMembers(n).IsEmpty())
End If
' In VB, auto properties have an implicit backing field that is named using the property
' name preceded by an underscore. We will use this as the field name so we don't mess up
' any existing references to this field.
Return fieldName
End Function
Friend Overrides Function GetTypeBlock(syntaxNode As SyntaxNode) As SyntaxNode
......@@ -100,5 +93,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VisualBasicConvertAutoPropertyToFul
Friend Overrides Function GetPropertyWithoutInitializer(propertyNode As SyntaxNode) As SyntaxNode
Return DirectCast(propertyNode, PropertyStatementSyntax).WithInitializer(Nothing)
End Function
Friend Overrides Function ConvertPropertyToExpressionBodyIfDesired(options As DocumentOptionSet, propertyNode As SyntaxNode) As SyntaxNode
Return propertyNode
End Function
End Class
End Namespace
......@@ -79,12 +79,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
End If
Dim initializerNode = TryCast(CodeGenerationFieldInfo.GetInitializer(field), ExpressionSyntax)
Dim initializer As EqualsValueSyntax
If (initializerNode IsNot Nothing) Then
initializer = SyntaxFactory.EqualsValue(initializerNode)
Else
initializer = GenerateEqualsValue(field)
End If
Dim initializer = If(initializerNode IsNot Nothing, SyntaxFactory.EqualsValue(initializerNode), GenerateEqualsValue(field))
Dim fieldDeclaration =
SyntaxFactory.FieldDeclaration(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册