未验证 提交 f5429f49 编写于 作者: D dotnet-automerge-bot 提交者: GitHub

Merge pull request #34032 from dotnet/merges/dev16.0-to-master

Merge dev16.0 to master
......@@ -1842,5 +1842,237 @@ class RefClass
}";
await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference);
}
[WorkItem(33890, "https://github.com/dotnet/roslyn/issues/33890")]
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsSyncNamespace)]
public async Task ChangeNamespace_ExtensionMethodInReducedForm()
{
var defaultNamespace = "A";
var documentPath1 = CreateDocumentFilePath(new[] { "B", "C" }, "File1.cs");
var documentPath2 = CreateDocumentFilePath(Array.Empty<string>(), "File2.cs");
var code =
$@"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" FilePath=""{ProjectFilePath}"" RootNamespace=""{defaultNamespace}"" CommonReferences=""true"">
<Document Folders=""{documentPath1.folder}"" FilePath=""{documentPath1.filePath}"">
namespace [||]{defaultNamespace}
{{
public static class Extensions
{{
public static bool Foo(this Class1 c1) => true;
}}
}}</Document>
<Document Folders=""{documentPath2.folder}"" FilePath=""{documentPath2.filePath}"">
namespace {defaultNamespace}
{{
using System;
public class Class1
{{
public bool Bar(Class1 c1) => c1.Foo();
}}
}}</Document>
</Project>
</Workspace>";
var expectedSourceOriginal =
$@"namespace A.B.C
{{
public static class Extensions
{{
public static bool Foo(this Class1 c1) => true;
}}
}}";
var expectedSourceReference =
$@"
namespace {defaultNamespace}
{{
using System;
using A.B.C;
public class Class1
{{
public bool Bar(Class1 c1) => c1.Foo();
}}
}}";
await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference);
}
[WorkItem(33890, "https://github.com/dotnet/roslyn/issues/33890")]
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsSyncNamespace)]
public async Task ChangeNamespace_ExternsionMethodInRegularForm()
{
var defaultNamespace = "A";
var documentPath1 = CreateDocumentFilePath(new[] { "B", "C" }, "File1.cs");
var documentPath2 = CreateDocumentFilePath(Array.Empty<string>(), "File2.cs");
var code =
$@"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" FilePath=""{ProjectFilePath}"" RootNamespace=""{defaultNamespace}"" CommonReferences=""true"">
<Document Folders=""{documentPath1.folder}"" FilePath=""{documentPath1.filePath}"">
namespace [||]A
{{
public static class Extensions
{{
public static bool Foo(this Class1 c1) => true;
}}
}}</Document>
<Document Folders=""{documentPath2.folder}"" FilePath=""{documentPath2.filePath}"">
using System;
namespace A
{{
public class Class1
{{
public bool Bar(Class1 c1) => Extensions.Foo(c1);
}}
}}</Document>
</Project>
</Workspace>";
var expectedSourceOriginal =
$@"namespace A.B.C
{{
public static class Extensions
{{
public static bool Foo(this Class1 c1) => true;
}}
}}";
var expectedSourceReference =
$@"
using System;
using A.B.C;
namespace A
{{
public class Class1
{{
public bool Bar(Class1 c1) => Extensions.Foo(c1);
}}
}}";
await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference);
}
[WorkItem(33890, "https://github.com/dotnet/roslyn/issues/33890")]
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsSyncNamespace)]
public async Task ChangeNamespace_ContainsBothTypeAndExternsionMethod()
{
var defaultNamespace = "A";
var documentPath1 = CreateDocumentFilePath(new[] { "B", "C" }, "File1.cs");
var documentPath2 = CreateDocumentFilePath(Array.Empty<string>(), "File2.cs");
var code =
$@"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" FilePath=""{ProjectFilePath}"" RootNamespace=""{defaultNamespace}"" CommonReferences=""true"">
<Document Folders=""{documentPath1.folder}"" FilePath=""{documentPath1.filePath}"">
namespace [||]A
{{
public static class Extensions
{{
public static bool Foo(this Class1 c1) => true;
}}
public class Class2
{{ }}
}}</Document>
<Document Folders=""{documentPath2.folder}"" FilePath=""{documentPath2.filePath}"">
using System;
namespace A
{{
public class Class1
{{
public bool Bar(Class1 c1, Class2 c2) => c2 == null ? c1.Foo() : true;
}}
}}</Document>
</Project>
</Workspace>";
var expectedSourceOriginal =
@"namespace A.B.C
{
public static class Extensions
{
public static bool Foo(this Class1 c1) => true;
}
public class Class2
{ }
}";
var expectedSourceReference =
@"
using System;
using A.B.C;
namespace A
{
public class Class1
{
public bool Bar(Class1 c1, Class2 c2) => c2 == null ? c1.Foo() : true;
}
}";
await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference);
}
[WorkItem(33890, "https://github.com/dotnet/roslyn/issues/33890")]
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsSyncNamespace)]
public async Task ChangeNamespace_WithExtensionMethodReferencesInVBDocument()
{
var defaultNamespace = "A.B.C";
var declaredNamespace = "A.B.C.D";
var documentPath1 = CreateDocumentFilePath(Array.Empty<string>(), "File1.cs");
var code =
$@"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" FilePath=""{ProjectFilePath}"" RootNamespace=""{defaultNamespace}"" CommonReferences=""true"">
<Document Folders=""{documentPath1.folder}"" FilePath=""{documentPath1.filePath}"">
using System;
namespace [||]{declaredNamespace}
{{
public static class Extensions
{{
public static bool Foo(this String s) => true;
}}
}}</Document>
</Project>
<Project Language=""Visual Basic"" AssemblyName=""Assembly2"" CommonReferences=""true"">
<Document>
Imports {declaredNamespace}
Public Class VBClass
Public Function Foo(s As string) As Boolean
Return s.Foo()
End Function
End Class</Document>
</Project>
</Workspace>";
var expectedSourceOriginal =
$@"
using System;
namespace {defaultNamespace}
{{
public static class Extensions
{{
public static bool Foo(this string s) => true;
}}
}}";
var expectedSourceReference =
$@"
Imports {defaultNamespace}
Public Class VBClass
Public Function Foo(s As string) As Boolean
Return s.Foo()
End Function
End Class";
await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference);
}
}
}
......@@ -383,8 +383,8 @@ private static SyntaxNode CreateImport(SyntaxGenerator syntaxGenerator, string n
// Separating references to declaredSymbols into two groups based on wheter it's located in the same
// document as the namespace declaration. This is because code change required for them are different.
var refLocationsInCurrentDocument = new List<ReferenceLocation>();
var refLocationsInOtherDocuments = new List<ReferenceLocation>();
var refLocationsInCurrentDocument = new List<LocationForAffectedSymbol>();
var refLocationsInOtherDocuments = new List<LocationForAffectedSymbol>();
var refLocations = await Task.WhenAll(
declaredSymbols.Select(declaredSymbol
......@@ -434,13 +434,65 @@ private static async Task<Solution> MergeDocumentChangesAsync(Solution originalS
return originalSolution;
}
private static async Task<ImmutableArray<ReferenceLocation>> FindReferenceLocationsForSymbol(
private readonly struct LocationForAffectedSymbol
{
public LocationForAffectedSymbol(ReferenceLocation location, bool isReferenceToExtensionMethod)
{
ReferenceLocation = location;
IsReferenceToExtensionMethod = isReferenceToExtensionMethod;
}
public ReferenceLocation ReferenceLocation { get; }
public bool IsReferenceToExtensionMethod { get; }
public Document Document => ReferenceLocation.Document;
}
private static async Task<ImmutableArray<LocationForAffectedSymbol>> FindReferenceLocationsForSymbol(
Document document, ISymbol symbol, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var builder = ArrayBuilder<LocationForAffectedSymbol>.GetInstance();
try
{
var referencedSymbols = await FindReferencesAsync(symbol, document, cancellationToken).ConfigureAwait(false);
builder.AddRange(referencedSymbols
.Where(refSymbol => refSymbol.Definition.Equals(symbol))
.SelectMany(refSymbol => refSymbol.Locations)
.Select(location => new LocationForAffectedSymbol(location, isReferenceToExtensionMethod: false)));
// So far we only have references to types declared in affected namespace. We also need to
// handle invocation of extension methods (in reduced form) that are declared in those types.
// Therefore additional calls to find references are needed for those extension methods.
// This will returns all the references, not just in the reduced form. But we will
// not further distinguish the usage. In the worst case, those references are redundant because
// they are already covered by the type references found above.
if (symbol is INamedTypeSymbol typeSymbol && typeSymbol.MightContainExtensionMethods)
{
foreach (var methodSymbol in typeSymbol.GetMembers().OfType<IMethodSymbol>())
{
if (methodSymbol.IsExtensionMethod)
{
var referencedMethodSymbols = await FindReferencesAsync(methodSymbol, document, cancellationToken).ConfigureAwait(false);
builder.AddRange(referencedMethodSymbols
.SelectMany(refSymbol => refSymbol.Locations)
.Select(location => new LocationForAffectedSymbol(location, isReferenceToExtensionMethod: true)));
}
}
}
var progress = new StreamingProgressCollector(StreamingFindReferencesProgress.Instance);
return builder.ToImmutable();
}
finally
{
builder.Free();
}
}
private static async Task<ImmutableArray<ReferencedSymbol>> FindReferencesAsync(ISymbol symbol, Document document, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var progress = new StreamingProgressCollector(StreamingFindReferencesProgress.Instance);
await SymbolFinder.FindReferencesAsync(
symbolAndProjectId: SymbolAndProjectId.Create(symbol, document.Project.Id),
solution: document.Project.Solution,
......@@ -449,14 +501,12 @@ private static async Task<Solution> MergeDocumentChangesAsync(Solution originalS
options: FindReferencesSearchOptions.Default,
cancellationToken: cancellationToken).ConfigureAwait(false);
var referencedSymbols = progress.GetReferencedSymbols();
return referencedSymbols.Where(refSymbol => refSymbol.Definition.Equals(symbol))
.SelectMany(refSymbol => refSymbol.Locations).ToImmutableArray();
return progress.GetReferencedSymbols();
}
private async Task<Document> FixDeclarationDocumentAsync(
Document document,
IReadOnlyList<ReferenceLocation> refLocations,
IReadOnlyList<LocationForAffectedSymbol> refLocations,
string oldNamespace,
string newNamespace,
CancellationToken cancellationToken)
......@@ -529,7 +579,7 @@ private static async Task<Solution> MergeDocumentChangesAsync(Solution originalS
private async Task<Document> FixReferencingDocumentAsync(
Document document,
IEnumerable<ReferenceLocation> refLocations,
IEnumerable<LocationForAffectedSymbol> refLocations,
string newNamespace,
CancellationToken cancellationToken)
{
......@@ -576,7 +626,7 @@ await FixReferencesAsync(document, changeNamespaceService, addImportService, ref
Document document,
IChangeNamespaceService changeNamespaceService,
IAddImportsService addImportService,
IEnumerable<ReferenceLocation> refLocations,
IEnumerable<LocationForAffectedSymbol> refLocations,
ImmutableArray<string> newNamespaceParts,
CancellationToken cancellationToken)
{
......@@ -598,7 +648,7 @@ await FixReferencesAsync(document, changeNamespaceService, addImportService, ref
// Ignore references via alias. For simple cases where the alias is defined as the type we are interested,
// 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.Alias != null)
if (refLoc.ReferenceLocation.Alias != null)
{
continue;
}
......@@ -614,11 +664,16 @@ await FixReferencesAsync(document, changeNamespaceService, addImportService, ref
// For the reference to Foo where it is used as a base class, the BaseTypeSyntax and the TypeSyntax
// have exact same span.
var refNode = root.FindNode(refLoc.Location.SourceSpan, findInsideTrivia: true, getInnermostNodeForTie: true);
if (abstractChangeNamespaceService.TryGetReplacementReferenceSyntax(
refNode, newNamespaceParts, syntaxFacts, out var oldNode, out var newNode))
var refNode = root.FindNode(refLoc.ReferenceLocation.Location.SourceSpan, findInsideTrivia: true, getInnermostNodeForTie: true);
// For invocation of extension method, we only need to add missing import.
if (!refLoc.IsReferenceToExtensionMethod)
{
editor.ReplaceNode(oldNode, newNode.WithAdditionalAnnotations(Simplifier.Annotation));
if (abstractChangeNamespaceService.TryGetReplacementReferenceSyntax(
refNode, newNamespaceParts, syntaxFacts, out var oldNode, out var newNode))
{
editor.ReplaceNode(oldNode, newNode.WithAdditionalAnnotations(Simplifier.Annotation));
}
}
// Use a dummy import node to figure out which container the new import will be added to.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册