From 09996ecd7812e5bd9251442502a89b132daa3ae9 Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Wed, 6 Mar 2019 11:43:59 -0800 Subject: [PATCH 1/6] Handle extension methods for sync namespace refactoring --- .../AbstractChangeNamespaceService.cs | 83 +++++++++++++++---- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs index 8c504d98fa5f..fb95c5886357 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs @@ -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(); - var refLocationsInOtherDocuments = new List(); + var refLocationsInCurrentDocument = new List(); + var refLocationsInOtherDocuments = new List(); var refLocations = await Task.WhenAll( declaredSymbols.Select(declaredSymbol @@ -434,13 +434,29 @@ private static async Task MergeDocumentChangesAsync(Solution originalS return originalSolution; } - private static async Task> FindReferenceLocationsForSymbol( + private 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> FindReferenceLocationsForSymbol( Document document, ISymbol symbol, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - var progress = new StreamingProgressCollector(StreamingFindReferencesProgress.Instance); + var builder = ArrayBuilder.GetInstance(); + var progress = new StreamingProgressCollector(StreamingFindReferencesProgress.Instance); await SymbolFinder.FindReferencesAsync( symbolAndProjectId: SymbolAndProjectId.Create(symbol, document.Project.Id), solution: document.Project.Solution, @@ -450,13 +466,47 @@ await SymbolFinder.FindReferencesAsync( cancellationToken: cancellationToken).ConfigureAwait(false); var referencedSymbols = progress.GetReferencedSymbols(); - return referencedSymbols.Where(refSymbol => refSymbol.Definition.Equals(symbol)) - .SelectMany(refSymbol => refSymbol.Locations).ToImmutableArray(); + builder.AddRange(referencedSymbols + .Where(refSymbol => refSymbol.Definition.Equals(symbol)) + .SelectMany(refSymbol => refSymbol.Locations) + .Select(location => new LocationForAffectedSymbol(location, false))); + + // Find references above doesn't handle invocation of extension methods (in reduced form) + // that are declared in the affected types, so we need to find references to extension methods + // as well. 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 could be covered by the type references found above. + if (symbol is INamedTypeSymbol typeSymbol && typeSymbol.MightContainExtensionMethods) + { + foreach (var memberSymbol in typeSymbol.GetMembers()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (memberSymbol is IMethodSymbol methodSymbol && methodSymbol.IsExtensionMethod) + { + var methodProgress = new StreamingProgressCollector(StreamingFindReferencesProgress.Instance); + await SymbolFinder.FindReferencesAsync( + symbolAndProjectId: SymbolAndProjectId.Create(methodSymbol, document.Project.Id), + solution: document.Project.Solution, + documents: null, + progress: methodProgress, + options: FindReferencesSearchOptions.Default, + cancellationToken: cancellationToken).ConfigureAwait(false); + + var referencedMethodSymbols = methodProgress.GetReferencedSymbols(); + builder.AddRange(referencedMethodSymbols + .SelectMany(refSymbol => refSymbol.Locations) + .Select(location => new LocationForAffectedSymbol(location, true))); + } + } + } + + return builder.ToImmutableAndFree(); } private async Task FixDeclarationDocumentAsync( Document document, - IReadOnlyList refLocations, + IReadOnlyList refLocations, string oldNamespace, string newNamespace, CancellationToken cancellationToken) @@ -529,7 +579,7 @@ private async Task FixDeclarationDocumentAsync( private async Task FixReferencingDocumentAsync( Document document, - IEnumerable refLocations, + IEnumerable refLocations, string newNamespace, CancellationToken cancellationToken) { @@ -576,7 +626,7 @@ await FixReferencesAsync(document, changeNamespaceService, addImportService, ref Document document, IChangeNamespaceService changeNamespaceService, IAddImportsService addImportService, - IEnumerable refLocations, + IEnumerable refLocations, ImmutableArray 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. From 8309eb44735ee1d3320317e0d4b9196e849b2677 Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Wed, 6 Mar 2019 11:44:07 -0800 Subject: [PATCH 2/6] Add tests --- .../SyncNamespaceTests_ChangeNamespace.cs | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs index 60d0612b92ea..6011c1115e78 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs @@ -1839,6 +1839,177 @@ namespace Foo class RefClass { } +}"; + await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsSyncNamespace)] + public async Task ChangeNamespace_ExternsionMethodInReducedForm() + { + var defaultNamespace = "A"; + + var documentPath1 = CreateDocumentFilePath(new[] { "B", "C" }, "File1.cs"); + var documentPath2 = CreateDocumentFilePath(Array.Empty(), "File2.cs"); + var code = +$@" + + + +namespace [||]{defaultNamespace} +{{ + public static class Extensions + {{ + public static bool Foo(this Class1 c1) => true; + }} +}} + +namespace {defaultNamespace} +{{ + using System; + + public class Class1 + {{ + public bool Bar(Class1 c1) => c1.Foo(); + }} +}} + +"; + + 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); + } + + [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(), "File2.cs"); + var code = +$@" + + + +namespace [||]A +{{ + public static class Extensions + {{ + public static bool Foo(this Class1 c1) => true; + }} +}} + +using System; + +namespace A +{{ + public class Class1 + {{ + public bool Bar(Class1 c1) => Extensions.Foo(c1); + }} +}} + +"; + + 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); + } + + [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(), "File2.cs"); + var code = +$@" + + + +namespace [||]A +{{ + public static class Extensions + {{ + public static bool Foo(this Class1 c1) => true; + }} + + public class Class2 + {{ }} +}} + +using System; + +namespace A +{{ + public class Class1 + {{ + public bool Bar(Class1 c1, Class2 c2) => c2 == null ? c1.Foo() : true; + }} +}} + +"; + + 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); } From 0f9991f77c0f3584d7ec7826eaa8feea793d371b Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Wed, 6 Mar 2019 14:42:35 -0800 Subject: [PATCH 3/6] Address review comments --- .../SyncNamespaceTests_ChangeNamespace.cs | 61 +++++++++++++ .../AbstractChangeNamespaceService.cs | 85 ++++++++++--------- 2 files changed, 104 insertions(+), 42 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs index 6011c1115e78..203ef8b2fc92 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs @@ -1843,6 +1843,7 @@ 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_ExternsionMethodInReducedForm() { @@ -1898,6 +1899,7 @@ public class Class1 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() { @@ -1953,6 +1955,7 @@ public class Class1 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() { @@ -2013,5 +2016,63 @@ public class Class1 }"; 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(), "File1.cs"); + var code = +$@" + + + +using System; + +namespace [||]{declaredNamespace} +{{ + public static class Extensions + {{ + public static bool Foo(this String s) => true; + }} +}} + + + +Imports {declaredNamespace} + +Public Class VBClass + Public Function Foo(s As string) As Boolean + Return s.Foo() + End Function +End Class + +"; + + 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); + } } } diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs index fb95c5886357..ec3567f86efb 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs @@ -434,7 +434,7 @@ private static async Task MergeDocumentChangesAsync(Solution originalS return originalSolution; } - private struct LocationForAffectedSymbol + private readonly struct LocationForAffectedSymbol { public LocationForAffectedSymbol(ReferenceLocation location, bool isReferenceToExtensionMethod) { @@ -452,56 +452,57 @@ public LocationForAffectedSymbol(ReferenceLocation location, bool isReferenceToE private static async Task> FindReferenceLocationsForSymbol( Document document, ISymbol symbol, CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); var builder = ArrayBuilder.GetInstance(); - var progress = new StreamingProgressCollector(StreamingFindReferencesProgress.Instance); - await SymbolFinder.FindReferencesAsync( - symbolAndProjectId: SymbolAndProjectId.Create(symbol, document.Project.Id), - solution: document.Project.Solution, - documents: null, - progress: progress, - options: FindReferencesSearchOptions.Default, - cancellationToken: cancellationToken).ConfigureAwait(false); - - var referencedSymbols = progress.GetReferencedSymbols(); - builder.AddRange(referencedSymbols - .Where(refSymbol => refSymbol.Definition.Equals(symbol)) - .SelectMany(refSymbol => refSymbol.Locations) - .Select(location => new LocationForAffectedSymbol(location, false))); - - // Find references above doesn't handle invocation of extension methods (in reduced form) - // that are declared in the affected types, so we need to find references to extension methods - // as well. 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 could be covered by the type references found above. - if (symbol is INamedTypeSymbol typeSymbol && typeSymbol.MightContainExtensionMethods) + try { - foreach (var memberSymbol in typeSymbol.GetMembers()) + 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))); + + // Find references above doesn't handle invocation of extension methods (in reduced form) + // that are declared in the affected types, so we need to find references to extension methods + // as well. 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 could be covered by the type references found above. + if (symbol is INamedTypeSymbol typeSymbol && typeSymbol.MightContainExtensionMethods) { - cancellationToken.ThrowIfCancellationRequested(); - - if (memberSymbol is IMethodSymbol methodSymbol && methodSymbol.IsExtensionMethod) + foreach (var methodSymbol in typeSymbol.GetMembers().OfType()) { - var methodProgress = new StreamingProgressCollector(StreamingFindReferencesProgress.Instance); - await SymbolFinder.FindReferencesAsync( - symbolAndProjectId: SymbolAndProjectId.Create(methodSymbol, document.Project.Id), - solution: document.Project.Solution, - documents: null, - progress: methodProgress, - options: FindReferencesSearchOptions.Default, - cancellationToken: cancellationToken).ConfigureAwait(false); - - var referencedMethodSymbols = methodProgress.GetReferencedSymbols(); - builder.AddRange(referencedMethodSymbols - .SelectMany(refSymbol => refSymbol.Locations) - .Select(location => new LocationForAffectedSymbol(location, true))); + 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))); + } } } + + return builder.ToImmutable(); + } + finally + { + builder.Free(); } - return builder.ToImmutableAndFree(); + async Task> FindReferencesAsync(ISymbol s, Document d, CancellationToken c) + { + cancellationToken.ThrowIfCancellationRequested(); + var progress = new StreamingProgressCollector(StreamingFindReferencesProgress.Instance); + await SymbolFinder.FindReferencesAsync( + symbolAndProjectId: SymbolAndProjectId.Create(s, d.Project.Id), + solution: d.Project.Solution, + documents: null, + progress: progress, + options: FindReferencesSearchOptions.Default, + cancellationToken: c).ConfigureAwait(false); + + return progress.GetReferencedSymbols(); + } } private async Task FixDeclarationDocumentAsync( @@ -666,7 +667,7 @@ await FixReferencesAsync(document, changeNamespaceService, addImportService, ref var refNode = root.FindNode(refLoc.ReferenceLocation.Location.SourceSpan, findInsideTrivia: true, getInnermostNodeForTie: true); - //For invocation of extension method, we only need to add missing import. + // For invocation of extension method, we only need to add missing import. if (!refLoc.IsReferenceToExtensionMethod) { if (abstractChangeNamespaceService.TryGetReplacementReferenceSyntax( From bf917106152dbe60cadeba5839c41075b0b713c0 Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Wed, 6 Mar 2019 15:21:40 -0800 Subject: [PATCH 4/6] Address review comments --- .../AbstractChangeNamespaceService.cs | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs index ec3567f86efb..ddbadd7e64c4 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs @@ -452,9 +452,7 @@ public LocationForAffectedSymbol(ReferenceLocation location, bool isReferenceToE private static async Task> FindReferenceLocationsForSymbol( Document document, ISymbol symbol, CancellationToken cancellationToken) { - var builder = ArrayBuilder.GetInstance(); - try { var referencedSymbols = await FindReferencesAsync(symbol, document, cancellationToken).ConfigureAwait(false); @@ -488,21 +486,21 @@ private static async Task> FindReferen { builder.Free(); } + } - async Task> FindReferencesAsync(ISymbol s, Document d, CancellationToken c) - { - cancellationToken.ThrowIfCancellationRequested(); - var progress = new StreamingProgressCollector(StreamingFindReferencesProgress.Instance); - await SymbolFinder.FindReferencesAsync( - symbolAndProjectId: SymbolAndProjectId.Create(s, d.Project.Id), - solution: d.Project.Solution, - documents: null, - progress: progress, - options: FindReferencesSearchOptions.Default, - cancellationToken: c).ConfigureAwait(false); - - return progress.GetReferencedSymbols(); - } + private static async Task> 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, + documents: null, + progress: progress, + options: FindReferencesSearchOptions.Default, + cancellationToken: cancellationToken).ConfigureAwait(false); + + return progress.GetReferencedSymbols(); } private async Task FixDeclarationDocumentAsync( From b242f3c33558958f18ecc6bd34578cf63e397a8b Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Thu, 7 Mar 2019 13:41:09 -0800 Subject: [PATCH 5/6] Fix comments --- .../SyncNamespace/AbstractChangeNamespaceService.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs index ddbadd7e64c4..dcef77978a0e 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs @@ -461,11 +461,12 @@ private static async Task> FindReferen .SelectMany(refSymbol => refSymbol.Locations) .Select(location => new LocationForAffectedSymbol(location, isReferenceToExtensionMethod: false))); - // Find references above doesn't handle invocation of extension methods (in reduced form) - // that are declared in the affected types, so we need to find references to extension methods - // as well. This will returns all the references, not just in the reduced form. But we will + // 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 could be covered by the type references found above. + // they are already covered by the type references found above. if (symbol is INamedTypeSymbol typeSymbol && typeSymbol.MightContainExtensionMethods) { foreach (var methodSymbol in typeSymbol.GetMembers().OfType()) From c03b88306d6688e732ba708d2fc4f27de4d6c9b0 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Fri, 8 Mar 2019 10:30:37 -0800 Subject: [PATCH 6/6] Fix type Co-Authored-By: genlu --- .../SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs index 203ef8b2fc92..3323bab7fca0 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs @@ -1845,7 +1845,7 @@ class RefClass [WorkItem(33890, "https://github.com/dotnet/roslyn/issues/33890")] [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsSyncNamespace)] - public async Task ChangeNamespace_ExternsionMethodInReducedForm() + public async Task ChangeNamespace_ExtensionMethodInReducedForm() { var defaultNamespace = "A";