diff --git a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs
index b935477b81d21..b821b64c58ad0 100644
--- a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs
+++ b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs
@@ -1448,6 +1448,72 @@ class Class1
await TestChangeNamespaceAsync(code, expectedSourceOriginal);
}
+ [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/34707")]
+ public async Task ChangeFromGlobalNamespace_DoNotSimplifyUnrelatedCode()
+ {
+ var defaultNamespace = "A";
+ var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs");
+ var documentPath2 = CreateDocumentFilePath([], "File2.cs");
+ var code =
+ $$"""
+
+
+
+ class [||]Class1
+ {
+ private A.Class2 c2;
+ private A.B.Class3 c3;
+ private A.B.C.Class4 c4;
+
+ void M1()
+ {
+ int i = 0;
+ // This cast should not be touched.
+ int j = (int)i;
+ }
+ }
+
+
+ namespace A
+ {
+ class Class2{}
+
+ namespace B
+ {
+ class Class3 {}
+
+ namespace C
+ {
+ class Class4 {}
+ }
+ }
+ }
+
+
+ """;
+
+ var expectedSourceOriginal =
+ """
+ namespace A.B.C
+ {
+ class Class1
+ {
+ private Class2 c2;
+ private Class3 c3;
+ private Class4 c4;
+
+ void M1()
+ {
+ int i = 0;
+ // This cast should not be touched.
+ int j = (int)i;
+ }
+ }
+ }
+ """;
+ await TestChangeNamespaceAsync(code, expectedSourceOriginal);
+ }
+
[Fact]
public async Task ChangeFromGlobalNamespace_ChangeUsingsInMultipleContainers()
{
@@ -2169,7 +2235,7 @@ namespace {{defaultNamespace}}
{
public static class Extensions
{
- public static bool Foo(this string s) => true;
+ public static bool Foo(this String s) => true;
}
}
""";
diff --git a/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpChangeNamespaceService.cs b/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpChangeNamespaceService.cs
index 62b99076757e4..e5e5f299ddb32 100644
--- a/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpChangeNamespaceService.cs
+++ b/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpChangeNamespaceService.cs
@@ -13,11 +13,13 @@
using Microsoft.CodeAnalysis.ChangeNamespace;
using Microsoft.CodeAnalysis.CSharp.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Simplification;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
@@ -27,14 +29,18 @@ namespace Microsoft.CodeAnalysis.CSharp.ChangeNamespace;
using static SyntaxFactory;
[ExportLanguageService(typeof(IChangeNamespaceService), LanguageNames.CSharp), Shared]
-internal sealed class CSharpChangeNamespaceService :
- AbstractChangeNamespaceService
+[method: ImportingConstructor]
+[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
+internal sealed class CSharpChangeNamespaceService() :
+ AbstractChangeNamespaceService<
+ CompilationUnitSyntax,
+ MemberDeclarationSyntax,
+ BaseNamespaceDeclarationSyntax,
+ NameSyntax,
+ SimpleNameSyntax,
+ QualifiedCrefSyntax>
{
- [ImportingConstructor]
- [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
- public CSharpChangeNamespaceService()
- {
- }
+ public override AbstractReducer NameReducer { get; } = new CSharpNameReducer();
protected override async Task> GetValidContainersFromAllLinkedDocumentsAsync(
Document document,
diff --git a/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpSyncNamespaceCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpSyncNamespaceCodeRefactoringProvider.cs
index 0dcbb8db822a5..2ebf4708298b3 100644
--- a/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpSyncNamespaceCodeRefactoringProvider.cs
+++ b/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpSyncNamespaceCodeRefactoringProvider.cs
@@ -17,15 +17,11 @@
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.SyncNamespace;
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.SyncNamespace), Shared]
-internal sealed class CSharpSyncNamespaceCodeRefactoringProvider
- : AbstractSyncNamespaceCodeRefactoringProvider
+[method: ImportingConstructor]
+[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
+internal sealed class CSharpSyncNamespaceCodeRefactoringProvider()
+ : AbstractSyncNamespaceCodeRefactoringProvider
{
- [ImportingConstructor]
- [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
- public CSharpSyncNamespaceCodeRefactoringProvider()
- {
- }
-
protected override async Task TryGetApplicableInvocationNodeAsync(Document document, TextSpan span, CancellationToken cancellationToken)
{
if (!span.IsEmpty)
@@ -58,7 +54,4 @@ public CSharpSyncNamespaceCodeRefactoringProvider()
return null;
}
-
- protected override string EscapeIdentifier(string identifier)
- => identifier.EscapeIdentifier();
}
diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs
index d78c5d574e0e7..2684264135022 100644
--- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs
+++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs
@@ -34,7 +34,7 @@ namespace Microsoft.CodeAnalysis.ChangeNamespace;
///
/// This intermediate class is used to hide method `TryGetReplacementReferenceSyntax` from .
///
-internal abstract class AbstractChangeNamespaceService : IChangeNamespaceService
+internal abstract partial class AbstractChangeNamespaceService : IChangeNamespaceService
{
public abstract Task CanChangeNamespaceAsync(Document document, SyntaxNode container, CancellationToken cancellationToken);
@@ -42,6 +42,8 @@ internal abstract class AbstractChangeNamespaceService : IChangeNamespaceService
public abstract Task TryChangeTopLevelNamespacesAsync(Document document, string targetNamespace, CancellationToken cancellationToken);
+ public abstract AbstractReducer NameReducer { get; }
+
///
/// Try to get a new node to replace given node, which is a reference to a top-level type declared inside the
/// namespace to be changed. If this reference is the right side of a qualified name, the new node returned would
@@ -57,11 +59,20 @@ internal abstract class AbstractChangeNamespaceService : IChangeNamespaceService
public abstract bool TryGetReplacementReferenceSyntax(SyntaxNode reference, ImmutableArray newNamespaceParts, ISyntaxFactsService syntaxFacts, [NotNullWhen(returnValue: true)] out SyntaxNode? old, [NotNullWhen(returnValue: true)] out SyntaxNode? @new);
}
-internal abstract class AbstractChangeNamespaceService
+internal abstract partial class AbstractChangeNamespaceService<
+ TCompilationUnitSyntax,
+ TMemberDeclarationSyntax,
+ TNamespaceDeclarationSyntax,
+ TNameSyntax,
+ TSimpleNameSyntax,
+ TCrefSyntax>
: AbstractChangeNamespaceService
- where TNamespaceDeclarationSyntax : SyntaxNode
where TCompilationUnitSyntax : SyntaxNode
where TMemberDeclarationSyntax : SyntaxNode
+ where TNamespaceDeclarationSyntax : TMemberDeclarationSyntax
+ where TNameSyntax : SyntaxNode
+ where TSimpleNameSyntax : TNameSyntax
+ where TCrefSyntax : SyntaxNode
{
private static readonly char[] s_dotSeparator = ['.'];
@@ -125,9 +136,7 @@ public override async Task CanChangeNamespaceAsync(Document document, Synt
var originalNamespaceDeclarations = await GetTopLevelNamespacesAsync(document, cancellationToken).ConfigureAwait(false);
if (originalNamespaceDeclarations.Length == 0)
- {
return null;
- }
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var originalNamespaceName = semanticModel.GetRequiredDeclaredSymbol(originalNamespaceDeclarations.First(), cancellationToken).ToDisplayString();
@@ -139,12 +148,10 @@ public override async Task CanChangeNamespaceAsync(Document document, Synt
for (var i = 0; i < originalNamespaceDeclarations.Length; i++)
{
var namespaceName = semanticModel.GetRequiredDeclaredSymbol(originalNamespaceDeclarations[i], cancellationToken).ToDisplayString();
+
+ // Skip all namespaces that didn't match the original namespace name that we were syncing.
if (namespaceName != originalNamespaceName)
- {
- // Skip all namespaces that didn't match the original namespace name that
- // we were syncing.
continue;
- }
syntaxRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
@@ -194,16 +201,12 @@ public override async Task ChangeNamespaceAsync(
var containersFromAllDocuments = await GetValidContainersFromAllLinkedDocumentsAsync(document, container, cancellationToken).ConfigureAwait(false);
if (containersFromAllDocuments.IsDefault)
- {
return solution;
- }
// No action required if declared namespace already matches target.
var declaredNamespace = GetDeclaredNamespace(container);
if (syntaxFacts.StringComparer.Equals(targetNamespace, declaredNamespace))
- {
return solution;
- }
// Annotate the container nodes so we can still find and modify them after syntax tree has changed.
var annotatedSolution = await AnnotateContainersAsync(solution, containersFromAllDocuments, cancellationToken).ConfigureAwait(false);
@@ -222,9 +225,8 @@ public override async Task ChangeNamespaceAsync(
foreach (var documentId in documentIds)
{
- var (newSolution, refDocumentIds) =
- await ChangeNamespaceInSingleDocumentAsync(solutionAfterNamespaceChange, documentId, declaredNamespace, targetNamespace, cancellationToken)
- .ConfigureAwait(false);
+ var (newSolution, refDocumentIds) = await ChangeNamespaceInSingleDocumentAsync(
+ solutionAfterNamespaceChange, documentId, declaredNamespace, targetNamespace, cancellationToken).ConfigureAwait(false);
solutionAfterNamespaceChange = newSolution;
referenceDocuments.AddRange(refDocumentIds);
}
@@ -463,8 +465,8 @@ private static SyntaxNode CreateImport(SyntaxGenerator syntaxGenerator, string n
}
}
- var documentWithNewNamespace = await FixDeclarationDocumentAsync(document, refLocationsInCurrentDocument, oldNamespace, newNamespace, cancellationToken)
- .ConfigureAwait(false);
+ var documentWithNewNamespace = await FixDeclarationDocumentAsync(
+ document, refLocationsInCurrentDocument, oldNamespace, newNamespace, cancellationToken).ConfigureAwait(false);
var solutionWithChangedNamespace = documentWithNewNamespace.Project.Solution;
var refLocationsInSolution = refLocationsInOtherDocuments
@@ -501,15 +503,6 @@ private static SyntaxNode CreateImport(SyntaxGenerator syntaxGenerator, string n
return (solutionWithFixedReferences, refLocationGroups.SelectAsArray(g => g.Key));
}
- private readonly struct LocationForAffectedSymbol(ReferenceLocation location, bool isReferenceToExtensionMethod)
- {
- public ReferenceLocation ReferenceLocation { get; } = location;
-
- public bool IsReferenceToExtensionMethod { get; } = isReferenceToExtensionMethod;
-
- public Document Document => ReferenceLocation.Document;
- }
-
private static async Task> FindReferenceLocationsForSymbolAsync(
Document document, ISymbol symbol, CancellationToken cancellationToken)
{
@@ -631,9 +624,49 @@ private async Task FixDeclarationDocumentAsync(
var services = documentWithAddedImports.Project.Solution.Services;
root = Formatter.Format(root, Formatter.Annotation, services, documentOptions.FormattingOptions, cancellationToken);
- root = root.WithAdditionalAnnotations(Simplifier.Annotation);
+ using var _ = PooledHashSet.GetInstance(out var allNamespaceNameParts);
+ allNamespaceNameParts.AddRange(oldNamespaceParts);
+ allNamespaceNameParts.AddRange(newNamespaceParts);
+
+ var syntaxFacts = document.GetRequiredLanguageService();
+ root = AddSimplifierAnnotationToPotentialReferences(syntaxFacts, root, allNamespaceNameParts);
+
var formattedDocument = documentWithAddedImports.WithSyntaxRoot(root);
- return await Simplifier.ReduceAsync(formattedDocument, documentOptions.SimplifierOptions, cancellationToken).ConfigureAwait(false);
+ return await SimplifyTypeNamesAsync(formattedDocument, documentOptions, cancellationToken).ConfigureAwait(false);
+ }
+
+ private static SyntaxNode AddSimplifierAnnotationToPotentialReferences(
+ ISyntaxFactsService syntaxFacts, SyntaxNode root, HashSet allNamespaceNameParts)
+ {
+ // Find all identifiers in this tree that use at least one of the namespace names of either the old or new
+ // namespace. Mark those as needing potential complexification/simplification to preserve meaning.
+ //
+ // Note: we could go further here and actually bind these nodes to make sure they are actually references
+ // to one of the namespaces in question. But that doesn't seem super necessary as the chance that these names
+ // are actually to something else *and* they would reduce without issue seems very low. This can be revisited
+ // if we get feedback on this.
+
+ using var _ = PooledHashSet.GetInstance(out var namesToUpdate);
+ foreach (var descendent in root.DescendantNodes(descendIntoTrivia: true))
+ {
+ if (descendent is TSimpleNameSyntax simpleName &&
+ allNamespaceNameParts.Contains(syntaxFacts.GetIdentifierOfSimpleName(simpleName).ValueText))
+ {
+ namesToUpdate.Add(GetHighestNameOrCref(simpleName));
+ }
+ }
+
+ return root.ReplaceNodes(
+ namesToUpdate,
+ (_, current) => current.WithAdditionalAnnotations(Simplifier.Annotation));
+
+ static SyntaxNode GetHighestNameOrCref(TNameSyntax name)
+ {
+ while (name.Parent is TNameSyntax parentName)
+ name = parentName;
+
+ return name.Parent is TCrefSyntax ? name.Parent : name;
+ }
}
private static async Task FixReferencingDocumentAsync(
@@ -651,9 +684,8 @@ private static async Task FixReferencingDocumentAsync(
var newNamespaceParts = GetNamespaceParts(newNamespace);
- var (documentWithRefFixed, containers) =
- await FixReferencesAsync(document, changeNamespaceService, addImportService, refLocations, newNamespaceParts, cancellationToken)
- .ConfigureAwait(false);
+ var (documentWithRefFixed, containers) = await FixReferencesAsync(
+ document, changeNamespaceService, addImportService, refLocations, newNamespaceParts, cancellationToken).ConfigureAwait(false);
var documentOptions = await document.GetCodeCleanupOptionsAsync(cancellationToken).ConfigureAwait(false);
@@ -666,10 +698,24 @@ await FixReferencesAsync(document, changeNamespaceService, addImportService, ref
cancellationToken).ConfigureAwait(false);
// Need to invoke formatter explicitly since we are doing the diff merge ourselves.
- var formattedDocument = await Formatter.FormatAsync(documentWithAdditionalImports, Formatter.Annotation, documentOptions.FormattingOptions, cancellationToken)
- .ConfigureAwait(false);
+ var formattedDocument = await Formatter.FormatAsync(
+ documentWithAdditionalImports, Formatter.Annotation, documentOptions.FormattingOptions, cancellationToken).ConfigureAwait(false);
- return await Simplifier.ReduceAsync(formattedDocument, documentOptions.SimplifierOptions, cancellationToken).ConfigureAwait(false);
+ return await SimplifyTypeNamesAsync(formattedDocument, documentOptions, cancellationToken).ConfigureAwait(false);
+ }
+
+ private static async Task SimplifyTypeNamesAsync(
+ Document document, CodeCleanupOptions documentOptions, CancellationToken cancellationToken)
+ {
+ var changeNamespaceService = document.GetRequiredLanguageService();
+ var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
+ var service = document.GetRequiredLanguageService();
+ return await service.ReduceAsync(
+ document,
+ [new TextSpan(0, text.Length)],
+ documentOptions.SimplifierOptions,
+ [changeNamespaceService.NameReducer],
+ cancellationToken).ConfigureAwait(false);
}
///
@@ -708,9 +754,7 @@ await FixReferencesAsync(document, changeNamespaceService, addImportService, ref
// it will be handled properly because it is one of the reference to the type symbol. Otherwise, we don't
// attempt to make a potential fix, and user might end up with errors as a result.
if (refLoc.ReferenceLocation.Alias != null)
- {
continue;
- }
// Other documents in the solution might have changed after we calculated those ReferenceLocation,
// so we can't trust anything to be still up-to-date except their spans.
@@ -743,9 +787,7 @@ await FixReferencesAsync(document, changeNamespaceService, addImportService, ref
}
foreach (var container in containers)
- {
editor.TrackNode(container);
- }
var fixedDocument = editor.GetChangedDocument();
root = await fixedDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
@@ -858,7 +900,7 @@ private SyntaxNodeSpanStartComparer()
{
}
- public static SyntaxNodeSpanStartComparer Instance { get; } = new SyntaxNodeSpanStartComparer();
+ public static SyntaxNodeSpanStartComparer Instance { get; } = new();
public int Compare(SyntaxNode? x, SyntaxNode? y)
{
diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.cs
index 02e2bb37a7f5a..aa2df2e5388e1 100644
--- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.cs
+++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.cs
@@ -17,6 +17,18 @@ internal abstract partial class AbstractSyncNamespaceCodeRefactoringProvider
+ /// Try to get the node that can be used to trigger the refactoring based on current cursor position.
+ ///
+ ///
+ /// (1) a node of type node, if cursor in the name and it's the
+ /// only namespace declaration in the document.
+ /// (2) a node of type node, if the cursor is in the name of first
+ /// declaration in global namespace and there's no namespace declaration in this document.
+ /// (3) otherwise, null.
+ ///
+ protected abstract Task TryGetApplicableInvocationNodeAsync(Document document, TextSpan span, CancellationToken cancellationToken);
+
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var (document, textSpan, cancellationToken) = context;
@@ -28,9 +40,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
var state = await State.CreateAsync(this, document, textSpan, cancellationToken).ConfigureAwait(false);
if (state == null)
- {
return;
- }
// No move file action if rootnamespace isn't a prefix of current declared namespace
if (state.RelativeDeclaredNamespace != null)
@@ -79,18 +89,4 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
context.RegisterRefactoring(solutionChangeAction, textSpan);
}
}
-
- ///
- /// Try to get the node that can be used to trigger the refactoring based on current cursor position.
- ///
- ///
- /// (1) a node of type node, if cursor in the name and it's the
- /// only namespace declaration in the document.
- /// (2) a node of type node, if the cursor is in the name of first
- /// declaration in global namespace and there's no namespace declaration in this document.
- /// (3) otherwise, null.
- ///
- protected abstract Task TryGetApplicableInvocationNodeAsync(Document document, TextSpan span, CancellationToken cancellationToken);
-
- protected abstract string EscapeIdentifier(string identifier);
}
diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/LocationForAffectedSymbol.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/LocationForAffectedSymbol.cs
new file mode 100644
index 0000000000000..7832d47981ee0
--- /dev/null
+++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/LocationForAffectedSymbol.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.CodeAnalysis.FindSymbols;
+
+namespace Microsoft.CodeAnalysis.ChangeNamespace;
+
+internal abstract partial class AbstractChangeNamespaceService
+{
+ protected readonly struct LocationForAffectedSymbol(ReferenceLocation location, bool isReferenceToExtensionMethod)
+ {
+ public ReferenceLocation ReferenceLocation { get; } = location;
+
+ public bool IsReferenceToExtensionMethod { get; } = isReferenceToExtensionMethod;
+
+ public Document Document => ReferenceLocation.Document;
+ }
+}
diff --git a/src/Features/VisualBasic/Portable/CodeRefactorings/SyncNamespace/VisualBasicChangeNamespaceService.vb b/src/Features/VisualBasic/Portable/CodeRefactorings/SyncNamespace/VisualBasicChangeNamespaceService.vb
index 41fd0cc2fa77b..a333205724464 100644
--- a/src/Features/VisualBasic/Portable/CodeRefactorings/SyncNamespace/VisualBasicChangeNamespaceService.vb
+++ b/src/Features/VisualBasic/Portable/CodeRefactorings/SyncNamespace/VisualBasicChangeNamespaceService.vb
@@ -8,19 +8,29 @@ Imports System.Threading
Imports Microsoft.CodeAnalysis.ChangeNamespace
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.LanguageService
+Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.Text
+Imports Microsoft.CodeAnalysis.VisualBasic.Simplification
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeNamespace
- Friend Class VisualBasicChangeNamespaceService
- Inherits AbstractChangeNamespaceService(Of NamespaceStatementSyntax, CompilationUnitSyntax, StatementSyntax)
+ Friend NotInheritable Class VisualBasicChangeNamespaceService
+ Inherits AbstractChangeNamespaceService(Of
+ CompilationUnitSyntax,
+ StatementSyntax,
+ NamespaceStatementSyntax,
+ NameSyntax,
+ SimpleNameSyntax,
+ CrefReferenceSyntax)
Public Sub New()
End Sub
+ Public Overrides ReadOnly Property NameReducer As AbstractReducer = New VisualBasicNameReducer()
+
Public Overrides Function TryGetReplacementReferenceSyntax(reference As SyntaxNode, newNamespaceParts As ImmutableArray(Of String), syntaxFacts As ISyntaxFactsService, ByRef old As SyntaxNode, ByRef [new] As SyntaxNode) As Boolean
Dim nameRef = TryCast(reference, SimpleNameSyntax)
old = nameRef
@@ -41,7 +51,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeNamespace
[new] = [new].WithTriviaFrom(old)
- ElseIf syntaxFacts.IsNameOfsimpleMemberAccessExpression(nameRef) Then
+ ElseIf syntaxFacts.IsNameOfSimpleMemberAccessExpression(nameRef) Then
old = nameRef.Parent
If IsGlobalNamespace(newNamespaceParts) Then
[new] = SyntaxFactory.SimpleMemberAccessExpression(SyntaxFactory.GlobalName(), nameRef.WithoutTrivia())
diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.cs
index ab1209fb94670..97cab33d7bedd 100644
--- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.cs
+++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.cs
@@ -2,8 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-#nullable disable
-
using System;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Simplification.Simplifiers;
diff --git a/src/Workspaces/Core/Portable/ChangeNamespace/IChangeNamespaceService.cs b/src/Workspaces/Core/Portable/ChangeNamespace/IChangeNamespaceService.cs
index 971b57a606e9a..491404e0e62d2 100644
--- a/src/Workspaces/Core/Portable/ChangeNamespace/IChangeNamespaceService.cs
+++ b/src/Workspaces/Core/Portable/ChangeNamespace/IChangeNamespaceService.cs
@@ -5,11 +5,14 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Simplification;
namespace Microsoft.CodeAnalysis.ChangeNamespace;
internal interface IChangeNamespaceService : ILanguageService
{
+ AbstractReducer NameReducer { get; }
+
///
/// Determine whether we can change the namespace for given in the document.
/// Linked documents are not supported, except for a regular document in a multi-targeting project,
diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicNameReducer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicNameReducer.vb
index 6ce8c2a21f676..caca8e31a6be3 100644
--- a/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicNameReducer.vb
+++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/VisualBasicNameReducer.vb
@@ -5,7 +5,6 @@
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Formatting
-Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.Text
diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/Simplifiers/ExpressionSimplifier.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Simplifiers/ExpressionSimplifier.vb
index 5d59bc8eeecd9..f7b6cd943910b 100644
--- a/src/Workspaces/VisualBasic/Portable/Simplification/Simplifiers/ExpressionSimplifier.vb
+++ b/src/Workspaces/VisualBasic/Portable/Simplification/Simplifiers/ExpressionSimplifier.vb
@@ -21,12 +21,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification.Simplifiers
Private Sub New()
End Sub
- Public Overrides Function TrySimplify(expression As ExpressionSyntax,
- semanticModel As SemanticModel,
- options As VisualBasicSimplifierOptions,
- ByRef replacementNode As ExpressionSyntax,
- ByRef issueSpan As TextSpan,
- cancellationToken As CancellationToken) As Boolean
+ Public Overrides Function TrySimplify(
+ expression As ExpressionSyntax,
+ semanticModel As SemanticModel,
+ options As VisualBasicSimplifierOptions,
+ ByRef replacementNode As ExpressionSyntax,
+ ByRef issueSpan As TextSpan,
+ cancellationToken As CancellationToken) As Boolean
Dim memberAccessExpression = TryCast(expression, MemberAccessExpressionSyntax)
If memberAccessExpression?.Expression?.Kind() = SyntaxKind.MeExpression Then