提交 54d3f8a4 编写于 作者: C Cyrus Najmabadi

Add a setter to the auto-prop if the field is written to outside of a constructor.

上级 5323f402
...@@ -60,13 +60,18 @@ private async Task<Solution> ProcessResult(CodeFixContext context, Diagnostic di ...@@ -60,13 +60,18 @@ private async Task<Solution> ProcessResult(CodeFixContext context, Diagnostic di
var propertySymbol = (IPropertySymbol)propertySemanticModel.GetDeclaredSymbol(property); var propertySymbol = (IPropertySymbol)propertySemanticModel.GetDeclaredSymbol(property);
Debug.Assert(fieldDocument.Project == propertyDocument.Project); Debug.Assert(fieldDocument.Project == propertyDocument.Project);
var compilation = await fieldDocument.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var project = fieldDocument.Project;
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
// First, rename all usages of the field to point at the property. Except don't actually
// rename the field itself. We want to be able to find it again post rename.
var solution = context.Document.Project.Solution; var solution = context.Document.Project.Solution;
var updatedSolution = await Renamer.RenameSymbolAsync(solution, var fieldLocations = await Renamer.GetRenameLocationsAsync(solution, fieldSymbol, solution.Workspace.Options, cancellationToken).ConfigureAwait(false);
fieldSymbol, propertySymbol.Name, solution.Workspace.Options,
// First, create the updated property we want to replace the old property with
var updatedProperty = UpdateProperty(project, fieldSymbol, propertySymbol, property, fieldLocations, cancellationToken);
// Now, rename all usages of the field to point at the property. Except don't actually
// rename the field itself. We want to be able to find it again post rename.
var updatedSolution = await Renamer.RenameAsync(fieldLocations, propertySymbol.Name,
location => !location.SourceSpan.IntersectsWith(declaratorLocation.SourceSpan), location => !location.SourceSpan.IntersectsWith(declaratorLocation.SourceSpan),
symbols => HasConflict(symbols, propertySymbol, compilation, cancellationToken), symbols => HasConflict(symbols, propertySymbol, compilation, cancellationToken),
cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
...@@ -90,8 +95,6 @@ private async Task<Solution> ProcessResult(CodeFixContext context, Diagnostic di ...@@ -90,8 +95,6 @@ private async Task<Solution> ProcessResult(CodeFixContext context, Diagnostic di
var fieldDeclaration = (FieldDeclarationSyntax)declarator.Parent.Parent; var fieldDeclaration = (FieldDeclarationSyntax)declarator.Parent.Parent;
var nodeToRemove = fieldDeclaration.Declaration.Variables.Count > 1 ? declarator : (SyntaxNode)fieldDeclaration; var nodeToRemove = fieldDeclaration.Declaration.Variables.Count > 1 ? declarator : (SyntaxNode)fieldDeclaration;
var updatedProperty = UpdateProperty(property);
const SyntaxRemoveOptions options = SyntaxRemoveOptions.KeepUnbalancedDirectives | SyntaxRemoveOptions.AddElasticMarker; const SyntaxRemoveOptions options = SyntaxRemoveOptions.KeepUnbalancedDirectives | SyntaxRemoveOptions.AddElasticMarker;
if (fieldDocument == propertyDocument) if (fieldDocument == propertyDocument)
{ {
...@@ -149,9 +152,86 @@ private async Task<Solution> ProcessResult(CodeFixContext context, Diagnostic di ...@@ -149,9 +152,86 @@ private async Task<Solution> ProcessResult(CodeFixContext context, Diagnostic di
return null; return null;
} }
private PropertyDeclarationSyntax UpdateProperty(PropertyDeclarationSyntax propertyDeclaration) private PropertyDeclarationSyntax UpdateProperty(
Project project, IFieldSymbol fieldSymbol, IPropertySymbol propertySymbol, PropertyDeclarationSyntax propertyDeclaration,
RenameLocations fieldRenameLocations, CancellationToken cancellationToken)
{
var updatedProperty = propertyDeclaration.WithAccessorList(UpdateAccessorList(propertyDeclaration.AccessorList));
// We may need to add a setter if the field is written to outside of the constructor
// of it's class.
if (AddSetterIfNecessary(fieldSymbol, propertyDeclaration, fieldRenameLocations, cancellationToken))
{
var accessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
var generator = SyntaxGenerator.GetGenerator(project);
if (fieldSymbol.DeclaredAccessibility != propertySymbol.DeclaredAccessibility)
{
accessor = (AccessorDeclarationSyntax)generator.WithAccessibility(accessor, fieldSymbol.DeclaredAccessibility);
}
updatedProperty = updatedProperty.AddAccessorListAccessors(accessor);
}
return updatedProperty;
}
private bool AddSetterIfNecessary(
IFieldSymbol fieldSymbol,
PropertyDeclarationSyntax propertyDeclaration,
RenameLocations fieldRenameLocations,
CancellationToken cancellationToken)
{
if (propertyDeclaration.AccessorList.Accessors.Any(SyntaxKind.SetAccessorDeclaration))
{
// No need to add an setter if we already have one.
return false;
}
// If the original field was written to outside of a constructor (or the property
// we're converting), then we'll need to add a setter to the property we're creating.
var containingTypeNodes = fieldSymbol.ContainingType.DeclaringSyntaxReferences.Select(s => s.GetSyntax(cancellationToken)).ToImmutableArray();
return fieldRenameLocations.Locations.Any(loc => NeedsSetter(loc, containingTypeNodes, propertyDeclaration, cancellationToken));
}
private bool NeedsSetter(
RenameLocation location,
ImmutableArray<SyntaxNode> containingTypeNodes,
PropertyDeclarationSyntax propertyDeclaration,
CancellationToken cancellationToken)
{ {
return propertyDeclaration.WithAccessorList(UpdateAccessorList(propertyDeclaration.AccessorList)); if (!location.IsWrittenTo)
{
// We don't need a setter if we're not writing to this field.
return false;
}
var node = location.Location.FindToken(cancellationToken).Parent;
while (node != null)
{
if (node == propertyDeclaration)
{
// We don't need a setter if we're a reference in the property we're replacing.
return false;
}
if (node.IsKind(SyntaxKind.ConstructorDeclaration))
{
// If we're written to in a constructor in the field's class, we don't need
// a setter.
if (containingTypeNodes.Contains(node.Parent))
{
return false;
}
}
node = node.Parent;
}
// We do need a setter
return true;
} }
private AccessorListSyntax UpdateAccessorList(AccessorListSyntax accessorList) private AccessorListSyntax UpdateAccessorList(AccessorListSyntax accessorList)
......
...@@ -191,5 +191,29 @@ public void TestUpdateReferencesConflictResolution() ...@@ -191,5 +191,29 @@ public void TestUpdateReferencesConflictResolution()
@"class Class { [|int i|]; int P { get { return i; } } public Class(int P) { i = 1; } }", @"class Class { [|int i|]; int P { get { return i; } } public Class(int P) { i = 1; } }",
@"class Class { int P { get; } public Class(int P) { this.P = 1; } }"); @"class Class { int P { get; } public Class(int P) { this.P = 1; } }");
} }
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseAutoProperty)]
public void TestWriteInConstructor()
{
Test(
@"class Class { [|int i|]; int P { get { return i; } } public Class() { i = 1; } }",
@"class Class { int P { get; } public Class() { P = 1; } }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseAutoProperty)]
public void TestWriteInNotInConstructor1()
{
Test(
@"class Class { [|int i|]; int P { get { return i; } } public Foo() { i = 1; } }",
@"class Class { int P { get; set; } public Foo() { P = 1; } }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseAutoProperty)]
public void TestWriteInNotInConstructor2()
{
Test(
@"class Class { [|int i|]; public int P { get { return i; } } public Foo() { i = 1; } }",
@"class Class { public int P { get; private set; } public Foo() { P = 1; } }");
}
} }
} }
...@@ -14,12 +14,12 @@ internal abstract partial class AbstractEditorInlineRenameService ...@@ -14,12 +14,12 @@ internal abstract partial class AbstractEditorInlineRenameService
{ {
private class InlineRenameLocationSet : IInlineRenameLocationSet private class InlineRenameLocationSet : IInlineRenameLocationSet
{ {
private readonly RenameLocationSet _renameLocationSet; private readonly RenameLocations _renameLocationSet;
private readonly SymbolInlineRenameInfo _renameInfo; private readonly SymbolInlineRenameInfo _renameInfo;
public IList<InlineRenameLocation> Locations { get; } public IList<InlineRenameLocation> Locations { get; }
public InlineRenameLocationSet(SymbolInlineRenameInfo renameInfo, RenameLocationSet renameLocationSet) public InlineRenameLocationSet(SymbolInlineRenameInfo renameInfo, RenameLocations renameLocationSet)
{ {
_renameInfo = renameInfo; _renameInfo = renameInfo;
_renameLocationSet = renameLocationSet; _renameLocationSet = renameLocationSet;
......
...@@ -31,7 +31,7 @@ private partial class SymbolInlineRenameInfo : IInlineRenameInfo ...@@ -31,7 +31,7 @@ private partial class SymbolInlineRenameInfo : IInlineRenameInfo
private readonly Document _document; private readonly Document _document;
private readonly IEnumerable<IRefactorNotifyService> _refactorNotifyServices; private readonly IEnumerable<IRefactorNotifyService> _refactorNotifyServices;
private Task<RenameLocationSet> _underlyingFindRenameLocationsTask; private Task<RenameLocations> _underlyingFindRenameLocationsTask;
/// <summary> /// <summary>
/// Whether or not we shortened the trigger span (say because we were renaming an attribute, /// Whether or not we shortened the trigger span (say because we were renaming an attribute,
...@@ -61,7 +61,7 @@ private partial class SymbolInlineRenameInfo : IInlineRenameInfo ...@@ -61,7 +61,7 @@ private partial class SymbolInlineRenameInfo : IInlineRenameInfo
_document = document; _document = document;
this.RenameSymbol = renameSymbol; this.RenameSymbol = renameSymbol;
this.HasOverloads = RenameLocationSet.GetOverloadedSymbols(this.RenameSymbol).Any(); this.HasOverloads = RenameLocations.GetOverloadedSymbols(this.RenameSymbol).Any();
this.ForceRenameOverloads = forceRenameOverloads; this.ForceRenameOverloads = forceRenameOverloads;
_isRenamingAttributePrefix = CanRenameAttributePrefix(document, triggerSpan, cancellationToken); _isRenamingAttributePrefix = CanRenameAttributePrefix(document, triggerSpan, cancellationToken);
...@@ -208,14 +208,14 @@ public string GetFinalSymbolName(string replacementText) ...@@ -208,14 +208,14 @@ public string GetFinalSymbolName(string replacementText)
public Task<IInlineRenameLocationSet> FindRenameLocationsAsync(OptionSet optionSet, CancellationToken cancellationToken) public Task<IInlineRenameLocationSet> FindRenameLocationsAsync(OptionSet optionSet, CancellationToken cancellationToken)
{ {
Task<RenameLocationSet> renameTask; Task<RenameLocations> renameTask;
lock (_gate) lock (_gate)
{ {
if (_underlyingFindRenameLocationsTask == null) if (_underlyingFindRenameLocationsTask == null)
{ {
// If this is the first call, then just start finding the initial set of rename // If this is the first call, then just start finding the initial set of rename
// locations. // locations.
_underlyingFindRenameLocationsTask = RenameLocationSet.FindAsync( _underlyingFindRenameLocationsTask = RenameLocations.FindAsync(
this.RenameSymbol, _document.Project.Solution, optionSet, cancellationToken); this.RenameSymbol, _document.Project.Solution, optionSet, cancellationToken);
renameTask = _underlyingFindRenameLocationsTask; renameTask = _underlyingFindRenameLocationsTask;
...@@ -234,7 +234,7 @@ public Task<IInlineRenameLocationSet> FindRenameLocationsAsync(OptionSet optionS ...@@ -234,7 +234,7 @@ public Task<IInlineRenameLocationSet> FindRenameLocationsAsync(OptionSet optionS
return GetLocationSet(renameTask, optionSet, cancellationToken); return GetLocationSet(renameTask, optionSet, cancellationToken);
} }
private async Task<IInlineRenameLocationSet> GetLocationSet(Task<RenameLocationSet> renameTask, OptionSet optionSet, CancellationToken cancellationToken) private async Task<IInlineRenameLocationSet> GetLocationSet(Task<RenameLocations> renameTask, OptionSet optionSet, CancellationToken cancellationToken)
{ {
var locationSet = await renameTask.ConfigureAwait(false); var locationSet = await renameTask.ConfigureAwait(false);
if (optionSet != null) if (optionSet != null)
......
...@@ -97,7 +97,7 @@ private IInlineRenameInfo GetRenameInfo(Document document, int position, Cancell ...@@ -97,7 +97,7 @@ private IInlineRenameInfo GetRenameInfo(Document document, int position, Cancell
} }
} }
var symbol = RenameLocationSet.ReferenceProcessing.GetRenamableSymbolAsync(document, triggerToken.SpanStart, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken); var symbol = RenameLocations.ReferenceProcessing.GetRenamableSymbolAsync(document, triggerToken.SpanStart, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken);
if (symbol == null) if (symbol == null)
{ {
return new FailureInlineRenameInfo(EditorFeaturesResources.YouCannotRenameThisElement); return new FailureInlineRenameInfo(EditorFeaturesResources.YouCannotRenameThisElement);
......
...@@ -68,7 +68,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename ...@@ -68,7 +68,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename
Dim document = workspace.CurrentSolution.GetDocument(cursorDocument.Id) Dim document = workspace.CurrentSolution.GetDocument(cursorDocument.Id)
Dim symbol = RenameLocationSet.ReferenceProcessing.GetRenamableSymbolAsync(document, cursorPosition, CancellationToken.None).Result Dim symbol = RenameLocations.ReferenceProcessing.GetRenamableSymbolAsync(document, cursorPosition, CancellationToken.None).Result
If symbol Is Nothing Then If symbol Is Nothing Then
AssertEx.Fail("The symbol touching the $$ could not be found.") AssertEx.Fail("The symbol touching the $$ could not be found.")
...@@ -82,7 +82,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename ...@@ -82,7 +82,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename
Next Next
End If End If
Dim locations = RenameLocationSet.FindAsync(symbol, workspace.CurrentSolution, optionSet, CancellationToken.None).Result Dim locations = RenameLocations.FindAsync(symbol, workspace.CurrentSolution, optionSet, CancellationToken.None).Result
Dim originalName = symbol.Name.Split("."c).Last() Dim originalName = symbol.Name.Split("."c).Last()
Dim result = ConflictResolver.ResolveConflictsAsync(locations, originalName, renameTo, optionSet, CancellationToken.None).Result Dim result = ConflictResolver.ResolveConflictsAsync(locations, originalName, renameTo, optionSet, CancellationToken.None).Result
......
...@@ -614,7 +614,7 @@ private SyntaxToken RenameToken(SyntaxToken oldToken, SyntaxToken newToken, stri ...@@ -614,7 +614,7 @@ private SyntaxToken RenameToken(SyntaxToken oldToken, SyntaxToken newToken, stri
private SyntaxToken RenameInStringLiteral(SyntaxToken oldToken, SyntaxToken newToken, Func<SyntaxTriviaList, string, string, SyntaxTriviaList, SyntaxToken> createNewStringLiteral) private SyntaxToken RenameInStringLiteral(SyntaxToken oldToken, SyntaxToken newToken, Func<SyntaxTriviaList, string, string, SyntaxTriviaList, SyntaxToken> createNewStringLiteral)
{ {
var originalString = newToken.ToString(); var originalString = newToken.ToString();
string replacedString = RenameLocationSet.ReferenceProcessing.ReplaceMatchingSubStrings(originalString, _originalText, _replacementText); string replacedString = RenameLocations.ReferenceProcessing.ReplaceMatchingSubStrings(originalString, _originalText, _replacementText);
if (replacedString != originalString) if (replacedString != originalString)
{ {
var oldSpan = oldToken.Span; var oldSpan = oldToken.Span;
...@@ -642,7 +642,7 @@ private SyntaxToken RenameInTrivia(SyntaxToken token, IEnumerable<SyntaxTrivia> ...@@ -642,7 +642,7 @@ private SyntaxToken RenameInTrivia(SyntaxToken token, IEnumerable<SyntaxTrivia>
private SyntaxTrivia RenameInCommentTrivia(SyntaxTrivia trivia) private SyntaxTrivia RenameInCommentTrivia(SyntaxTrivia trivia)
{ {
var originalString = trivia.ToString(); var originalString = trivia.ToString();
string replacedString = RenameLocationSet.ReferenceProcessing.ReplaceMatchingSubStrings(originalString, _originalText, _replacementText); string replacedString = RenameLocations.ReferenceProcessing.ReplaceMatchingSubStrings(originalString, _originalText, _replacementText);
if (replacedString != originalString) if (replacedString != originalString)
{ {
var oldSpan = trivia.Span; var oldSpan = trivia.Span;
...@@ -819,7 +819,7 @@ private SyntaxToken RenameWithinToken(SyntaxToken oldToken, SyntaxToken newToken ...@@ -819,7 +819,7 @@ private SyntaxToken RenameWithinToken(SyntaxToken oldToken, SyntaxToken newToken
var properties = new List<ISymbol>(); var properties = new List<ISymbol>();
foreach (var referencedSymbol in referencedSymbols) foreach (var referencedSymbol in referencedSymbols)
{ {
var property = await RenameLocationSet.ReferenceProcessing.GetPropertyFromAccessorOrAnOverride( var property = await RenameLocations.ReferenceProcessing.GetPropertyFromAccessorOrAnOverride(
referencedSymbol, baseSolution, cancellationToken).ConfigureAwait(false); referencedSymbol, baseSolution, cancellationToken).ConfigureAwait(false);
if (property != null) if (property != null)
{ {
......
...@@ -36,7 +36,15 @@ public static SyntaxGenerator GetGenerator(Workspace workspace, string language) ...@@ -36,7 +36,15 @@ public static SyntaxGenerator GetGenerator(Workspace workspace, string language)
/// </summary> /// </summary>
public static SyntaxGenerator GetGenerator(Document document) public static SyntaxGenerator GetGenerator(Document document)
{ {
return document.Project.LanguageServices.GetService<SyntaxGenerator>(); return GetGenerator(document.Project);
}
/// <summary>
/// Gets the <see cref="SyntaxGenerator"/> for the language corresponding to the project.
/// </summary>
public static SyntaxGenerator GetGenerator(Project project)
{
return project.LanguageServices.GetService<SyntaxGenerator>();
} }
#region Declarations #region Declarations
......
Microsoft.CodeAnalysis.Editing.SyntaxEditor.RemoveNode(Microsoft.CodeAnalysis.SyntaxNode node, Microsoft.CodeAnalysis.SyntaxRemoveOptions options) -> void Microsoft.CodeAnalysis.Editing.SyntaxEditor.RemoveNode(Microsoft.CodeAnalysis.SyntaxNode node, Microsoft.CodeAnalysis.SyntaxRemoveOptions options) -> void
Microsoft.CodeAnalysis.Project.IsSubmission.get -> bool Microsoft.CodeAnalysis.Project.IsSubmission.get -> bool
Microsoft.CodeAnalysis.Workspace.UpdateReferencesAfterAdd() -> void Microsoft.CodeAnalysis.Workspace.UpdateReferencesAfterAdd() -> void
static Microsoft.CodeAnalysis.Editing.SyntaxGenerator.GetGenerator(Microsoft.CodeAnalysis.Project project) -> Microsoft.CodeAnalysis.Editing.SyntaxGenerator
virtual Microsoft.CodeAnalysis.Editing.SyntaxGenerator.RemoveNode(Microsoft.CodeAnalysis.SyntaxNode root, Microsoft.CodeAnalysis.SyntaxNode node, Microsoft.CodeAnalysis.SyntaxRemoveOptions options) -> Microsoft.CodeAnalysis.SyntaxNode virtual Microsoft.CodeAnalysis.Editing.SyntaxGenerator.RemoveNode(Microsoft.CodeAnalysis.SyntaxNode root, Microsoft.CodeAnalysis.SyntaxNode node, Microsoft.CodeAnalysis.SyntaxRemoveOptions options) -> Microsoft.CodeAnalysis.SyntaxNode
\ No newline at end of file
...@@ -28,7 +28,7 @@ internal static partial class ConflictResolver ...@@ -28,7 +28,7 @@ internal static partial class ConflictResolver
private class Session private class Session
{ {
// Set of All Locations that will be renamed (does not include non-reference locations that need to be checked for conflicts) // Set of All Locations that will be renamed (does not include non-reference locations that need to be checked for conflicts)
private readonly RenameLocationSet _renameLocationSet; private readonly RenameLocations _renameLocationSet;
// Rename Symbol's Source Location // Rename Symbol's Source Location
private readonly Location _renameSymbolDeclarationLocation; private readonly Location _renameSymbolDeclarationLocation;
...@@ -52,7 +52,7 @@ private class Session ...@@ -52,7 +52,7 @@ private class Session
private bool _documentOfRenameSymbolHasBeenRenamed; private bool _documentOfRenameSymbolHasBeenRenamed;
public Session( public Session(
RenameLocationSet renameLocationSet, RenameLocations renameLocationSet,
Location renameSymbolDeclarationLocation, Location renameSymbolDeclarationLocation,
string originalText, string originalText,
string replacementText, string replacementText,
......
...@@ -45,7 +45,7 @@ internal static partial class ConflictResolver ...@@ -45,7 +45,7 @@ internal static partial class ConflictResolver
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A conflict resolution containing the new solution.</returns> /// <returns>A conflict resolution containing the new solution.</returns>
public static Task<ConflictResolution> ResolveConflictsAsync( public static Task<ConflictResolution> ResolveConflictsAsync(
RenameLocationSet renameLocationSet, RenameLocations renameLocationSet,
string originalText, string originalText,
string replacementText, string replacementText,
OptionSet optionSet, OptionSet optionSet,
......
...@@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.Rename ...@@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.Rename
/// A helper class that contains some of the methods and filters that must be used when /// A helper class that contains some of the methods and filters that must be used when
/// processing the raw results from the FindReferences API. /// processing the raw results from the FindReferences API.
/// </summary> /// </summary>
internal sealed partial class RenameLocationSet internal sealed partial class RenameLocations
{ {
internal static class ReferenceProcessing internal static class ReferenceProcessing
{ {
...@@ -354,7 +354,8 @@ internal static async Task<IEnumerable<RenameLocation>> GetRenamableReferenceLoc ...@@ -354,7 +354,8 @@ internal static async Task<IEnumerable<RenameLocation>> GetRenamableReferenceLoc
{ {
if (location.Alias.Name == referencedSymbol.Name) if (location.Alias.Name == referencedSymbol.Name)
{ {
results.Add(new RenameLocation(location.Location, location.Document.Id, isCandidateLocation: location.IsCandidateLocation, isRenamableAliasUsage: true)); results.Add(new RenameLocation(location.Location, location.Document.Id,
isCandidateLocation: location.IsCandidateLocation, isRenamableAliasUsage: true, isWrittenTo: location.IsWrittenTo));
// We also need to add the location of the alias itself // We also need to add the location of the alias itself
var aliasLocation = location.Alias.Locations.Single(); var aliasLocation = location.Alias.Locations.Single();
...@@ -367,6 +368,7 @@ internal static async Task<IEnumerable<RenameLocation>> GetRenamableReferenceLoc ...@@ -367,6 +368,7 @@ internal static async Task<IEnumerable<RenameLocation>> GetRenamableReferenceLoc
results.Add(new RenameLocation( results.Add(new RenameLocation(
location.Location, location.Location,
location.Document.Id, location.Document.Id,
isWrittenTo: location.IsWrittenTo,
isCandidateLocation: location.IsCandidateLocation, isCandidateLocation: location.IsCandidateLocation,
isMethodGroupReference: location.IsCandidateLocation && location.CandidateReason == CandidateReason.MemberGroup, isMethodGroupReference: location.IsCandidateLocation && location.CandidateReason == CandidateReason.MemberGroup,
isRenamableAccessor: await IsPropertyAccessorOrAnOverride(referencedSymbol, solution, cancellationToken).ConfigureAwait(false))); isRenamableAccessor: await IsPropertyAccessorOrAnOverride(referencedSymbol, solution, cancellationToken).ConfigureAwait(false)));
......
...@@ -14,6 +14,7 @@ internal struct RenameLocation : IEquatable<RenameLocation> ...@@ -14,6 +14,7 @@ internal struct RenameLocation : IEquatable<RenameLocation>
public readonly bool IsRenamableAliasUsage; public readonly bool IsRenamableAliasUsage;
public readonly bool IsRenamableAccessor; public readonly bool IsRenamableAccessor;
public readonly TextSpan ContainingLocationForStringOrComment; public readonly TextSpan ContainingLocationForStringOrComment;
public readonly bool IsWrittenTo;
public bool IsRenameInStringOrComment { get { return ContainingLocationForStringOrComment != default(TextSpan); } } public bool IsRenameInStringOrComment { get { return ContainingLocationForStringOrComment != default(TextSpan); } }
...@@ -26,26 +27,25 @@ internal struct RenameLocation : IEquatable<RenameLocation> ...@@ -26,26 +27,25 @@ internal struct RenameLocation : IEquatable<RenameLocation>
bool isMethodGroupReference = false, bool isMethodGroupReference = false,
bool isRenamableAliasUsage = false, bool isRenamableAliasUsage = false,
bool isRenamableAccessor = false, bool isRenamableAccessor = false,
bool isWrittenTo = false,
TextSpan containingLocationForStringOrComment = default(TextSpan)) TextSpan containingLocationForStringOrComment = default(TextSpan))
{ {
this.Location = location; Location = location;
this.DocumentId = documentId; DocumentId = documentId;
this.IsCandidateLocation = isCandidateLocation; IsCandidateLocation = isCandidateLocation;
this.IsMethodGroupReference = isMethodGroupReference; IsMethodGroupReference = isMethodGroupReference;
this.IsRenamableAliasUsage = isRenamableAliasUsage; IsRenamableAliasUsage = isRenamableAliasUsage;
this.IsRenamableAccessor = isRenamableAccessor; IsRenamableAccessor = isRenamableAccessor;
this.ContainingLocationForStringOrComment = containingLocationForStringOrComment; IsWrittenTo = isWrittenTo;
ContainingLocationForStringOrComment = containingLocationForStringOrComment;
} }
public RenameLocation(ReferenceLocation referenceLocation, DocumentId documentId) public RenameLocation(ReferenceLocation referenceLocation, DocumentId documentId)
: this(referenceLocation.Location, documentId,
isCandidateLocation: referenceLocation.IsCandidateLocation && referenceLocation.CandidateReason != CandidateReason.LateBound,
isMethodGroupReference: referenceLocation.IsCandidateLocation && referenceLocation.CandidateReason == CandidateReason.MemberGroup,
isWrittenTo: referenceLocation.IsWrittenTo)
{ {
this.Location = referenceLocation.Location;
this.DocumentId = documentId;
this.IsCandidateLocation = referenceLocation.IsCandidateLocation && referenceLocation.CandidateReason != CandidateReason.LateBound;
this.IsMethodGroupReference = referenceLocation.IsCandidateLocation && referenceLocation.CandidateReason == CandidateReason.MemberGroup;
this.IsRenamableAliasUsage = false;
this.IsRenamableAccessor = false;
this.ContainingLocationForStringOrComment = default(TextSpan);
} }
public bool Equals(RenameLocation other) public bool Equals(RenameLocation other)
......
...@@ -14,10 +14,10 @@ ...@@ -14,10 +14,10 @@
namespace Microsoft.CodeAnalysis.Rename namespace Microsoft.CodeAnalysis.Rename
{ {
/// <summary> /// <summary>
/// Holds the ILocations of a symbol that should be renamed, along with the symbol and Solution /// Holds the Locations of a symbol that should be renamed, along with the symbol and Solution
/// for the set. /// for the set.
/// </summary> /// </summary>
internal sealed partial class RenameLocationSet internal sealed partial class RenameLocations
{ {
private class SearchResult private class SearchResult
{ {
...@@ -37,26 +37,27 @@ public SearchResult(ISet<RenameLocation> locations, IEnumerable<ReferenceLocatio ...@@ -37,26 +37,27 @@ public SearchResult(ISet<RenameLocation> locations, IEnumerable<ReferenceLocatio
private readonly ISymbol _symbol; private readonly ISymbol _symbol;
private readonly Solution _solution; private readonly Solution _solution;
private readonly SearchResult _mergedResult; private readonly SearchResult _mergedResult;
internal OptionSet Options { get; }
// possibly null fields // possibly null fields
private readonly OptionSet _optionSet;
private readonly SearchResult _originalSymbolResult; private readonly SearchResult _originalSymbolResult;
private readonly List<SearchResult> _overloadsResult; private readonly List<SearchResult> _overloadsResult;
private readonly IEnumerable<RenameLocation> _stringsResult; private readonly IEnumerable<RenameLocation> _stringsResult;
private readonly IEnumerable<RenameLocation> _commentsResult; private readonly IEnumerable<RenameLocation> _commentsResult;
public RenameLocationSet(ISet<RenameLocation> locations, ISymbol symbol, Solution solution, IEnumerable<ISymbol> referencedSymbols, IEnumerable<ReferenceLocation> implicitLocations) internal RenameLocations(ISet<RenameLocation> locations, ISymbol symbol, Solution solution, IEnumerable<ISymbol> referencedSymbols, IEnumerable<ReferenceLocation> implicitLocations, OptionSet options)
{ {
_symbol = symbol; _symbol = symbol;
_solution = solution; _solution = solution;
_mergedResult = new SearchResult(locations, implicitLocations, referencedSymbols); _mergedResult = new SearchResult(locations, implicitLocations, referencedSymbols);
Options = options;
} }
private RenameLocationSet(ISymbol symbol, Solution solution, OptionSet optionSet, SearchResult originalSymbolResult, List<SearchResult> overloadsResult, IEnumerable<RenameLocation> stringsResult, IEnumerable<RenameLocation> commentsResult) private RenameLocations(ISymbol symbol, Solution solution, OptionSet options, SearchResult originalSymbolResult, List<SearchResult> overloadsResult, IEnumerable<RenameLocation> stringsResult, IEnumerable<RenameLocation> commentsResult)
{ {
_symbol = symbol; _symbol = symbol;
_solution = solution; _solution = solution;
_optionSet = optionSet; Options = options;
_originalSymbolResult = originalSymbolResult; _originalSymbolResult = originalSymbolResult;
_overloadsResult = overloadsResult; _overloadsResult = overloadsResult;
_stringsResult = stringsResult; _stringsResult = stringsResult;
...@@ -66,18 +67,18 @@ private RenameLocationSet(ISymbol symbol, Solution solution, OptionSet optionSet ...@@ -66,18 +67,18 @@ private RenameLocationSet(ISymbol symbol, Solution solution, OptionSet optionSet
var mergedReferencedSymbols = new List<ISymbol>(); var mergedReferencedSymbols = new List<ISymbol>();
var mergedImplicitLocations = new List<ReferenceLocation>(); var mergedImplicitLocations = new List<ReferenceLocation>();
if (optionSet.GetOption(RenameOptions.RenameInStrings)) if (options.GetOption(RenameOptions.RenameInStrings))
{ {
mergedLocations.AddRange(stringsResult); mergedLocations.AddRange(stringsResult);
} }
if (optionSet.GetOption(RenameOptions.RenameInComments)) if (options.GetOption(RenameOptions.RenameInComments))
{ {
mergedLocations.AddRange(commentsResult); mergedLocations.AddRange(commentsResult);
} }
var renameMethodGroupReferences = optionSet.GetOption(RenameOptions.RenameOverloads) || !GetOverloadedSymbols(symbol).Any(); var renameMethodGroupReferences = options.GetOption(RenameOptions.RenameOverloads) || !GetOverloadedSymbols(symbol).Any();
var overloadsToMerge = (optionSet.GetOption(RenameOptions.RenameOverloads) ? overloadsResult : null) ?? SpecializedCollections.EmptyEnumerable<SearchResult>(); var overloadsToMerge = (options.GetOption(RenameOptions.RenameOverloads) ? overloadsResult : null) ?? SpecializedCollections.EmptyEnumerable<SearchResult>();
foreach (var result in overloadsToMerge.Concat(originalSymbolResult)) foreach (var result in overloadsToMerge.Concat(originalSymbolResult))
{ {
mergedLocations.AddRange(renameMethodGroupReferences mergedLocations.AddRange(renameMethodGroupReferences
...@@ -100,22 +101,22 @@ private RenameLocationSet(ISymbol symbol, Solution solution, OptionSet optionSet ...@@ -100,22 +101,22 @@ private RenameLocationSet(ISymbol symbol, Solution solution, OptionSet optionSet
/// <summary> /// <summary>
/// Find the locations that need to be renamed. /// Find the locations that need to be renamed.
/// </summary> /// </summary>
public static async Task<RenameLocationSet> FindAsync(ISymbol symbol, Solution solution, OptionSet optionSet, CancellationToken cancellationToken) internal static async Task<RenameLocations> FindAsync(ISymbol symbol, Solution solution, OptionSet optionSet, CancellationToken cancellationToken)
{ {
Contract.ThrowIfNull(symbol); Contract.ThrowIfNull(symbol);
using (Logger.LogBlock(FunctionId.Rename_AllRenameLocations, cancellationToken)) using (Logger.LogBlock(FunctionId.Rename_AllRenameLocations, cancellationToken))
{ {
symbol = await ReferenceProcessing.FindDefinitionSymbolAsync(symbol, solution, cancellationToken).ConfigureAwait(false); symbol = await ReferenceProcessing.FindDefinitionSymbolAsync(symbol, solution, cancellationToken).ConfigureAwait(false);
var originalSymbolResult = await AddLocationsReferenceSymbolsAsync(symbol, solution, cancellationToken).ConfigureAwait(false); var originalSymbolResult = await AddLocationsReferenceSymbolsAsync(symbol, solution, cancellationToken).ConfigureAwait(false);
var intermediateResult = new RenameLocationSet(symbol, solution, optionSet, originalSymbolResult, overloadsResult: null, stringsResult: null, commentsResult: null); var intermediateResult = new RenameLocations(symbol, solution, optionSet, originalSymbolResult, overloadsResult: null, stringsResult: null, commentsResult: null);
return await intermediateResult.FindWithUpdatedOptionsAsync(optionSet, cancellationToken).ConfigureAwait(false); return await intermediateResult.FindWithUpdatedOptionsAsync(optionSet, cancellationToken).ConfigureAwait(false);
} }
} }
public async Task<RenameLocationSet> FindWithUpdatedOptionsAsync(OptionSet optionSet, CancellationToken cancellationToken) internal async Task<RenameLocations> FindWithUpdatedOptionsAsync(OptionSet optionSet, CancellationToken cancellationToken)
{ {
Contract.ThrowIfNull(_optionSet, "FindWithUpdatedOptionsAsync can only be called on a result of FindAsync"); Contract.ThrowIfNull(Options, "FindWithUpdatedOptionsAsync can only be called on a result of FindAsync");
using (Logger.LogBlock(FunctionId.Rename_AllRenameLocations, cancellationToken)) using (Logger.LogBlock(FunctionId.Rename_AllRenameLocations, cancellationToken))
{ {
var overloadsResult = _overloadsResult ?? (optionSet.GetOption(RenameOptions.RenameOverloads) var overloadsResult = _overloadsResult ?? (optionSet.GetOption(RenameOptions.RenameOverloads)
...@@ -130,7 +131,7 @@ public async Task<RenameLocationSet> FindWithUpdatedOptionsAsync(OptionSet optio ...@@ -130,7 +131,7 @@ public async Task<RenameLocationSet> FindWithUpdatedOptionsAsync(OptionSet optio
optionSet.GetOption(RenameOptions.RenameInComments) && _commentsResult == null, optionSet.GetOption(RenameOptions.RenameInComments) && _commentsResult == null,
cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
return new RenameLocationSet(_symbol, _solution, optionSet, _originalSymbolResult, return new RenameLocations(_symbol, _solution, optionSet, _originalSymbolResult,
_overloadsResult ?? overloadsResult, _overloadsResult ?? overloadsResult,
_stringsResult ?? stringsAndComments.Item1, _stringsResult ?? stringsAndComments.Item1,
_commentsResult ?? stringsAndComments.Item2); _commentsResult ?? stringsAndComments.Item2);
...@@ -191,4 +192,4 @@ internal static IEnumerable<ISymbol> GetOverloadedSymbols(ISymbol symbol) ...@@ -191,4 +192,4 @@ internal static IEnumerable<ISymbol> GetOverloadedSymbols(ISymbol symbol)
return new SearchResult(locations, implicitLocations, referencedSymbols); return new SearchResult(locations, implicitLocations, referencedSymbols);
} }
} }
} }
\ No newline at end of file
...@@ -18,14 +18,7 @@ public static Task<Solution> RenameSymbolAsync(Solution solution, ISymbol symbol ...@@ -18,14 +18,7 @@ public static Task<Solution> RenameSymbolAsync(Solution solution, ISymbol symbol
return RenameSymbolAsync(solution, symbol, newName, optionSet, filter: null, cancellationToken: cancellationToken); return RenameSymbolAsync(solution, symbol, newName, optionSet, filter: null, cancellationToken: cancellationToken);
} }
internal static async Task<Solution> RenameSymbolAsync( internal static Task<RenameLocations> GetRenameLocationsAsync(Solution solution, ISymbol symbol, OptionSet options, CancellationToken cancellationToken)
Solution solution,
ISymbol symbol,
string newName,
OptionSet optionSet,
Func<Location, bool> filter,
Func<IEnumerable<ISymbol>, bool?> hasConflict = null,
CancellationToken cancellationToken = default(CancellationToken))
{ {
if (solution == null) if (solution == null)
{ {
...@@ -37,27 +30,66 @@ public static Task<Solution> RenameSymbolAsync(Solution solution, ISymbol symbol ...@@ -37,27 +30,66 @@ public static Task<Solution> RenameSymbolAsync(Solution solution, ISymbol symbol
throw new ArgumentNullException(nameof(symbol)); throw new ArgumentNullException(nameof(symbol));
} }
cancellationToken.ThrowIfCancellationRequested();
options = options ?? solution.Workspace.Options;
return RenameLocations.FindAsync(symbol, solution, options, cancellationToken);
}
internal static async Task<Solution> RenameAsync(
RenameLocations locations,
string newName,
Func<Location, bool> filter = null,
Func<IEnumerable<ISymbol>, bool?> hasConflict = null,
CancellationToken cancellationToken = default(CancellationToken))
{
if (string.IsNullOrEmpty(newName)) if (string.IsNullOrEmpty(newName))
{ {
throw new ArgumentException("newName"); throw new ArgumentException(nameof(newName));
} }
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
optionSet = optionSet ?? solution.Workspace.Options; var symbol = locations.Symbol;
var renameLocationSet = await RenameLocationSet.FindAsync(symbol, solution, optionSet, cancellationToken).ConfigureAwait(false);
if (filter != null) if (filter != null)
{ {
renameLocationSet = new RenameLocationSet( locations = new RenameLocations(
renameLocationSet.Locations.Where(loc => filter(loc.Location)).ToSet(), locations.Locations.Where(loc => filter(loc.Location)).ToSet(),
renameLocationSet.Symbol, renameLocationSet.Solution, symbol, locations.Solution,
renameLocationSet.ReferencedSymbols, renameLocationSet.ImplicitLocations); locations.ReferencedSymbols, locations.ImplicitLocations,
locations.Options);
} }
var conflictResolution = await ConflictResolver.ResolveConflictsAsync( var conflictResolution = await ConflictResolver.ResolveConflictsAsync(
renameLocationSet, symbol.Name, newName, optionSet, hasConflict, cancellationToken).ConfigureAwait(false); locations, symbol.Name, newName, locations.Options, hasConflict, cancellationToken).ConfigureAwait(false);
return conflictResolution.NewSolution; return conflictResolution.NewSolution;
} }
internal static async Task<Solution> RenameSymbolAsync(
Solution solution,
ISymbol symbol,
string newName,
OptionSet options,
Func<Location, bool> filter,
Func<IEnumerable<ISymbol>, bool?> hasConflict = null,
CancellationToken cancellationToken = default(CancellationToken))
{
if (solution == null)
{
throw new ArgumentNullException(nameof(solution));
}
if (symbol == null)
{
throw new ArgumentNullException(nameof(symbol));
}
cancellationToken.ThrowIfCancellationRequested();
options = options ?? solution.Workspace.Options;
var renameLocations = await GetRenameLocationsAsync(solution, symbol, options, cancellationToken).ConfigureAwait(false);
return await RenameAsync(renameLocations, newName, filter, hasConflict, cancellationToken).ConfigureAwait(false);
}
} }
} }
\ No newline at end of file
...@@ -649,7 +649,7 @@ ...@@ -649,7 +649,7 @@
<Compile Include="Rename\RenameEntityKind.cs" /> <Compile Include="Rename\RenameEntityKind.cs" />
<Compile Include="Rename\RenameLocation.cs" /> <Compile Include="Rename\RenameLocation.cs" />
<Compile Include="Rename\RenameLocation.ReferenceProcessing.cs" /> <Compile Include="Rename\RenameLocation.ReferenceProcessing.cs" />
<Compile Include="Rename\RenameLocationSet.cs" /> <Compile Include="Rename\RenameLocations.cs" />
<Compile Include="Rename\RenameOptions.cs" /> <Compile Include="Rename\RenameOptions.cs" />
<Compile Include="Rename\Renamer.cs" /> <Compile Include="Rename\Renamer.cs" />
<Compile Include="Rename\RenameRewriterParameters.cs" /> <Compile Include="Rename\RenameRewriterParameters.cs" />
......
...@@ -545,7 +545,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Rename ...@@ -545,7 +545,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Rename
Private Function RenameInStringLiteral(oldToken As SyntaxToken, newToken As SyntaxToken, createNewStringLiteral As Func(Of SyntaxTriviaList, String, String, SyntaxTriviaList, SyntaxToken)) As SyntaxToken Private Function RenameInStringLiteral(oldToken As SyntaxToken, newToken As SyntaxToken, createNewStringLiteral As Func(Of SyntaxTriviaList, String, String, SyntaxTriviaList, SyntaxToken)) As SyntaxToken
Dim originalString = newToken.ToString() Dim originalString = newToken.ToString()
Dim replacedString As String = RenameLocationSet.ReferenceProcessing.ReplaceMatchingSubStrings(originalString, _originalText, _replacementText) Dim replacedString As String = RenameLocations.ReferenceProcessing.ReplaceMatchingSubStrings(originalString, _originalText, _replacementText)
If replacedString <> originalString Then If replacedString <> originalString Then
Dim oldSpan = oldToken.Span Dim oldSpan = oldToken.Span
newToken = createNewStringLiteral(newToken.LeadingTrivia, replacedString, replacedString, newToken.TrailingTrivia) newToken = createNewStringLiteral(newToken.LeadingTrivia, replacedString, replacedString, newToken.TrailingTrivia)
...@@ -558,7 +558,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Rename ...@@ -558,7 +558,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Rename
Private Function RenameInCommentTrivia(trivia As SyntaxTrivia) As SyntaxTrivia Private Function RenameInCommentTrivia(trivia As SyntaxTrivia) As SyntaxTrivia
Dim originalString = trivia.ToString() Dim originalString = trivia.ToString()
Dim replacedString As String = RenameLocationSet.ReferenceProcessing.ReplaceMatchingSubStrings(originalString, _originalText, _replacementText) Dim replacedString As String = RenameLocations.ReferenceProcessing.ReplaceMatchingSubStrings(originalString, _originalText, _replacementText)
If replacedString <> originalString Then If replacedString <> originalString Then
Dim oldSpan = trivia.Span Dim oldSpan = trivia.Span
Dim newTrivia = SyntaxFactory.CommentTrivia(replacedString) Dim newTrivia = SyntaxFactory.CommentTrivia(replacedString)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册