diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyStringComparison.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyStringComparison.cs index 036cce07c4..a1fc32df6f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyStringComparison.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyStringComparison.cs @@ -102,7 +102,9 @@ protected override void InitializeWorker(CompilationStartAnalysisContext context // plus as additional StringComparison parameter. Default StringComparison may or may not match user's intent, // but it is recommended to explicitly specify it for clarity and readability: // https://learn.microsoft.com/dotnet/standard/base-types/best-practices-strings#recommendations-for-string-usage - IEnumerable methodsWithSameNameAsTargetMethod = targetMethod.ContainingType.GetMembers(targetMethod.Name).OfType(); + var methodsWithSameNameAsTargetMethod = + GetAccessibleMethodsWithSameNameAsTargetMethod(invocationExpression, targetMethod); + if (methodsWithSameNameAsTargetMethod.HasMoreThan(1)) { var correctOverload = methodsWithSameNameAsTargetMethod @@ -208,5 +210,18 @@ private static ParameterInfo GetParameterInfo(INamedTypeSymbol type, bool isArra { return ParameterInfo.GetParameterInfo(type, isArray, arrayRank, isParams); } + + private static IEnumerable GetAccessibleMethodsWithSameNameAsTargetMethod( + IInvocationOperation invocationExpression, + IMethodSymbol targetMethod) + { + var invocationStart = invocationExpression.Syntax.GetLocation().SourceSpan.Start; + + return targetMethod.ContainingType + .GetMembers(targetMethod.Name) + .OfType() + .Where(method => method.IsStatic == targetMethod.IsStatic && + invocationExpression.SemanticModel!.IsAccessible(invocationStart, method)); + } } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyStringComparisonTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyStringComparisonTests.cs index 976db58946..c240de58a2 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyStringComparisonTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyStringComparisonTests.cs @@ -587,6 +587,254 @@ private void F(Expression> e) {} }"); } + [Fact, WorkItem(6943, "https://github.com/dotnet/roslyn-analyzers/issues/6943")] + public async Task CA1307_StaticMethodWithPrivateOverload_NoDiagnosticAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class G +{ + public static void DoSomething() + { + F.M(""""); + } +} + +public class F +{ + private static void M(string s, StringComparison c) { } + public static void M(string s) { } +}"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class G + Public Shared Sub DoSomething() + F.M("""") + End Sub +End Class + +Public Class F + Private Shared Sub M(s As String, c As StringComparison) + End Sub + + Public Shared Sub M(s As String) + End Sub +End Class"); + } + + [Fact, WorkItem(6943, "https://github.com/dotnet/roslyn-analyzers/issues/6943")] + public async Task CA1307_StaticMethodWithAccessibleInstanceOverload_NoDiagnosticAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class G +{ + public static void DoSomething() + { + F.M(""""); + } +} + +public class F +{ + public void M(string s, StringComparison c) { } + public static void M(string s) { } +}"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class G + Public Shared Sub DoSomething() + F.M("""") + End Sub +End Class + +Public Class F + Public Sub M(s As String, c As StringComparison) + End Sub + + Public Shared Sub M(s As String) + End Sub +End Class"); + } + + [Fact, WorkItem(6943, "https://github.com/dotnet/roslyn-analyzers/issues/6943")] + public async Task CA1307_StaticMethodWithProtectedStaticOverloadOnBaseClass_DiagnosticAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class G : F +{ + public static void DoSomething() + { + F.M(""""); + } +} + +public class F +{ + protected static void M(string s, StringComparison c) { } + public static void M(string s) { } +}", + GetCA1307CSharpResultsAt(8, 9, "F.M(string)", + "G.DoSomething()", + "F.M(string, System.StringComparison)")); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class G + Inherits F + + Public Shared Sub DoSomething() + F.M("""") + End Sub +End Class + +Public Class F + Protected Shared Sub M(s As String, c As StringComparison) + End Sub + + Public Shared Sub M(s As String) + End Sub +End Class", + GetCA1307BasicResultsAt(8, 9, "F.M(String)", + "G.DoSomething()", + "F.M(String, System.StringComparison)")); + } + + [Fact, WorkItem(6943, "https://github.com/dotnet/roslyn-analyzers/issues/6943")] + public async Task CA1307_PrivateOverloadOnBaseClass_NoDiagnosticAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class G : F +{ + public void DoSomething() + { + M(""""); + } +} + +public class F +{ + private void M(string s, StringComparison c) { } + public void M(string s) { } +}"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class G + Inherits F + + Public Sub DoSomething() + M("""") + End Sub +End Class + +Public Class F + Private Sub M(s As String, c As StringComparison) + End Sub + + Public Sub M(s As String) + End Sub +End Class"); + } + + [Fact, WorkItem(6943, "https://github.com/dotnet/roslyn-analyzers/issues/6943")] + public async Task CA1307_ProtectedOverloadOnBaseClass_DiagnosticAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class G : F +{ + public void DoSomething() + { + M(""""); + } +} + +public class F +{ + protected void M(string s, StringComparison c) { } + public void M(string s) { } +}", + GetCA1307CSharpResultsAt(8, 9, "F.M(string)", + "G.DoSomething()", + "F.M(string, System.StringComparison)")); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class G + Inherits F + + Public Sub DoSomething() + M("""") + End Sub +End Class + +Public Class F + Protected Sub M(s As String, c As StringComparison) + End Sub + + Public Sub M(s As String) + End Sub +End Class", + GetCA1307BasicResultsAt(8, 9, "F.M(String)", + "G.DoSomething()", + "F.M(String, System.StringComparison)")); + } + + [Fact, WorkItem(6943, "https://github.com/dotnet/roslyn-analyzers/issues/6943")] + public async Task CA1307_StaticOverloadOnBaseClass_NoDiagnosticAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class G : F +{ + public void DoSomething() + { + M(""""); + } +} + +public class F +{ + public static void M(string s, StringComparison c) { } + public void M(string s) { } +}"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class G + Inherits F + + Public Sub DoSomething() + M("""") + End Sub +End Class + +Public Class F + Public Shared Sub M(s As String, c As StringComparison) + End Sub + + Public Sub M(s As String) + End Sub +End Class"); + } + private static DiagnosticResult GetCA1307CSharpResultsAt(int line, int column, string arg1, string arg2, string arg3) => #pragma warning disable RS0030 // Do not use banned APIs VerifyCS.Diagnostic(SpecifyStringComparisonAnalyzer.Rule_CA1307)