From 65d4fca1820a71aaba566869158df298b4652d58 Mon Sep 17 00:00:00 2001 From: David Poeschl Date: Tue, 18 Oct 2016 11:20:27 -0700 Subject: [PATCH] Don't allow rename on the well-known ValueTuple types Fixes #14159 The well-known ValueTuple types are very special in that you can ask for the SymbolInfo on the ValueTuple token and not get back the ValueTuple type. This means that the token we invoke rename on has no locations, which Rename Tracking mistakenly interpreted as being okay to rename. This has been fixed, and also the RenameUtilities.GetTokenRenameInfo method has been updated to return NoSymbolsTokenInfo when it finds an ITypeSymbol with IsTupleType. --- ...eTrackingTaggerProvider.TrackingSession.cs | 4 +- .../RenameTrackingTaggerProviderTests.cs | 67 +++++++++++++++++++ .../RenameTracking/RenameTrackingTestState.cs | 13 ++++ .../Core/Portable/Rename/RenameUtilities.cs | 5 ++ .../Shared/Extensions/ISymbolExtensions.cs | 10 +++ 5 files changed, 97 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs b/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs index d6e2187869b..c9ee008168b 100644 --- a/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs +++ b/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs @@ -194,7 +194,7 @@ private async Task DetermineIfRenamableSymbolsAsync(IEnum // Get the source symbol if possible var sourceSymbol = await SymbolFinder.FindSourceDefinitionAsync(symbol, document.Project.Solution, _cancellationToken).ConfigureAwait(false) ?? symbol; - if (!sourceSymbol.Locations.All(loc => loc.IsInSource)) + if (!sourceSymbol.IsFromSource()) { return TriggerIdentifierKind.NotRenamable; } @@ -217,7 +217,7 @@ private async Task DetermineIfRenamableSymbolAsync(ISymbo return TriggerIdentifierKind.NotRenamable; } - if (!sourceSymbol.Locations.All(loc => loc.IsInSource)) + if (!sourceSymbol.IsFromSource()) { return TriggerIdentifierKind.NotRenamable; } diff --git a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs index 896c91bdf70..69a199c22a7 100644 --- a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs +++ b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs @@ -1478,5 +1478,72 @@ End Sub await state.AssertNoTag(); } } + + [WpfFact] + [Trait(Traits.Feature, Traits.Features.RenameTracking)] + [WorkItem(14159, "https://github.com/dotnet/roslyn/issues/14159")] + public async Task RenameTrackingNotOnWellKnownValueTupleType() + { + var workspaceXml = @" + + + +using System; + +class C +{ + void M() + { + var x = new ValueTuple$$<int>(); + } +} + +namespace System +{ + public struct ValueTuple<T1> + { + public T1 Item1; + } +} + + +"; + using (var state = await RenameTrackingTestState.CreateFromWorkspaceXmlAsync(workspaceXml, LanguageNames.CSharp)) + { + state.EditorOperations.InsertText("2"); + await state.AssertNoTag(); + } + } + + [WpfFact] + [Trait(Traits.Feature, Traits.Features.RenameTracking)] + [WorkItem(14159, "https://github.com/dotnet/roslyn/issues/14159")] + public async Task RenameTrackingOnThingsCalledValueTupleThatAreNotTheWellKnownType() + { + var workspaceXml = @" + + + +class C +{ + void M() + { + var x = new ValueTuple$$<int>(); + } +} + +public struct ValueTuple<T1> +{ + public T1 Item1; +} + + +"; + using (var state = await RenameTrackingTestState.CreateFromWorkspaceXmlAsync(workspaceXml, LanguageNames.CSharp)) + { + state.EditorOperations.InsertText("2"); + await state.AssertTag("ValueTuple", "ValueTuple2"); + } + } } } diff --git a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs index f4aa44cef15..3c94b36b2c0 100644 --- a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs +++ b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs @@ -60,6 +60,19 @@ internal sealed class RenameTrackingTestState : IDisposable return new RenameTrackingTestState(workspace, languageName, onBeforeGlobalSymbolRenamedReturnValue, onAfterGlobalSymbolRenamedReturnValue); } + public static async Task CreateFromWorkspaceXmlAsync( + string workspaceXml, + string languageName, + bool onBeforeGlobalSymbolRenamedReturnValue = true, + bool onAfterGlobalSymbolRenamedReturnValue = true) + { + var workspace = await TestWorkspace.CreateAsync( + workspaceXml, + exportProvider: TestExportProvider.CreateExportProviderWithCSharpAndVisualBasic()); + + return new RenameTrackingTestState(workspace, languageName, onBeforeGlobalSymbolRenamedReturnValue, onAfterGlobalSymbolRenamedReturnValue); + } + public RenameTrackingTestState( TestWorkspace workspace, string languageName, diff --git a/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs b/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs index 3aeae96bbcd..cd75a8219d8 100644 --- a/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs +++ b/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs @@ -122,6 +122,11 @@ private static bool ShouldRenameOnlyAffectDeclaringProject(ISymbol symbol) var symbolInfo = semanticModel.GetSymbolInfo(token, cancellationToken); if (symbolInfo.Symbol != null) { + if (symbolInfo.Symbol.IsTupleType()) + { + return TokenRenameInfo.NoSymbolsTokenInfo; + } + return TokenRenameInfo.CreateSingleSymbolTokenInfo(symbolInfo.Symbol); } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs index c053b316b5f..651a5fd4bbc 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs @@ -168,6 +168,11 @@ public static bool IsArrayType(this ISymbol symbol) return symbol?.Kind == SymbolKind.ArrayType; } + public static bool IsTupleType(this ISymbol symbol) + { + return (symbol as ITypeSymbol)?.IsTupleType ?? false; + } + public static bool IsAnonymousFunction(this ISymbol symbol) { return (symbol as IMethodSymbol)?.MethodKind == MethodKind.AnonymousFunction; @@ -819,6 +824,11 @@ public static bool IsEventAccessor(this ISymbol symbol) method.MethodKind == MethodKind.EventRemove); } + public static bool IsFromSource(this ISymbol symbol) + { + return symbol.Locations.Any() && symbol.Locations.All(location => location.IsInSource); + } + public static DeclarationModifiers GetSymbolModifiers(this ISymbol symbol) { return new DeclarationModifiers( -- GitLab