Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ public Visibility Visibility

public bool Submit()
{
if (StatusSeverity == Severity.Error)
if (!_isReplacementTextValid)
{
return false;
}
Expand Down
41 changes: 29 additions & 12 deletions src/EditorFeatures/Test2/Rename/InlineRenameTests.vb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -110,7 +114,7 @@ class C

<WpfTheory>
<CombinatorialData, Trait(Traits.Feature, Traits.Features.Rename)>
Public Async Function RenameToConflictingMemberReportsConflict(host As RenameTestHost) As Task
Public Async Function RenameToConflictingMemberReportsConflictAndAllowsSubmit(host As RenameTestHost) As Task
Using workspace = CreateWorkspaceWithWaiter(
<Workspace>
<Project Language="C#" CommonReferences="true">
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,86 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename
_outputHelper = outputHelper
End Sub

<Theory>
<CombinatorialData>
Public Sub RenameMethodReferenceWithUnboundArgument(host As RenameTestHost)
Using result = RenameEngineResult.Create(_outputHelper,
Comment thread
akhera99 marked this conversation as resolved.
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class Program
{
static void Main(string[] args)
{
[|Test|](x);
[|Test|](1);
}

static void [|$$Test|](int number)
{
}
}
</Document>
</Project>
</Workspace>, host:=host, renameTo:="Test2")
End Using
End Sub

<Theory>
<CombinatorialData>
Public Sub RenameMethodDoesNotRenameAmbiguousReferenceWithUnboundArgument(host As RenameTestHost)
Using result = RenameEngineResult.Create(_outputHelper,
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class Program
{
static void Main(string[] args)
{
Test(x);
[|Test|](1);
}

static void [|$$Test|](int number)
{
}

static void Test(string value)
{
}
}
</Document>
</Project>
</Workspace>, 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

<Theory>
<CombinatorialData>
Public Sub RenameTypeReferenceWithUnboundConstructorArgument(host As RenameTestHost)
Using result = RenameEngineResult.Create(_outputHelper,
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
record [|$$MyRecord|](int Number);

class Program
{
static void Main(string[] args)
{
var r = new [|MyRecord|](x);
}
}
</Document>
</Project>
</Workspace>, host:=host, renameTo:="MyRecord2")
End Using
End Sub

<WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/773543")>
<CombinatorialData>
Public Sub BreakingRenameWithRollBacksInsideLambdas_2(host As RenameTestHost)
Expand Down Expand Up @@ -3647,4 +3727,3 @@ class C
End Sub
End Class
End Namespace

2 changes: 1 addition & 1 deletion src/Workspaces/Core/Portable/Rename/RenameUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ internal static ImmutableArray<ISymbol> 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,12 @@ internal static async Task<IEnumerable<RenameLocation>> 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<RenameLocation>();

// If we were originally naming an alias, then we'll only use the location if was
Expand Down Expand Up @@ -301,7 +307,7 @@ internal static async Task<IEnumerable<RenameLocation>> 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();
Expand All @@ -324,12 +330,29 @@ internal static async Task<IEnumerable<RenameLocation>> 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<CandidateReason> 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<ISyntaxFactsService>();
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<RenameLocation> strings, ImmutableArray<RenameLocation> comments)> GetRenamableLocationsInStringsAndCommentsAsync(
Expand Down