diff --git a/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj b/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj
index 88fa74e11c498f1a6b10ab79fcde00828dfd1cfa..00583439c981a20e3b203674724d8fdc578cf236 100644
--- a/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj
+++ b/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj
@@ -201,6 +201,7 @@
+
@@ -275,4 +276,4 @@
-
+
\ No newline at end of file
diff --git a/src/EditorFeatures/CSharp/RenameTracking/CSharpRenameTrackingLanguageHeuristicsService.cs b/src/EditorFeatures/CSharp/RenameTracking/CSharpRenameTrackingLanguageHeuristicsService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0fc209a227e50672c700093bb0674e8da50a3636
--- /dev/null
+++ b/src/EditorFeatures/CSharp/RenameTracking/CSharpRenameTrackingLanguageHeuristicsService.cs
@@ -0,0 +1,17 @@
+// 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 Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking;
+using Microsoft.CodeAnalysis.Host.Mef;
+
+namespace Microsoft.CodeAnalysis.Editor.CSharp.RenameTracking
+{
+ [ExportLanguageService(typeof(IRenameTrackingLanguageHeuristicsService), LanguageNames.CSharp), Shared]
+ internal sealed class CSharpRenameTrackingLanguageHeuristicsService : IRenameTrackingLanguageHeuristicsService
+ {
+ public bool IsIdentifierValidForRenameTracking(string name)
+ {
+ return name != "var" && name != "dynamic";
+ }
+ }
+}
diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj
index 2bac1c9d2fe560841f85c02edfe7550dcd9ee0d3..23598c0f06d7150e1b649eee9caf092e57a57a89 100644
--- a/src/EditorFeatures/Core/EditorFeatures.csproj
+++ b/src/EditorFeatures/Core/EditorFeatures.csproj
@@ -586,6 +586,7 @@
+
diff --git a/src/EditorFeatures/Core/Implementation/RenameTracking/IRenameTrackingLanguageHeuristicsService.cs b/src/EditorFeatures/Core/Implementation/RenameTracking/IRenameTrackingLanguageHeuristicsService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..206234fc850836e68b40ecacd159afad2939ffe5
--- /dev/null
+++ b/src/EditorFeatures/Core/Implementation/RenameTracking/IRenameTrackingLanguageHeuristicsService.cs
@@ -0,0 +1,11 @@
+// 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 Microsoft.CodeAnalysis.Host;
+
+namespace Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking
+{
+ internal interface IRenameTrackingLanguageHeuristicsService : ILanguageService
+ {
+ bool IsIdentifierValidForRenameTracking(string name);
+ }
+}
diff --git a/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.StateMachine.cs b/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.StateMachine.cs
index 61076f3967a539bee0ef3d8365c65594776db43f..b227373a37eee9bf75afe4a1593312f582805618 100644
--- a/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.StateMachine.cs
+++ b/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.StateMachine.cs
@@ -253,8 +253,9 @@ public bool CanInvokeRename(out TrackingSession trackingSession, bool isSmartTag
}
ISyntaxFactsService syntaxFactsService;
- return TryGetSyntaxFactsService(out syntaxFactsService) &&
- trackingSession.CanInvokeRename(syntaxFactsService, isSmartTagCheck, waitForResult, cancellationToken);
+ IRenameTrackingLanguageHeuristicsService languageHeuristicsService;
+ return TryGetSyntaxFactsService(out syntaxFactsService) && TryGetLanguageHeuristicsService(out languageHeuristicsService) &&
+ trackingSession.CanInvokeRename(syntaxFactsService, languageHeuristicsService, isSmartTagCheck, waitForResult, cancellationToken);
}
internal async Task> GetDiagnostic(SyntaxTree tree, DiagnosticDescriptor diagnosticDescriptor, CancellationToken cancellationToken)
@@ -332,6 +333,20 @@ private bool TryGetSyntaxFactsService(out ISyntaxFactsService syntaxFactsService
return syntaxFactsService != null;
}
+ private bool TryGetLanguageHeuristicsService(out IRenameTrackingLanguageHeuristicsService languageHeuristicsService)
+ {
+ // Can be called on a background thread
+
+ languageHeuristicsService = null;
+ var document = _buffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
+ if (document != null)
+ {
+ languageHeuristicsService = document.Project.LanguageServices.GetService();
+ }
+
+ return languageHeuristicsService != null;
+ }
+
public void Connect()
{
AssertIsForeground();
diff --git a/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs b/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs
index 4aa38af80dfca182507e186b5bdc23f6e451a95f..a12323635d4d2f62fc7288815046ef1c7c1f2d8e 100644
--- a/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs
+++ b/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs
@@ -159,7 +159,8 @@ private async Task DetermineIfRenamableIdentifierAsync(Sn
return TriggerIdentifierKind.NotRenamable;
}
- if (syntaxFactsService.IsIdentifier(token))
+ var languageHeuristicsService = document.Project.LanguageServices.GetService();
+ if (syntaxFactsService.IsIdentifier(token) && languageHeuristicsService.IsIdentifierValidForRenameTracking(token.Text))
{
var semanticModel = await document.GetSemanticModelForNodeAsync(token.Parent, _cancellationToken).ConfigureAwait(false);
var semanticFacts = document.GetLanguageService();
@@ -218,7 +219,12 @@ private async Task DetermineIfRenamableSymbolAsync(ISymbo
: TriggerIdentifierKind.RenamableReference;
}
- internal bool CanInvokeRename(ISyntaxFactsService syntaxFactsService, bool isSmartTagCheck, bool waitForResult, CancellationToken cancellationToken)
+ internal bool CanInvokeRename(
+ ISyntaxFactsService syntaxFactsService,
+ IRenameTrackingLanguageHeuristicsService languageHeuristicsService,
+ bool isSmartTagCheck,
+ bool waitForResult,
+ CancellationToken cancellationToken)
{
if (IsRenamableIdentifier(_isRenamableIdentifierTask, waitForResult, cancellationToken))
{
@@ -227,7 +233,8 @@ internal bool CanInvokeRename(ISyntaxFactsService syntaxFactsService, bool isSma
var comparison = isRenamingDeclaration || syntaxFactsService.IsCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
if (!string.Equals(OriginalName, newName, comparison) &&
- syntaxFactsService.IsValidIdentifier(newName))
+ syntaxFactsService.IsValidIdentifier(newName) &&
+ languageHeuristicsService.IsIdentifierValidForRenameTracking(newName))
{
// At this point, we want to allow renaming if the user invoked Ctrl+. explicitly, but we
// want to avoid showing a smart tag if we're renaming a reference that binds to an existing
diff --git a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs
index 980867e19ce73b3d38a97b991dea698d0f0c62d1..60f8b19c1141562baf8e4148ad2ebd90b640f62b 100644
--- a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs
+++ b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs
@@ -1090,5 +1090,109 @@ void M()
state.AssertNoTag();
}
}
+
+ [Fact]
+ [WorkItem(2605, "https://github.com/dotnet/roslyn/issues/2605")]
+ [Trait(Traits.Feature, Traits.Features.RenameTracking)]
+ public void RenameTracking_CannotRenameToVarInCSharp()
+ {
+ var code = @"
+class C
+{
+ void M()
+ {
+ C$$ c;
+ }
+}";
+ using (var state = new RenameTrackingTestState(code, LanguageNames.CSharp))
+ {
+ state.EditorOperations.Backspace();
+ state.EditorOperations.InsertText("va");
+
+ state.AssertTag("C", "va");
+ Assert.NotEmpty(state.GetDocumentDiagnostics());
+
+ state.EditorOperations.InsertText("r");
+ state.AssertNoTag();
+ Assert.Empty(state.GetDocumentDiagnostics());
+
+ state.EditorOperations.InsertText("p");
+ state.AssertTag("C", "varp");
+ Assert.NotEmpty(state.GetDocumentDiagnostics());
+ }
+ }
+
+ [Fact]
+ [WorkItem(2605, "https://github.com/dotnet/roslyn/issues/2605")]
+ [Trait(Traits.Feature, Traits.Features.RenameTracking)]
+ public void RenameTracking_CannotRenameFromVarInCSharp()
+ {
+ var code = @"
+class C
+{
+ void M()
+ {
+ var$$ c = new C();
+ }
+}";
+ using (var state = new RenameTrackingTestState(code, LanguageNames.CSharp))
+ {
+ state.EditorOperations.Backspace();
+ state.AssertNoTag();
+ Assert.Empty(state.GetDocumentDiagnostics());
+ }
+ }
+
+ [Fact]
+ [WorkItem(2605, "https://github.com/dotnet/roslyn/issues/2605")]
+ [Trait(Traits.Feature, Traits.Features.RenameTracking)]
+ public void RenameTracking_CanRenameToVarInVisualBasic()
+ {
+ var code = @"
+Class C
+ Sub M()
+ Dim x as C$$
+ End Sub
+End Class";
+ using (var state = new RenameTrackingTestState(code, LanguageNames.VisualBasic))
+ {
+ state.EditorOperations.Backspace();
+ state.EditorOperations.InsertText("var");
+
+ state.AssertTag("C", "var");
+ Assert.NotEmpty(state.GetDocumentDiagnostics());
+ }
+ }
+
+ [Fact]
+ [WorkItem(2605, "https://github.com/dotnet/roslyn/issues/2605")]
+ [Trait(Traits.Feature, Traits.Features.RenameTracking)]
+ public void RenameTracking_CannotRenameToDynamicInCSharp()
+ {
+ var code = @"
+class C
+{
+ void M()
+ {
+ C$$ c;
+ }
+}";
+ using (var state = new RenameTrackingTestState(code, LanguageNames.CSharp))
+ {
+ state.EditorOperations.Backspace();
+ state.EditorOperations.InsertText("dynami");
+
+ state.AssertTag("C", "dynami");
+ Assert.NotEmpty(state.GetDocumentDiagnostics());
+
+ state.EditorOperations.InsertText("c");
+ state.AssertNoTag();
+ Assert.Empty(state.GetDocumentDiagnostics());
+
+ state.EditorOperations.InsertText("s");
+ state.AssertTag("C", "dynamics");
+ Assert.NotEmpty(state.GetDocumentDiagnostics());
+ }
+ }
}
}
diff --git a/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj b/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj
index 486ca275d1efdbc6fb706058909b08b8bbf67f39..a5d1175ca8d9ac5401cbec18a6c55d10e37045ef 100644
--- a/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj
+++ b/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj
@@ -216,6 +216,7 @@
+
diff --git a/src/EditorFeatures/VisualBasic/RenameTracking/BasicRenameTrackingLanguageHeuristicsService.vb b/src/EditorFeatures/VisualBasic/RenameTracking/BasicRenameTrackingLanguageHeuristicsService.vb
new file mode 100644
index 0000000000000000000000000000000000000000..192f43dbd7bab32cd034034b4f5e6ef633e7f24e
--- /dev/null
+++ b/src/EditorFeatures/VisualBasic/RenameTracking/BasicRenameTrackingLanguageHeuristicsService.vb
@@ -0,0 +1,16 @@
+' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+Imports System.Composition
+Imports Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking
+Imports Microsoft.CodeAnalysis.Host.Mef
+
+Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.RenameTracking
+
+ Friend NotInheritable Class BasicRenameTrackingLanguageHeuristicsService
+ Implements IRenameTrackingLanguageHeuristicsService
+
+ Public Function IsIdentifierValidForRenameTracking(name As String) As Boolean Implements IRenameTrackingLanguageHeuristicsService.IsIdentifierValidForRenameTracking
+ Return True
+ End Function
+ End Class
+End Namespace