Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CA1307: Only consider equal or more accessible overloads #6998

Merged
merged 8 commits into from
Dec 1, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -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<IMethodSymbol> methodsWithSameNameAsTargetMethod = targetMethod.ContainingType.GetMembers(targetMethod.Name).OfType<IMethodSymbol>();
var methodsWithSameNameAsTargetMethod =
GetAccessibleMethodsWithSameNameAsTargetMethod(invocationExpression, targetMethod);

if (methodsWithSameNameAsTargetMethod.HasMoreThan(1))
{
var correctOverload = methodsWithSameNameAsTargetMethod
Expand Down Expand Up @@ -208,5 +210,35 @@ private static ParameterInfo GetParameterInfo(INamedTypeSymbol type, bool isArra
{
return ParameterInfo.GetParameterInfo(type, isArray, arrayRank, isParams);
}

private static IEnumerable<IMethodSymbol> GetAccessibleMethodsWithSameNameAsTargetMethod(
IInvocationOperation invocationExpression,
IMethodSymbol targetMethod)
{
var invocationStart = invocationExpression.Syntax.GetLocation().SourceSpan.Start;

var methodsWithSameNameAsTargetMethod =
invocationExpression.SemanticModel?
.LookupSymbols(
mavasani marked this conversation as resolved.
Show resolved Hide resolved
invocationStart,
targetMethod.ContainingType,
targetMethod.Name)
.OfType<IMethodSymbol>()
.Where(method => method.IsStatic == targetMethod.IsStatic) ?? Enumerable.Empty<IMethodSymbol>();

var immutableHashSetBuilder = ImmutableHashSet.CreateBuilder<IMethodSymbol>(SymbolEqualityComparer.Default);
immutableHashSetBuilder.AddRange(methodsWithSameNameAsTargetMethod);

var protectedMethodsWithSameNameAsTargetMethod =
targetMethod.ContainingType
.GetMembers(targetMethod.Name)
.OfType<IMethodSymbol>()
.Where(method => method.DeclaredAccessibility == Accessibility.Protected &&
method.IsStatic == targetMethod.IsStatic);

immutableHashSetBuilder.AddRange(protectedMethodsWithSameNameAsTargetMethod);

return immutableHashSetBuilder.ToImmutableHashSet();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,254 @@ private void F(Expression<Func<C, bool>> 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)
Expand Down