提交 605070c5 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #14919 from CyrusNajmabadi/moveTypeImports

Only remove imports when moving a type, if the imports moved to the new file.

Fixes #12730
...@@ -307,11 +307,6 @@ private static class Comparisons<T> where T : IComparable<T> ...@@ -307,11 +307,6 @@ private static class Comparisons<T> where T : IComparable<T>
public static readonly IComparer<T> Comparer = Comparer<T>.Create(CompareTo); public static readonly IComparer<T> Comparer = Comparer<T>.Create(CompareTo);
} }
private static class Functions<T>
{
public static readonly Func<T, T> Identity = t => t;
}
public static bool IsSorted<T>(this IEnumerable<T> enumerable, IComparer<T> comparer) public static bool IsSorted<T>(this IEnumerable<T> enumerable, IComparer<T> comparer)
{ {
using (var e = enumerable.GetEnumerator()) using (var e = enumerable.GetEnumerator())
...@@ -393,4 +388,14 @@ public static IComparer<T> ToComparer<T>(this Comparison<T> comparison) ...@@ -393,4 +388,14 @@ public static IComparer<T> ToComparer<T>(this Comparison<T> comparison)
return Comparer<T>.Create(comparison); return Comparer<T>.Create(comparison);
} }
} }
/// <summary>
/// Cached versions of commonly used delegates.
/// </summary>
/// <typeparam name="T"></typeparam>
internal static class Functions<T>
{
public static readonly Func<T, T> Identity = t => t;
public static readonly Func<T, bool> True = t => true;
}
} }
\ No newline at end of file
...@@ -130,6 +130,7 @@ class Class2 { }"; ...@@ -130,6 +130,7 @@ class Class2 { }";
var codeAfterMove = var codeAfterMove =
@"// Banner Text @"// Banner Text
using System;
class Class2 { }"; class Class2 { }";
...@@ -708,5 +709,43 @@ class InnerClass3 ...@@ -708,5 +709,43 @@ class InnerClass3
}"; }";
await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText);
} }
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)]
public async Task MoveTypeUsings1()
{
var code =
@"
// Only used by inner type.
using System;
// Unused by both types.
using System.Collections;
class Outer {
[||]class Inner {
DateTime d;
}
}";
var codeAfterMove = @"
// Unused by both types.
using System.Collections;
partial class Outer {
}";
var expectedDocumentName = "Inner.cs";
var destinationDocumentText =
@"
// Only used by inner type.
using System;
partial class Outer {
class Inner {
DateTime d;
}
}";
await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText);
}
} }
} }
\ No newline at end of file
...@@ -7,10 +7,10 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings.M ...@@ -7,10 +7,10 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings.M
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)> <WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)>
Public Async Function TestMissing_OnMatchingFileName() As Task Public Async Function TestMissing_OnMatchingFileName() As Task
Dim code = Dim code =
<File> "
[||]Class test1 [||]Class test1
End Class End Class
</File>.ConvertTestSourceTag() "
Await TestMissingAsync(code) Await TestMissingAsync(code)
End Function End Function
...@@ -18,12 +18,12 @@ End Class ...@@ -18,12 +18,12 @@ End Class
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)> <WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)>
Public Async Function TestMissing_Nested_OnMatchingFileName_Simple() As Task Public Async Function TestMissing_Nested_OnMatchingFileName_Simple() As Task
Dim code = Dim code =
<File> "
Class Outer Class Outer
[||]Class test1 [||]Class test1
End Class End Class
End Class End Class
</File>.ConvertTestSourceTag() "
Await TestMissingAsync(code) Await TestMissingAsync(code)
End Function End Function
...@@ -31,79 +31,78 @@ End Class ...@@ -31,79 +31,78 @@ End Class
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)> <WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)>
Public Async Function MultipleTypesInFileWithNoContainerNamespace() As Task Public Async Function MultipleTypesInFileWithNoContainerNamespace() As Task
Dim code = Dim code =
<File> "
[||]Class Class1 [||]Class Class1
End Class End Class
Class Class2 Class Class2
End Class End Class
</File> "
Dim codeAfterMove = Dim codeAfterMove =
<File> "
Class Class2 Class Class2
End Class End Class
</File> "
Dim expectedDocumentName = "Class1.vb" Dim expectedDocumentName = "Class1.vb"
Dim destinationDocumentText = Dim destinationDocumentText =
<File> "
Class Class1 Class Class1
End Class End Class
</File> "
Await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText) Await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText)
End Function End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)> <WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)>
Public Async Function MoveNestedTypeToNewFile_Simple() As Task Public Async Function MoveNestedTypeToNewFile_Simple() As Task
Dim code = Dim code =
<File> "
Public Class Class1 Public Class Class1
Class Class2[||] Class Class2[||]
End Class End Class
End Class End Class
</File> "
Dim codeAfterMove = Dim codeAfterMove =
<File> "
Public Partial Class Class1 Public Partial Class Class1
End Class End Class
</File> "
Dim expectedDocumentName = "Class2.vb" Dim expectedDocumentName = "Class2.vb"
Dim destinationDocumentText = Dim destinationDocumentText =
<File> "
Public Partial Class Class1 Public Partial Class Class1
Class Class2 Class Class2
End Class End Class
End Class End Class
</File> "
Await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText) Await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText)
End Function End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)> <WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)>
Public Async Function MoveNestedTypeToNewFile_Simple_DottedName() As Task Public Async Function MoveNestedTypeToNewFile_Simple_DottedName() As Task
Dim code = Dim code =
<File> "
Public Class Class1 Public Class Class1
Class Class2[||] Class Class2[||]
End Class End Class
End Class End Class
</File> "
Dim codeAfterMove = Dim codeAfterMove =
<File> "
Public Partial Class Class1 Public Partial Class Class1
End Class End Class
</File> "
Dim expectedDocumentName = "Class1.Class2.vb" Dim expectedDocumentName = "Class1.Class2.vb"
Dim destinationDocumentText = Dim destinationDocumentText =
<File> "
Public Partial Class Class1 Public Partial Class Class1
Class Class2 Class Class2
End Class End Class
End Class End Class
</File> "
Await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText, index:=1) Await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText, index:=1)
End Function End Function
...@@ -111,33 +110,75 @@ End Class ...@@ -111,33 +110,75 @@ End Class
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)> <WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)>
Public Async Function MoveNestedTypeToNewFile_RemoveComments() As Task Public Async Function MoveNestedTypeToNewFile_RemoveComments() As Task
Dim code = Dim code =
<File> "
''' Outer comment ''' Outer comment
Public Class Class1 Public Class Class1
''' Inner comment ''' Inner comment
Class Class2[||] Class Class2[||]
End Class End Class
End Class End Class
</File> "
Dim codeAfterMove = Dim codeAfterMove =
<File> "
''' Outer comment ''' Outer comment
Public Partial Class Class1 Public Partial Class Class1
End Class End Class
</File> "
Dim expectedDocumentName = "Class1.Class2.vb" Dim expectedDocumentName = "Class1.Class2.vb"
Dim destinationDocumentText = Dim destinationDocumentText =
<File> "
Public Partial Class Class1 Public Partial Class Class1
''' Inner comment ''' Inner comment
Class Class2 Class Class2
End Class End Class
End Class End Class
</File> "
Await TestMoveTypeToNewFileAsync( Await TestMoveTypeToNewFileAsync(
code, codeAfterMove, expectedDocumentName, destinationDocumentText, code, codeAfterMove, expectedDocumentName, destinationDocumentText,
index:=1, compareTokens:=False) index:=1, compareTokens:=False)
End Function End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)>
Public Async Function TestImports() As Task
Dim code =
"
' Used only by inner
Imports System
' Not used
Imports System.Collections
Class Outer
[||]Class Inner
Sub M(d as DateTime)
End Sub
End Class
End Class
"
Dim codeAfterMove =
"
' Not used
Imports System.Collections
Partial Class Outer
End Class
"
Dim expectedDocumentName = "Inner.vb"
Dim destinationDocumentText =
"
' Used only by inner
Imports System
Partial Class Outer
Class Inner
Sub M(d as DateTime)
End Sub
End Class
End Class
"
Await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText)
End Function
End Class End Class
End Namespace End Namespace
\ No newline at end of file
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Option Strict Off
Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions
......
...@@ -28,7 +28,8 @@ protected override LocalizableString GetTitleAndMessageFormatForClassificationId ...@@ -28,7 +28,8 @@ protected override LocalizableString GetTitleAndMessageFormatForClassificationId
protected override IEnumerable<SyntaxNode> GetUnnecessaryImports( protected override IEnumerable<SyntaxNode> GetUnnecessaryImports(
SemanticModel semanticModel, SyntaxNode root, CancellationToken cancellationToken) SemanticModel semanticModel, SyntaxNode root, CancellationToken cancellationToken)
{ {
return CSharpRemoveUnnecessaryImportsService.GetUnnecessaryImports(semanticModel, root, cancellationToken); return CSharpRemoveUnnecessaryImportsService.GetUnnecessaryImportsShared(
semanticModel, root, predicate: null, cancellationToken: cancellationToken);
} }
protected override IEnumerable<TextSpan> GetFixableDiagnosticSpans( protected override IEnumerable<TextSpan> GetFixableDiagnosticSpans(
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition; using System.Composition;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
...@@ -9,23 +11,26 @@ ...@@ -9,23 +11,26 @@
using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.RemoveUnnecessaryImports; using Microsoft.CodeAnalysis.RemoveUnnecessaryImports;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryImports namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryImports
{ {
[ExportLanguageService(typeof(IRemoveUnnecessaryImportsService), LanguageNames.CSharp), Shared] [ExportLanguageService(typeof(IRemoveUnnecessaryImportsService), LanguageNames.CSharp), Shared]
internal partial class CSharpRemoveUnnecessaryImportsService : internal partial class CSharpRemoveUnnecessaryImportsService :
AbstractRemoveUnnecessaryImportsService<UsingDirectiveSyntax>, IRemoveUnnecessaryImportsService AbstractRemoveUnnecessaryImportsService<UsingDirectiveSyntax>
{ {
public static IEnumerable<SyntaxNode> GetUnnecessaryImports(SemanticModel semanticModel, SyntaxNode root, CancellationToken cancellationToken) public static ImmutableArray<UsingDirectiveSyntax> GetUnnecessaryImportsShared(
SemanticModel semanticModel, SyntaxNode root,
Func<SyntaxNode, bool> predicate, CancellationToken cancellationToken)
{ {
predicate = predicate ?? Functions<SyntaxNode>.True;
var diagnostics = semanticModel.GetDiagnostics(cancellationToken: cancellationToken); var diagnostics = semanticModel.GetDiagnostics(cancellationToken: cancellationToken);
if (!diagnostics.Any()) if (!diagnostics.Any())
{ {
return null; return ImmutableArray<UsingDirectiveSyntax>.Empty;
} }
var unnecessaryImports = new HashSet<UsingDirectiveSyntax>(); var unnecessaryImports = new HashSet<UsingDirectiveSyntax>();
...@@ -36,26 +41,26 @@ public static IEnumerable<SyntaxNode> GetUnnecessaryImports(SemanticModel semant ...@@ -36,26 +41,26 @@ public static IEnumerable<SyntaxNode> GetUnnecessaryImports(SemanticModel semant
{ {
var node = root.FindNode(diagnostic.Location.SourceSpan) as UsingDirectiveSyntax; var node = root.FindNode(diagnostic.Location.SourceSpan) as UsingDirectiveSyntax;
if (node != null) if (node != null && predicate(node))
{ {
unnecessaryImports.Add(node); unnecessaryImports.Add(node);
} }
} }
} }
if (cancellationToken.IsCancellationRequested || !unnecessaryImports.Any()) return unnecessaryImports.ToImmutableArray();
{
return null;
}
return unnecessaryImports;
} }
public async Task<Document> RemoveUnnecessaryImportsAsync(Document document, CancellationToken cancellationToken) public override async Task<Document> RemoveUnnecessaryImportsAsync(
Document document,
Func<SyntaxNode, bool> predicate,
CancellationToken cancellationToken)
{ {
predicate = predicate ?? Functions<SyntaxNode>.True;
using (Logger.LogBlock(FunctionId.Refactoring_RemoveUnnecessaryImports_CSharp, cancellationToken)) using (Logger.LogBlock(FunctionId.Refactoring_RemoveUnnecessaryImports_CSharp, cancellationToken))
{ {
var unnecessaryImports = await GetCommonUnnecessaryImportsOfAllContextAsync(document, cancellationToken).ConfigureAwait(false); var unnecessaryImports = await GetCommonUnnecessaryImportsOfAllContextAsync(
document, predicate, cancellationToken).ConfigureAwait(false);
if (unnecessaryImports == null || unnecessaryImports.Any(import => import.OverlapsHiddenPosition(cancellationToken))) if (unnecessaryImports == null || unnecessaryImports.Any(import => import.OverlapsHiddenPosition(cancellationToken)))
{ {
return document; return document;
...@@ -66,18 +71,16 @@ public async Task<Document> RemoveUnnecessaryImportsAsync(Document document, Can ...@@ -66,18 +71,16 @@ public async Task<Document> RemoveUnnecessaryImportsAsync(Document document, Can
var oldRoot = (CompilationUnitSyntax)root; var oldRoot = (CompilationUnitSyntax)root;
var newRoot = (CompilationUnitSyntax)new Rewriter(unnecessaryImports, cancellationToken).Visit(oldRoot); var newRoot = (CompilationUnitSyntax)new Rewriter(unnecessaryImports, cancellationToken).Visit(oldRoot);
if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested();
{
return null;
}
return document.WithSyntaxRoot(await FormatResultAsync(document, newRoot, cancellationToken).ConfigureAwait(false)); return document.WithSyntaxRoot(await FormatResultAsync(document, newRoot, cancellationToken).ConfigureAwait(false));
} }
} }
protected override IEnumerable<UsingDirectiveSyntax> GetUnusedUsings(SemanticModel model, SyntaxNode root, CancellationToken cancellationToken) protected override ImmutableArray<UsingDirectiveSyntax> GetUnnecessaryImports(
SemanticModel model, SyntaxNode root,
Func<SyntaxNode, bool> predicate, CancellationToken cancellationToken)
{ {
return GetUnnecessaryImports(model, root, cancellationToken) as IEnumerable<UsingDirectiveSyntax>; return GetUnnecessaryImportsShared(model, root, predicate, cancellationToken);
} }
private async Task<SyntaxNode> FormatResultAsync(Document document, CompilationUnitSyntax newRoot, CancellationToken cancellationToken) private async Task<SyntaxNode> FormatResultAsync(Document document, CompilationUnitSyntax newRoot, CancellationToken cancellationToken)
......
...@@ -50,7 +50,9 @@ internal override async Task<ImmutableArray<CodeActionOperation>> GetOperationsA ...@@ -50,7 +50,9 @@ internal override async Task<ImmutableArray<CodeActionOperation>> GetOperationsA
var projectToBeUpdated = SemanticDocument.Document.Project; var projectToBeUpdated = SemanticDocument.Document.Project;
var newDocumentId = DocumentId.CreateNewId(projectToBeUpdated.Id, FileName); var newDocumentId = DocumentId.CreateNewId(projectToBeUpdated.Id, FileName);
var solutionWithNewDocument = await AddNewDocumentWithSingleTypeDeclarationAndImportsAsync(newDocumentId).ConfigureAwait(false); var documentWithMovedType = await AddNewDocumentWithSingleTypeDeclarationAndImportsAsync(newDocumentId).ConfigureAwait(false);
var solutionWithNewDocument = documentWithMovedType.Project.Solution;
// Get the original source document again, from the latest forked solution. // Get the original source document again, from the latest forked solution.
var sourceDocument = solutionWithNewDocument.GetDocument(SemanticDocument.Document.Id); var sourceDocument = solutionWithNewDocument.GetDocument(SemanticDocument.Document.Id);
...@@ -58,7 +60,7 @@ internal override async Task<ImmutableArray<CodeActionOperation>> GetOperationsA ...@@ -58,7 +60,7 @@ internal override async Task<ImmutableArray<CodeActionOperation>> GetOperationsA
// update source document to add partial modifiers to type chain // update source document to add partial modifiers to type chain
// and/or remove type declaration from original source document. // and/or remove type declaration from original source document.
var solutionWithBothDocumentsUpdated = await RemoveTypeFromSourceDocumentAsync( var solutionWithBothDocumentsUpdated = await RemoveTypeFromSourceDocumentAsync(
sourceDocument).ConfigureAwait(false); sourceDocument, documentWithMovedType).ConfigureAwait(false);
return ImmutableArray.Create<CodeActionOperation>(new ApplyChangesOperation(solutionWithBothDocumentsUpdated)); return ImmutableArray.Create<CodeActionOperation>(new ApplyChangesOperation(solutionWithBothDocumentsUpdated));
} }
...@@ -69,7 +71,7 @@ internal override async Task<ImmutableArray<CodeActionOperation>> GetOperationsA ...@@ -69,7 +71,7 @@ internal override async Task<ImmutableArray<CodeActionOperation>> GetOperationsA
/// </summary> /// </summary>
/// <param name="newDocumentId">id for the new document to be added</param> /// <param name="newDocumentId">id for the new document to be added</param>
/// <returns>the new solution which contains a new document with the type being moved</returns> /// <returns>the new solution which contains a new document with the type being moved</returns>
private async Task<Solution> AddNewDocumentWithSingleTypeDeclarationAndImportsAsync( private async Task<Document> AddNewDocumentWithSingleTypeDeclarationAndImportsAsync(
DocumentId newDocumentId) DocumentId newDocumentId)
{ {
var document = SemanticDocument.Document; var document = SemanticDocument.Document;
...@@ -101,20 +103,22 @@ internal override async Task<ImmutableArray<CodeActionOperation>> GetOperationsA ...@@ -101,20 +103,22 @@ internal override async Task<ImmutableArray<CodeActionOperation>> GetOperationsA
// update the text for the new document // update the text for the new document
solutionWithNewDocument = solutionWithNewDocument.WithDocumentSyntaxRoot(newDocumentId, modifiedRoot, PreservationMode.PreserveIdentity); solutionWithNewDocument = solutionWithNewDocument.WithDocumentSyntaxRoot(newDocumentId, modifiedRoot, PreservationMode.PreserveIdentity);
// get the updated document, perform clean up like remove unused usings. // get the updated document, give it the minimal set of imports that the type
// inside it needs.
var service = document.GetLanguageService<IRemoveUnnecessaryImportsService>();
var newDocument = solutionWithNewDocument.GetDocument(newDocumentId); var newDocument = solutionWithNewDocument.GetDocument(newDocumentId);
newDocument = await CleanUpDocumentAsync(newDocument).ConfigureAwait(false); newDocument = await service.RemoveUnnecessaryImportsAsync(newDocument, CancellationToken).ConfigureAwait(false);
return newDocument.Project.Solution; return newDocument;
} }
/// <summary> /// <summary>
/// update the original document and remove the type that was moved. /// update the original document and remove the type that was moved.
/// perform other fix ups as necessary. /// perform other fix ups as necessary.
/// </summary> /// </summary>
/// <param name="sourceDocument">original document</param>
/// <returns>an updated solution with the original document fixed up as appropriate.</returns> /// <returns>an updated solution with the original document fixed up as appropriate.</returns>
private async Task<Solution> RemoveTypeFromSourceDocumentAsync(Document sourceDocument) private async Task<Solution> RemoveTypeFromSourceDocumentAsync(
Document sourceDocument, Document documentWithMovedType)
{ {
var documentEditor = await DocumentEditor.CreateAsync(sourceDocument, CancellationToken).ConfigureAwait(false); var documentEditor = await DocumentEditor.CreateAsync(sourceDocument, CancellationToken).ConfigureAwait(false);
...@@ -126,7 +130,24 @@ private async Task<Solution> RemoveTypeFromSourceDocumentAsync(Document sourceDo ...@@ -126,7 +130,24 @@ private async Task<Solution> RemoveTypeFromSourceDocumentAsync(Document sourceDo
var updatedDocument = documentEditor.GetChangedDocument(); var updatedDocument = documentEditor.GetChangedDocument();
updatedDocument = await CleanUpDocumentAsync(updatedDocument).ConfigureAwait(false); // Now, remove any imports that we no longer need *if* they were used in the new
// file with the moved type. Essentially, those imports were here just to serve
// that new type and we shoudl remove them. If we have *other* imports that that
// other file does not use *and* we do not use, we'll still keep those around.
// Those may be important to the user for code they're about to write, and we
// don't want to interfere with them by removing them.
var service = sourceDocument.GetLanguageService<IRemoveUnnecessaryImportsService>();
var syntaxFacts = sourceDocument.GetLanguageService<ISyntaxFactsService>();
var rootWithMovedType = await documentWithMovedType.GetSyntaxRootAsync(CancellationToken).ConfigureAwait(false);
var movedImports = rootWithMovedType.DescendantNodes()
.Where(syntaxFacts.IsUsingOrExternOrImport)
.ToImmutableArray();
Func<SyntaxNode, bool> predicate = n => movedImports.Contains(i => i.IsEquivalentTo(n));
updatedDocument = await service.RemoveUnnecessaryImportsAsync(
updatedDocument, predicate, CancellationToken).ConfigureAwait(false);
return updatedDocument.Project.Solution; return updatedDocument.Project.Solution;
} }
...@@ -206,16 +227,6 @@ private static bool FilterToTopLevelMembers(SyntaxNode node, SyntaxNode typeNode ...@@ -206,16 +227,6 @@ private static bool FilterToTopLevelMembers(SyntaxNode node, SyntaxNode typeNode
} }
} }
} }
/// <summary>
/// Perform clean ups on a given document.
/// </summary>
private Task<Document> CleanUpDocumentAsync(Document document)
{
return document
.GetLanguageService<IRemoveUnnecessaryImportsService>()
.RemoveUnnecessaryImportsAsync(document, CancellationToken);
}
} }
} }
} }
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Collections.Immutable;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities; using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.RemoveUnnecessaryImports namespace Microsoft.CodeAnalysis.RemoveUnnecessaryImports
{ {
internal abstract class AbstractRemoveUnnecessaryImportsService<T> : ILanguageService, IEqualityComparer<T> where T : SyntaxNode internal abstract class AbstractRemoveUnnecessaryImportsService<T> :
IRemoveUnnecessaryImportsService, IEqualityComparer<T> where T : SyntaxNode
{ {
protected abstract IEnumerable<T> GetUnusedUsings(SemanticModel model, SyntaxNode root, CancellationToken cancellationToken); public Task<Document> RemoveUnnecessaryImportsAsync(Document document, CancellationToken cancellationToken)
=> RemoveUnnecessaryImportsAsync(document, predicate: null, cancellationToken: cancellationToken);
protected async Task<HashSet<T>> GetCommonUnnecessaryImportsOfAllContextAsync(Document document, CancellationToken cancellationToken) public abstract Task<Document> RemoveUnnecessaryImportsAsync(Document fromDocument, Func<SyntaxNode, bool> predicate, CancellationToken cancellationToken);
protected abstract ImmutableArray<T> GetUnnecessaryImports(
SemanticModel model, SyntaxNode root,
Func<SyntaxNode, bool> predicate, CancellationToken cancellationToken);
protected async Task<HashSet<T>> GetCommonUnnecessaryImportsOfAllContextAsync(
Document document, Func<SyntaxNode, bool> predicate, CancellationToken cancellationToken)
{ {
var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var unnecessaryImports = new HashSet<T>(GetUnnecessaryImportsOrEmpty(model, root, cancellationToken), this); var unnecessaryImports = new HashSet<T>(this);
unnecessaryImports.AddRange(GetUnnecessaryImports(
model, root, predicate, cancellationToken));
foreach (var current in document.GetLinkedDocuments()) foreach (var current in document.GetLinkedDocuments())
{ {
var currentModel = await current.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var currentModel = await current.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var currentRoot = await current.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var currentRoot = await current.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
unnecessaryImports.IntersectWith(GetUnnecessaryImportsOrEmpty(currentModel, currentRoot, cancellationToken)); unnecessaryImports.IntersectWith(GetUnnecessaryImports(
currentModel, currentRoot, predicate, cancellationToken));
} }
return unnecessaryImports; return unnecessaryImports;
} }
private IEnumerable<T> GetUnnecessaryImportsOrEmpty(SemanticModel model, SyntaxNode root, CancellationToken cancellationToken)
{
var imports = GetUnusedUsings(model, root, cancellationToken) ?? SpecializedCollections.EmptyEnumerable<T>();
return imports.Cast<T>();
}
bool IEqualityComparer<T>.Equals(T x, T y) bool IEqualityComparer<T>.Equals(T x, T y)
{ {
return x.Span == y.Span; return x.Span == y.Span;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host;
...@@ -8,8 +9,7 @@ namespace Microsoft.CodeAnalysis.RemoveUnnecessaryImports ...@@ -8,8 +9,7 @@ namespace Microsoft.CodeAnalysis.RemoveUnnecessaryImports
{ {
internal interface IRemoveUnnecessaryImportsService : ILanguageService internal interface IRemoveUnnecessaryImportsService : ILanguageService
{ {
/// <returns>Returns the rewritten document, or the document passed in if no changes were made. If cancellation
/// was observed, it returns null.</returns>
Task<Document> RemoveUnnecessaryImportsAsync(Document document, CancellationToken cancellationToken); Task<Document> RemoveUnnecessaryImportsAsync(Document document, CancellationToken cancellationToken);
Task<Document> RemoveUnnecessaryImportsAsync(Document fromDocument, Func<SyntaxNode, bool> predicate, CancellationToken cancellationToken);
} }
} }
\ No newline at end of file
...@@ -20,13 +20,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Diagnostics.RemoveUnnecessaryImport ...@@ -20,13 +20,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Diagnostics.RemoveUnnecessaryImport
Protected Overrides Function GetUnnecessaryImports( Protected Overrides Function GetUnnecessaryImports(
semanticModel As SemanticModel, root As SyntaxNode, cancellationToken As CancellationToken) As IEnumerable(Of SyntaxNode) semanticModel As SemanticModel, root As SyntaxNode, cancellationToken As CancellationToken) As IEnumerable(Of SyntaxNode)
Return VisualBasicRemoveUnnecessaryImportsService.GetUnnecessaryImports(semanticModel, root, cancellationToken) Return VisualBasicRemoveUnnecessaryImportsService.GetUnnecessaryImportsShared(
semanticModel, root, predicate:=Nothing, cancellationToken:=cancellationToken)
End Function End Function
Protected Overrides Function GetFixableDiagnosticSpans( Protected Overrides Function GetFixableDiagnosticSpans(
nodes As IEnumerable(Of SyntaxNode), tree As SyntaxTree, cancellationToken As CancellationToken) As IEnumerable(Of TextSpan) nodes As IEnumerable(Of SyntaxNode), tree As SyntaxTree, cancellationToken As CancellationToken) As IEnumerable(Of TextSpan)
' Create one fixable diagnostic that contains the entire Imports list. ' Create one fixable diagnostic that contains the entire Imports list.
Return SpecializedCollections.SingletonEnumerable(Of TextSpan)(tree.GetCompilationUnitRoot().Imports.GetContainedSpan()) Return SpecializedCollections.SingletonEnumerable(tree.GetCompilationUnitRoot().Imports.GetContainedSpan())
End Function End Function
Protected Overrides Function GetLastTokenDelegateForContiguousSpans() As Func(Of SyntaxNode, SyntaxToken) Protected Overrides Function GetLastTokenDelegateForContiguousSpans() As Func(Of SyntaxNode, SyntaxToken)
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. ' 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.Collections.Immutable
Imports System.Composition Imports System.Composition
Imports System.Threading Imports System.Threading
Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis
...@@ -13,12 +14,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports ...@@ -13,12 +14,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports
<ExportLanguageService(GetType(IRemoveUnnecessaryImportsService), LanguageNames.VisualBasic), [Shared]> <ExportLanguageService(GetType(IRemoveUnnecessaryImportsService), LanguageNames.VisualBasic), [Shared]>
Partial Friend Class VisualBasicRemoveUnnecessaryImportsService Partial Friend Class VisualBasicRemoveUnnecessaryImportsService
Inherits AbstractRemoveUnnecessaryImportsService(Of ImportsClauseSyntax) Inherits AbstractRemoveUnnecessaryImportsService(Of ImportsClauseSyntax)
Implements IRemoveUnnecessaryImportsService
Public Shared Function GetUnnecessaryImports(model As SemanticModel, root As SyntaxNode, cancellationToken As CancellationToken) As IEnumerable(Of SyntaxNode) Public Shared Function GetUnnecessaryImportsShared(
Dim unnecessaryImports = DirectCast(GetIndividualUnnecessaryImports(model, root, cancellationToken), ISet(Of ImportsClauseSyntax)) model As SemanticModel,
If unnecessaryImports Is Nothing Then root As SyntaxNode,
Return Nothing predicate As Func(Of SyntaxNode, Boolean),
cancellationToken As CancellationToken) As ImmutableArray(Of SyntaxNode)
predicate = If(predicate, Functions(Of SyntaxNode).True)
Dim unnecessaryImports = GetIndividualUnnecessaryImportsShared(model, root, predicate, cancellationToken)
If Not unnecessaryImports.Any() Then
Return ImmutableArray(Of SyntaxNode).Empty
End If End If
Return unnecessaryImports.Select( Return unnecessaryImports.Select(
...@@ -29,14 +34,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports ...@@ -29,14 +34,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports
Else Else
Return i Return i
End If End If
End Function).ToSet() End Function).ToImmutableArray()
End Function End Function
Public Async Function RemoveUnnecessaryImportsAsync(document As Document, cancellationToken As CancellationToken) As Task(Of Document) Implements IRemoveUnnecessaryImportsService.RemoveUnnecessaryImportsAsync Public Overrides Async Function RemoveUnnecessaryImportsAsync(
document As Document,
predicate As Func(Of SyntaxNode, Boolean),
cancellationToken As CancellationToken) As Task(Of Document)
predicate = If(predicate, Functions(Of SyntaxNode).True)
Using Logger.LogBlock(FunctionId.Refactoring_RemoveUnnecessaryImports_VisualBasic, cancellationToken) Using Logger.LogBlock(FunctionId.Refactoring_RemoveUnnecessaryImports_VisualBasic, cancellationToken)
Dim unnecessaryImports = Await GetCommonUnnecessaryImportsOfAllContextAsync(document, cancellationToken).ConfigureAwait(False) Dim unnecessaryImports = Await GetCommonUnnecessaryImportsOfAllContextAsync(
If unnecessaryImports Is Nothing OrElse unnecessaryImports.Any(Function(import) import.OverlapsHiddenPosition(cancellationToken)) Then document, predicate, cancellationToken).ConfigureAwait(False)
If unnecessaryImports.Any(Function(import) import.OverlapsHiddenPosition(cancellationToken)) Then
Return document Return document
End If End If
...@@ -46,56 +57,53 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports ...@@ -46,56 +57,53 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports
Dim newRoot = New Rewriter(unnecessaryImports, cancellationToken).Visit(oldRoot) Dim newRoot = New Rewriter(unnecessaryImports, cancellationToken).Visit(oldRoot)
newRoot = newRoot.WithAdditionalAnnotations(Formatter.Annotation) newRoot = newRoot.WithAdditionalAnnotations(Formatter.Annotation)
If cancellationToken.IsCancellationRequested Then cancellationToken.ThrowIfCancellationRequested()
Return Nothing
End If
Return document.WithSyntaxRoot(newRoot) Return document.WithSyntaxRoot(newRoot)
End Using End Using
End Function End Function
Protected Overrides Function GetUnusedUsings(model As SemanticModel, root As SyntaxNode, cancellationToken As CancellationToken) As IEnumerable(Of ImportsClauseSyntax) Protected Overrides Function GetUnnecessaryImports(
Return DirectCast(GetIndividualUnnecessaryImports(model, root, cancellationToken), IEnumerable(Of ImportsClauseSyntax)) model As SemanticModel, root As SyntaxNode,
predicate As Func(Of SyntaxNode, Boolean),
cancellationToken As CancellationToken) As ImmutableArray(Of ImportsClauseSyntax)
Return GetIndividualUnnecessaryImportsShared(model, root, predicate, cancellationToken)
End Function End Function
Private Shared Function GetIndividualUnnecessaryImports(semanticModel As SemanticModel, root As SyntaxNode, cancellationToken As CancellationToken) As IEnumerable(Of SyntaxNode) Private Shared Function GetIndividualUnnecessaryImportsShared(
Dim diagnostics = semanticModel.GetDiagnostics(cancellationToken:=cancellationToken) semanticModel As SemanticModel, root As SyntaxNode,
predicate As Func(Of SyntaxNode, Boolean),
CancellationToken As CancellationToken) As ImmutableArray(Of ImportsClauseSyntax)
Dim diagnostics = semanticModel.GetDiagnostics(cancellationToken:=CancellationToken)
Dim unnecessaryImports = New HashSet(Of ImportsClauseSyntax) Dim unnecessaryImports = New HashSet(Of ImportsClauseSyntax)
For Each diagnostic In diagnostics For Each diagnostic In diagnostics
If diagnostic.Id = "BC50000" Then If diagnostic.Id = "BC50000" Then
Dim node = root.FindNode(diagnostic.Location.SourceSpan) Dim node = root.FindNode(diagnostic.Location.SourceSpan)
If node IsNot Nothing Then If node IsNot Nothing AndAlso predicate(node) Then
unnecessaryImports.Add(DirectCast(node, ImportsClauseSyntax)) unnecessaryImports.Add(DirectCast(node, ImportsClauseSyntax))
End If End If
End If End If
If diagnostic.Id = "BC50001" Then If diagnostic.Id = "BC50001" Then
Dim node = TryCast(root.FindNode(diagnostic.Location.SourceSpan), ImportsStatementSyntax) Dim node = TryCast(root.FindNode(diagnostic.Location.SourceSpan), ImportsStatementSyntax)
If node IsNot Nothing Then If node IsNot Nothing AndAlso predicate(node) Then
unnecessaryImports.AddRange(node.ImportsClauses) unnecessaryImports.AddRange(node.ImportsClauses)
End If End If
End If End If
Next Next
If cancellationToken.IsCancellationRequested Then
Return Nothing
End If
Dim oldRoot = DirectCast(root, CompilationUnitSyntax) Dim oldRoot = DirectCast(root, CompilationUnitSyntax)
AddRedundantImports(oldRoot, semanticModel, unnecessaryImports, cancellationToken) AddRedundantImports(oldRoot, semanticModel, unnecessaryImports, predicate, CancellationToken)
If unnecessaryImports.Count = 0 Then
Return Nothing
End If
Return unnecessaryImports Return unnecessaryImports.ToImmutableArray()
End Function End Function
Private Shared Sub AddRedundantImports(compilationUnit As CompilationUnitSyntax, Private Shared Sub AddRedundantImports(
compilationUnit As CompilationUnitSyntax,
semanticModel As SemanticModel, semanticModel As SemanticModel,
unnecessaryImports As HashSet(Of ImportsClauseSyntax), unnecessaryImports As HashSet(Of ImportsClauseSyntax),
predicate As Func(Of SyntaxNode, Boolean),
cancellationToken As CancellationToken) cancellationToken As CancellationToken)
' Now that we've visited the tree, add any imports that bound to project level ' Now that we've visited the tree, add any imports that bound to project level
' imports. We definitely can remove them. ' imports. We definitely can remove them.
...@@ -106,18 +114,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports ...@@ -106,18 +114,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports
Dim simpleImportsClause = TryCast(clause, SimpleImportsClauseSyntax) Dim simpleImportsClause = TryCast(clause, SimpleImportsClauseSyntax)
If simpleImportsClause IsNot Nothing Then If simpleImportsClause IsNot Nothing Then
If simpleImportsClause.Alias Is Nothing Then If simpleImportsClause.Alias Is Nothing Then
AddRedundantMemberImportsClause(simpleImportsClause, semanticModel, unnecessaryImports, cancellationToken) AddRedundantMemberImportsClause(simpleImportsClause, semanticModel, unnecessaryImports, predicate, cancellationToken)
Else Else
AddRedundantAliasImportsClause(simpleImportsClause, semanticModel, unnecessaryImports, cancellationToken) AddRedundantAliasImportsClause(simpleImportsClause, semanticModel, unnecessaryImports, predicate, cancellationToken)
End If End If
End If End If
Next Next
Next Next
End Sub End Sub
Private Shared Sub AddRedundantAliasImportsClause(clause As SimpleImportsClauseSyntax, Private Shared Sub AddRedundantAliasImportsClause(
clause As SimpleImportsClauseSyntax,
semanticModel As SemanticModel, semanticModel As SemanticModel,
unnecessaryImports As HashSet(Of ImportsClauseSyntax), unnecessaryImports As HashSet(Of ImportsClauseSyntax),
predicate As Func(Of SyntaxNode, Boolean),
cancellationToken As CancellationToken) cancellationToken As CancellationToken)
Dim semanticInfo = semanticModel.GetSymbolInfo(clause.Name, cancellationToken) Dim semanticInfo = semanticModel.GetSymbolInfo(clause.Name, cancellationToken)
...@@ -129,14 +139,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports ...@@ -129,14 +139,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports
Dim compilation = semanticModel.Compilation Dim compilation = semanticModel.Compilation
Dim aliasSymbol = compilation.AliasImports.FirstOrDefault(Function(a) a.Name = clause.Alias.Identifier.ValueText) Dim aliasSymbol = compilation.AliasImports.FirstOrDefault(Function(a) a.Name = clause.Alias.Identifier.ValueText)
If aliasSymbol IsNot Nothing AndAlso aliasSymbol.Target.Equals(semanticInfo.Symbol) Then If aliasSymbol IsNot Nothing AndAlso
aliasSymbol.Target.Equals(semanticInfo.Symbol) AndAlso
predicate(clause) Then
unnecessaryImports.Add(clause) unnecessaryImports.Add(clause)
End If End If
End Sub End Sub
Private Shared Sub AddRedundantMemberImportsClause(clause As SimpleImportsClauseSyntax, Private Shared Sub AddRedundantMemberImportsClause(
clause As SimpleImportsClauseSyntax,
semanticModel As SemanticModel, semanticModel As SemanticModel,
unnecessaryImports As HashSet(Of ImportsClauseSyntax), unnecessaryImports As HashSet(Of ImportsClauseSyntax),
predicate As Func(Of SyntaxNode, Boolean),
cancellationToken As CancellationToken) cancellationToken As CancellationToken)
Dim semanticInfo = semanticModel.GetSymbolInfo(clause.Name, cancellationToken) Dim semanticInfo = semanticModel.GetSymbolInfo(clause.Name, cancellationToken)
...@@ -147,7 +161,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports ...@@ -147,7 +161,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports
End If End If
Dim compilation = semanticModel.Compilation Dim compilation = semanticModel.Compilation
If compilation.MemberImports.Contains(namespaceOrType) Then If compilation.MemberImports.Contains(namespaceOrType) AndAlso
predicate(clause) Then
unnecessaryImports.Add(clause) unnecessaryImports.Add(clause)
End If End If
End Sub End Sub
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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;
using System.Composition; using System.Composition;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -21,6 +22,10 @@ public XamlRemoveUnnecessaryImportsService(IXamlRemoveUnnecessaryNamespacesServi ...@@ -21,6 +22,10 @@ public XamlRemoveUnnecessaryImportsService(IXamlRemoveUnnecessaryNamespacesServi
} }
public Task<Document> RemoveUnnecessaryImportsAsync(Document document, CancellationToken cancellationToken) public Task<Document> RemoveUnnecessaryImportsAsync(Document document, CancellationToken cancellationToken)
=> RemoveUnnecessaryImportsAsync(document, predicate: null, cancellationToken: cancellationToken);
public Task<Document> RemoveUnnecessaryImportsAsync(
Document document, Func<SyntaxNode, bool> predicate, CancellationToken cancellationToken)
{ {
return _removeService.RemoveUnnecessaryNamespacesAsync(document, cancellationToken) ?? Task.FromResult(document); return _removeService.RemoveUnnecessaryNamespacesAsync(document, cancellationToken) ?? Task.FromResult(document);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册