Skip to content

Commit d139d26

Browse files
authored
Fix method check for callback and returns (#429)
## Summary - refine callback/returns detection by checking method name and containing type - add microbenchmark comparing new vs old detection logic <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Tests** - Added benchmarks to compare performance and memory usage of two methods for identifying callback or return symbols. - **Refactor** - Improved detection of callback or return symbols with more precise and reliable checks. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 36fc6f0 commit d139d26

File tree

2 files changed

+149
-5
lines changed

2 files changed

+149
-5
lines changed

src/Common/SemanticModelExtensions.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,20 @@ private static bool IsCallbackOrReturnSymbol(ISymbol? symbol)
120120
return false;
121121
}
122122

123-
string? methodName = methodSymbol.ToString();
124-
125-
if (string.IsNullOrEmpty(methodName))
123+
INamedTypeSymbol? containingType = methodSymbol.ContainingType;
124+
if (containingType is null)
126125
{
127126
return false;
128127
}
129128

130-
return methodName.StartsWith("Moq.Language.ICallback", StringComparison.Ordinal)
131-
|| methodName.StartsWith("Moq.Language.IReturns", StringComparison.Ordinal);
129+
string containingTypeName = containingType.ToDisplayString();
130+
131+
bool isCallback = string.Equals(methodSymbol.Name, "Callback", StringComparison.Ordinal)
132+
&& containingTypeName.StartsWith("Moq.Language.ICallback", StringComparison.Ordinal);
133+
134+
bool isReturns = string.Equals(methodSymbol.Name, "Returns", StringComparison.Ordinal)
135+
&& containingTypeName.StartsWith("Moq.Language.IReturns", StringComparison.Ordinal);
136+
137+
return isCallback || isReturns;
132138
}
133139
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using BenchmarkDotNet.Attributes;
5+
using Microsoft.CodeAnalysis;
6+
using Moq.Analyzers.Benchmarks.Helpers;
7+
8+
namespace Moq.Analyzers.Benchmarks;
9+
10+
[InProcess]
11+
[MemoryDiagnoser]
12+
public class CallbackReturnsSymbolBenchmarks
13+
{
14+
private IMethodSymbol? _callbackMethod;
15+
private IMethodSymbol? _returnsMethod;
16+
17+
[GlobalSetup]
18+
public void Setup()
19+
{
20+
(string Name, string Content)[] sources =
21+
[
22+
("Sample", @"namespace Moq.Language { public interface ICallback { void Callback(); } public interface IReturns { void Returns(); } }")
23+
];
24+
25+
// Suppress VSTHRD002: Synchronously waiting on tasks or awaiters may cause deadlocks.
26+
// This is required for BenchmarkDotNet compatibility and is safe in this context.
27+
#pragma warning disable VSTHRD002
28+
Compilation? compilation = CSharpCompilationCreator.CreateAsync(sources).GetAwaiter().GetResult();
29+
#pragma warning restore VSTHRD002
30+
if (compilation is null)
31+
{
32+
throw new InvalidOperationException("Failed to create C# compilation for benchmark sources.");
33+
}
34+
35+
INamedTypeSymbol? callbackType = compilation.GetTypeByMetadataName("Moq.Language.ICallback");
36+
if (callbackType is null)
37+
{
38+
throw new InvalidOperationException("Type 'Moq.Language.ICallback' not found in compilation.");
39+
}
40+
41+
IMethodSymbol[] callbackMembers = callbackType.GetMembers("Callback").OfType<IMethodSymbol>().ToArray();
42+
if (callbackMembers.Length != 1)
43+
{
44+
throw new InvalidOperationException($"Expected exactly one 'Callback' method in 'Moq.Language.ICallback', found {callbackMembers.Length}.");
45+
}
46+
47+
_callbackMethod = callbackMembers[0];
48+
49+
INamedTypeSymbol? returnsType = compilation.GetTypeByMetadataName("Moq.Language.IReturns");
50+
if (returnsType is null)
51+
{
52+
throw new InvalidOperationException("Type 'Moq.Language.IReturns' not found in compilation.");
53+
}
54+
55+
IMethodSymbol[] returnsMembers = returnsType.GetMembers("Returns").OfType<IMethodSymbol>().ToArray();
56+
if (returnsMembers.Length != 1)
57+
{
58+
throw new InvalidOperationException($"Expected exactly one 'Returns' method in 'Moq.Language.IReturns', found {returnsMembers.Length}.");
59+
}
60+
61+
_returnsMethod = returnsMembers[0];
62+
}
63+
64+
[Benchmark(Baseline = true)]
65+
public bool OldCallbackCheck()
66+
{
67+
if (_callbackMethod is null)
68+
{
69+
throw new InvalidOperationException("_callbackMethod is null. Ensure Setup completed successfully.");
70+
}
71+
72+
return OldIsCallbackOrReturnSymbol(_callbackMethod);
73+
}
74+
75+
[Benchmark]
76+
public bool NewCallbackCheck()
77+
{
78+
if (_callbackMethod is null)
79+
{
80+
throw new InvalidOperationException("_callbackMethod is null. Ensure Setup completed successfully.");
81+
}
82+
83+
return NewIsCallbackOrReturnSymbol(_callbackMethod);
84+
}
85+
86+
[Benchmark]
87+
public bool OldReturnsCheck()
88+
{
89+
if (_returnsMethod is null)
90+
{
91+
throw new InvalidOperationException("_returnsMethod is null. Ensure Setup completed successfully.");
92+
}
93+
94+
return OldIsCallbackOrReturnSymbol(_returnsMethod);
95+
}
96+
97+
[Benchmark]
98+
public bool NewReturnsCheck()
99+
{
100+
if (_returnsMethod is null)
101+
{
102+
throw new InvalidOperationException("_returnsMethod is null. Ensure Setup completed successfully.");
103+
}
104+
105+
return NewIsCallbackOrReturnSymbol(_returnsMethod);
106+
}
107+
108+
private static bool OldIsCallbackOrReturnSymbol(IMethodSymbol symbol)
109+
{
110+
string? methodName = symbol.ToString();
111+
if (string.IsNullOrEmpty(methodName))
112+
{
113+
return false;
114+
}
115+
116+
return methodName.StartsWith("Moq.Language.ICallback", StringComparison.Ordinal)
117+
|| methodName.StartsWith("Moq.Language.IReturns", StringComparison.Ordinal);
118+
}
119+
120+
private static bool NewIsCallbackOrReturnSymbol(IMethodSymbol symbol)
121+
{
122+
INamedTypeSymbol? containingType = symbol.ContainingType;
123+
if (containingType is null)
124+
{
125+
return false;
126+
}
127+
128+
string containingTypeName = containingType.ToDisplayString();
129+
130+
bool isCallback = string.Equals(symbol.Name, "Callback", StringComparison.Ordinal)
131+
&& containingTypeName.StartsWith("Moq.Language.ICallback", StringComparison.Ordinal);
132+
133+
bool isReturns = string.Equals(symbol.Name, "Returns", StringComparison.Ordinal)
134+
&& containingTypeName.StartsWith("Moq.Language.IReturns", StringComparison.Ordinal);
135+
136+
return isCallback || isReturns;
137+
}
138+
}

0 commit comments

Comments
 (0)