diff --git a/src/EditorFeatures/CSharpTest/RemoveUnusedMembers/RemoveUnusedMembersTests.cs b/src/EditorFeatures/CSharpTest/RemoveUnusedMembers/RemoveUnusedMembersTests.cs index c4ffd2be83217565e777632970921960ded23e16..2ca4ce88c2e57353b7057dd93ac51382e4320b1f 100644 --- a/src/EditorFeatures/CSharpTest/RemoveUnusedMembers/RemoveUnusedMembersTests.cs +++ b/src/EditorFeatures/CSharpTest/RemoveUnusedMembers/RemoveUnusedMembersTests.cs @@ -2349,5 +2349,17 @@ public class MyClass }", new TestParameters(retainNonFixableDiagnostics: true, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8)), expected: Diagnostic("IDE0052")); } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedMembers)] + [WorkItem(37213, "https://github.com/dotnet/roslyn/issues/37213")] + public async Task UsedPrivateExtensionMethod() + { + await TestDiagnosticMissingAsync( +@"public static class B +{ + public static void PublicExtensionMethod(this string s) => s.PrivateExtensionMethod(); + private static void [|PrivateExtensionMethod|](this string s) { } +}"); + } } } diff --git a/src/EditorFeatures/VisualBasicTest/RemoveUnusedMembers/RemoveUnusedMembersTests.vb b/src/EditorFeatures/VisualBasicTest/RemoveUnusedMembers/RemoveUnusedMembersTests.vb index ac5d07a279f7ee76a6fa2ab7943a637e0cd36139..200686ce8fb85f2794266affd753a1ea663e3a19 100644 --- a/src/EditorFeatures/VisualBasicTest/RemoveUnusedMembers/RemoveUnusedMembersTests.vb +++ b/src/EditorFeatures/VisualBasicTest/RemoveUnusedMembers/RemoveUnusedMembersTests.vb @@ -1560,5 +1560,23 @@ End Class") End Sub End Class") End Function + + + + Public Async Function UsedPrivateExtensionMethod() As Task + Await TestMissingInRegularAndScriptAsync( +"Imports System.Runtime.CompilerServices + +Public Module B + + Sub PublicExtensionMethod(s As String) + s.PrivateExtensionMethod() + End Sub + + + Private Sub [|PrivateExtensionMethod|](s As String) + End Sub +End Module") + End Function End Class End Namespace diff --git a/src/Features/Core/Portable/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs b/src/Features/Core/Portable/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs index 88e36f9fa2600459d4f5ad9d15be148d8bef26de..2fe14e4acec38d67a25c05b2b66b8cae202eb5af 100644 --- a/src/Features/Core/Portable/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs @@ -288,6 +288,13 @@ private void AnalyzeInvocationOperation(OperationAnalysisContext operationContex // A method invocation is considered as a read reference to the symbol // to ensure that we consider the method as "used". OnSymbolUsage(targetMethod, ValueUsageInfo.Read); + + // If the invoked method is a reduced extension method, also mark the original + // method from which it was reduced as "used". + if (targetMethod.ReducedFrom != null) + { + OnSymbolUsage(targetMethod.ReducedFrom, ValueUsageInfo.Read); + } } private void AnalyzeNameOfOperation(OperationAnalysisContext operationContext)