From 84c0f528dbfa5183a59841143a8643e387ced568 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Wed, 30 Mar 2022 17:05:10 -0700 Subject: [PATCH] Enable NRT in AbstractSyncNamespaceCodeRefactoringProvider --- ...arpSyncNamespaceCodeRefactoringProvider.cs | 9 ++-- ...eRefactoringProvider.MoveFileCodeAction.cs | 8 ++-- ...cNamespaceCodeRefactoringProvider.State.cs | 44 ++++++++++--------- ...actSyncNamespaceCodeRefactoringProvider.cs | 6 +-- 4 files changed, 32 insertions(+), 35 deletions(-) diff --git a/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpSyncNamespaceCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpSyncNamespaceCodeRefactoringProvider.cs index 1bd683cfdeb52..a18d0f1c39500 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpSyncNamespaceCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/SyncNamespace/CSharpSyncNamespaceCodeRefactoringProvider.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.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; @@ -28,14 +26,15 @@ public CSharpSyncNamespaceCodeRefactoringProvider() { } - protected override async Task TryGetApplicableInvocationNodeAsync(Document document, TextSpan span, CancellationToken cancellationToken) + protected override async Task TryGetApplicableInvocationNodeAsync(Document document, TextSpan span, CancellationToken cancellationToken) { if (!span.IsEmpty) return null; - var position = span.Start; + if (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false) is not CompilationUnitSyntax compilationUnit) + return null; - var compilationUnit = (CompilationUnitSyntax)await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var position = span.Start; var namespaceDecls = compilationUnit.DescendantNodes(n => n is CompilationUnitSyntax or BaseNamespaceDeclarationSyntax) .OfType().ToImmutableArray(); diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.MoveFileCodeAction.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.MoveFileCodeAction.cs index a129c38618712..e251d8d797f2b 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.MoveFileCodeAction.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.MoveFileCodeAction.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.Collections.Generic; using System.Collections.Immutable; @@ -14,6 +12,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace @@ -42,12 +41,11 @@ public MoveFileCodeAction(State state, ImmutableArray newFolders) protected override async Task> ComputeOperationsAsync(CancellationToken cancellationToken) { - var id = _state.Document.Id; + var document = _state.Document; var solution = _state.Document.Project.Solution; - var document = solution.GetDocument(id); var newDocumentId = DocumentId.CreateNewId(document.Project.Id, document.Name); - solution = solution.RemoveDocument(id); + solution = solution.RemoveDocument(document.Id); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); solution = solution.AddDocument(newDocumentId, document.Name, text, folders: _newfolders); diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.State.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.State.cs index 9346e60d8705b..5690a4596014d 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.State.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.State.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.Collections.Generic; using System.Diagnostics; using System.IO; @@ -43,7 +41,7 @@ internal sealed class State /// This is the new name we want to change the namespace to. /// Empty string means global namespace, whereas null means change namespace action is not available. /// - public string TargetNamespace { get; } + public string? TargetNamespace { get; } /// /// This is the part of the declared namespace that is contained in default namespace. @@ -51,13 +49,13 @@ internal sealed class State /// For example, if default namespace is `A` and declared namespace is `A.B.C`, /// this would be `B.C`. /// - public string RelativeDeclaredNamespace { get; } + public string? RelativeDeclaredNamespace { get; } private State( Document document, SyntaxNode container, - string targetNamespace, - string relativeDeclaredNamespace) + string? targetNamespace, + string? relativeDeclaredNamespace) { Document = document; Container = container; @@ -65,7 +63,7 @@ private State( RelativeDeclaredNamespace = relativeDeclaredNamespace; } - public static async Task CreateAsync( + public static async Task CreateAsync( AbstractSyncNamespaceCodeRefactoringProvider provider, Document document, TextSpan textSpan, @@ -93,7 +91,7 @@ public static async Task CreateAsync( return null; } - var changeNamespaceService = document.GetLanguageService(); + var changeNamespaceService = document.GetRequiredLanguageService(); var canChange = await changeNamespaceService.CanChangeNamespaceAsync(document, applicableNode, cancellationToken).ConfigureAwait(false); if (!canChange || !IsDocumentPathRootedInProjectFolder(document)) @@ -101,7 +99,7 @@ public static async Task CreateAsync( return null; } - var syntaxFacts = document.GetLanguageService(); + var syntaxFacts = document.GetRequiredLanguageService(); // We can't determine what the expected namespace would be without knowing the default namespace. var defaultNamespace = GetDefaultNamespace(document, syntaxFacts); @@ -152,36 +150,40 @@ public static async Task CreateAsync( /// private static bool IsDocumentPathRootedInProjectFolder(Document document) { + var absoluteDircetoryPath = PathUtilities.GetDirectoryName(document.FilePath); + if (absoluteDircetoryPath is null) + return false; + var projectRoot = PathUtilities.GetDirectoryName(document.Project.FilePath); - var folderPath = Path.Combine(document.Folders.ToArray()); + if (projectRoot is null) + return false; - var absoluteDircetoryPath = PathUtilities.GetDirectoryName(document.FilePath); + var folderPath = Path.Combine(document.Folders.ToArray()); var logicalDirectoryPath = PathUtilities.CombineAbsoluteAndRelativePaths(projectRoot, folderPath); + if (logicalDirectoryPath is null) + return false; return PathUtilities.PathsEqual(absoluteDircetoryPath, logicalDirectoryPath); } - private static string GetDefaultNamespace(Document document, ISyntaxFactsService syntaxFacts) + private static string? GetDefaultNamespace(Document document, ISyntaxFactsService syntaxFacts) { var solution = document.Project.Solution; var linkedIds = document.GetLinkedDocumentIds(); - var documents = linkedIds.SelectAsArray(id => solution.GetDocument(id)).Add(document); + var documents = linkedIds.SelectAsArray(id => solution.GetRequiredDocument(id)).Add(document); // For all projects containing all the linked documents, bail if // 1. Any of them doesn't have default namespace, or // 2. Multiple default namespace are found. (this might be possible by tweaking project file). // The refactoring depends on a single default namespace to operate. - var defaultNamespaceFromProjects = new HashSet( + var defaultNamespaceFromProjects = new HashSet( documents.Select(d => d.Project.DefaultNamespace), syntaxFacts.StringComparer); - if (defaultNamespaceFromProjects.Count != 1 - || defaultNamespaceFromProjects.First() == null) - { + if (defaultNamespaceFromProjects.Count > 1) return null; - } - return defaultNamespaceFromProjects.Single(); + return defaultNamespaceFromProjects.SingleOrDefault(); } /// @@ -195,7 +197,7 @@ private static string GetDefaultNamespace(Document document, ISyntaxFactsService /// the relative namespace is "". /// - If is "" then the relative namespace us . /// - private static string GetRelativeNamespace(string relativeTo, string @namespace, ISyntaxFactsService syntaxFacts) + private static string? GetRelativeNamespace(string relativeTo, string @namespace, ISyntaxFactsService syntaxFacts) { Debug.Assert(relativeTo != null && @namespace != null); @@ -213,7 +215,7 @@ private static string GetRelativeNamespace(string relativeTo, string @namespace, } var containingText = relativeTo + "."; - var namespacePrefix = @namespace.Substring(0, containingText.Length); + var namespacePrefix = @namespace[..containingText.Length]; return syntaxFacts.StringComparer.Equals(containingText, namespacePrefix) ? @namespace[(relativeTo.Length + 1)..] diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.cs index c76493f0ee874..95d0fb8d24e5e 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.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 System.Threading.Tasks; @@ -69,7 +67,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte // is global namespace, i.e. default namespace is "" and the file is located at project // root directory, and no namespace declaration in the document, respectively. - var service = document.GetLanguageService(); + var service = document.GetRequiredLanguageService(); var solutionChangeAction = new ChangeNamespaceCodeAction( state.TargetNamespace.Length == 0 @@ -91,7 +89,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte /// 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 Task TryGetApplicableInvocationNodeAsync(Document document, TextSpan span, CancellationToken cancellationToken); protected abstract string EscapeIdentifier(string identifier);