diff --git a/src/EditorFeatures/Core/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs b/src/EditorFeatures/Core/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs index 00471015964d2..d26ea6df055de 100644 --- a/src/EditorFeatures/Core/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs +++ b/src/EditorFeatures/Core/InlineRename/UI/Adornment/RenameFlyoutViewModel.cs @@ -229,7 +229,7 @@ public Visibility Visibility public bool Submit() { - if (StatusSeverity == Severity.Error) + if (!_isReplacementTextValid) { return false; } diff --git a/src/EditorFeatures/Test2/Rename/InlineRenameTests.vb b/src/EditorFeatures/Test2/Rename/InlineRenameTests.vb index 980dd5b1c688a..30ce82a3a952f 100644 --- a/src/EditorFeatures/Test2/Rename/InlineRenameTests.vb +++ b/src/EditorFeatures/Test2/Rename/InlineRenameTests.vb @@ -7,12 +7,16 @@ Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeRefactorings Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.Editor.Host +Imports Microsoft.CodeAnalysis.Editor.Implementation.InlineRename +Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.RenameTracking Imports Microsoft.CodeAnalysis.InlineRename Imports Microsoft.CodeAnalysis.IntroduceVariable Imports Microsoft.CodeAnalysis.Notification Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Rename +Imports Microsoft.CodeAnalysis.Shared.TestHooks +Imports Microsoft.CodeAnalysis.Text Imports Microsoft.VisualStudio.Text Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename @@ -110,7 +114,7 @@ class C - Public Async Function RenameToConflictingMemberReportsConflict(host As RenameTestHost) As Task + Public Async Function RenameToConflictingMemberReportsConflictAndAllowsSubmit(host As RenameTestHost) As Task Using workspace = CreateWorkspaceWithWaiter( @@ -126,23 +130,36 @@ class C Dim session = StartSession(workspace) - Dim replacementInfo As IInlineRenameReplacementInfo = Nothing - AddHandler session.ReplacementsComputed, Sub(sender, info) replacementInfo = info - Dim cursorDocument = workspace.Documents.Single(Function(d) d.CursorPosition.HasValue) Dim caretPosition = cursorDocument.CursorPosition.Value Dim textBuffer = cursorDocument.GetTextBuffer() - ' Rename Foo to Bar - textBuffer.Delete(New Span(caretPosition, 3)) - textBuffer.Insert(caretPosition, "Bar") + Using viewModel = New RenameFlyoutViewModel( + session, + New TextSpan(caretPosition, 3), + registerOleComponent:=False, + workspace.GetService(Of IGlobalOptionService)(), + workspace.GetService(Of IThreadingContext)(), + workspace.GetService(Of IAsynchronousOperationListenerProvider)(), + smartRenameSessionFactory:=Nothing) - Await WaitForRename(workspace) + textBuffer.Delete(New Span(caretPosition, 3)) + textBuffer.Insert(caretPosition, "1") + + Await WaitForRename(workspace) + + Assert.False(viewModel.Submit()) + + textBuffer.Delete(New Span(caretPosition, 1)) + textBuffer.Insert(caretPosition, "Bar") + + Await WaitForRename(workspace) + + Assert.Equal(RenameFlyoutViewModel.Severity.Error, viewModel.StatusSeverity) + Assert.True(viewModel.Submit()) - ' Reports an unresolved conflict - Assert.NotNull(replacementInfo) - Dim replacementKinds = replacementInfo.GetAllReplacementKinds().ToList() - Assert.Contains(InlineRenameReplacementKind.UnresolvedConflict, replacementKinds) + Await WaitForRename(workspace) + End Using End Using End Function diff --git a/src/EditorFeatures/Test2/Rename/RenameEngineTests.CSharpConflicts.vb b/src/EditorFeatures/Test2/Rename/RenameEngineTests.CSharpConflicts.vb index 6027b0c79dca0..e0ba8f4f0b0aa 100644 --- a/src/EditorFeatures/Test2/Rename/RenameEngineTests.CSharpConflicts.vb +++ b/src/EditorFeatures/Test2/Rename/RenameEngineTests.CSharpConflicts.vb @@ -15,6 +15,86 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename _outputHelper = outputHelper End Sub + + + Public Sub RenameMethodReferenceWithUnboundArgument(host As RenameTestHost) + Using result = RenameEngineResult.Create(_outputHelper, + + + +class Program +{ + static void Main(string[] args) + { + [|Test|](x); + [|Test|](1); + } + + static void [|$$Test|](int number) + { + } +} + + + , host:=host, renameTo:="Test2") + End Using + End Sub + + + + Public Sub RenameMethodDoesNotRenameAmbiguousReferenceWithUnboundArgument(host As RenameTestHost) + Using result = RenameEngineResult.Create(_outputHelper, + + + +class Program +{ + static void Main(string[] args) + { + Test(x); + [|Test|](1); + } + + static void [|$$Test|](int number) + { + } + + static void Test(string value) + { + } +} + + + , host:=host, renameTo:="Test2") + + Dim documentText = result.ConflictResolution.NewSolution.Projects.Single().Documents.Single().GetTextAsync().Result.ToString() + Assert.Contains("Test(x);", documentText) + Assert.Contains("Test2(1);", documentText) + End Using + End Sub + + + + Public Sub RenameTypeReferenceWithUnboundConstructorArgument(host As RenameTestHost) + Using result = RenameEngineResult.Create(_outputHelper, + + + +record [|$$MyRecord|](int Number); + +class Program +{ + static void Main(string[] args) + { + var r = new [|MyRecord|](x); + } +} + + + , host:=host, renameTo:="MyRecord2") + End Using + End Sub + Public Sub BreakingRenameWithRollBacksInsideLambdas_2(host As RenameTestHost) @@ -3647,4 +3727,3 @@ class C End Sub End Class End Namespace - diff --git a/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs b/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs index 47b16cd46cc93..b4484913119a5 100644 --- a/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs +++ b/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs @@ -99,7 +99,7 @@ internal static ImmutableArray GetSymbolsTouchingPosition( if (symbols.Length == 0) { var info = semanticModel.GetSymbolInfo(bindableToken, cancellationToken); - if (info.CandidateReason == CandidateReason.MemberGroup) + if (info.CandidateReason is CandidateReason.MemberGroup or CandidateReason.OverloadResolutionFailure) { return info.CandidateSymbols; } diff --git a/src/Workspaces/Core/Portable/Rename/SymbolicRenameLocations.ReferenceProcessing.cs b/src/Workspaces/Core/Portable/Rename/SymbolicRenameLocations.ReferenceProcessing.cs index a7e106901cee1..c125add992a3d 100644 --- a/src/Workspaces/Core/Portable/Rename/SymbolicRenameLocations.ReferenceProcessing.cs +++ b/src/Workspaces/Core/Portable/Rename/SymbolicRenameLocations.ReferenceProcessing.cs @@ -269,6 +269,12 @@ internal static async Task> GetRenamableReferenceLoc if (location.IsImplicit) return []; + var candidateReason = location.CandidateReason; + if (candidateReason is CandidateReason.OverloadResolutionFailure) + { + candidateReason = await GetCandidateReasonForOverloadResolutionFailureAsync(location, cancellationToken).ConfigureAwait(false); + } + var results = new List(); // If we were originally naming an alias, then we'll only use the location if was @@ -301,7 +307,7 @@ internal static async Task> GetRenamableReferenceLoc if (location.Alias.Name == referencedSymbolName) { results.Add(new RenameLocation(location.Location, location.Document.Id, - candidateReason: location.CandidateReason, isRenamableAliasUsage: true, isWrittenTo: location.IsWrittenTo)); + candidateReason: candidateReason, isRenamableAliasUsage: true, isWrittenTo: location.IsWrittenTo)); // We also need to add the location of the alias itself var aliasLocation = location.Alias.Locations.Single(); @@ -324,12 +330,29 @@ internal static async Task> GetRenamableReferenceLoc location.Location, location.Document.Id, isWrittenTo: location.IsWrittenTo, - candidateReason: location.CandidateReason, + candidateReason: candidateReason, isRenamableAccessor: await IsPropertyAccessorOrAnOverrideAsync(referencedSymbol, solution, cancellationToken).ConfigureAwait(false))); } } return results; + + static async Task GetCandidateReasonForOverloadResolutionFailureAsync( + ReferenceLocation location, CancellationToken cancellationToken) + { + var syntaxRoot = await location.Document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var token = syntaxRoot.FindToken(location.Location.SourceSpan.Start, findInsideTrivia: true); + var syntaxFacts = location.Document.GetRequiredLanguageService(); + var bindableParent = syntaxFacts.TryGetBindableParent(token) ?? token.Parent; + if (bindableParent is null) + return location.CandidateReason; + + var semanticModel = await location.Document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var symbolInfo = semanticModel.GetSymbolInfo(bindableParent, cancellationToken); + return symbolInfo.CandidateSymbols.Length == 1 + ? CandidateReason.None + : location.CandidateReason; + } } internal static async Task<(ImmutableArray strings, ImmutableArray comments)> GetRenamableLocationsInStringsAndCommentsAsync(