Skip to content

Commit 9a9156b

Browse files
committed
#336 did a minor update for method discovery as well
1 parent 7b21a1e commit 9a9156b

File tree

4 files changed

+305
-14
lines changed

4 files changed

+305
-14
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727
- Static virtual members on interfaces will not be processed (issue [#337](https://github.com/JasonBock/Rocks/issues/3327))
2828
- Special types (`ArgIterator`, `RuntimeArgumentHandle`, `TypedReference`) used as parameter or return types are now handled (issue [#329](https://github.com/JasonBock/Rocks/issues/329))
2929
- Nested generic types are now handled correctly (issue [#333](https://github.com/JasonBock/Rocks/issues/333))
30+
- Members that hide other members are now handled correctly (issue [#336](https://github.com/JasonBock/Rocks/issues/336))
3031

3132
## [8.3.1] - 2024-09-30
3233

src/Rocks.Tests/Generators/InheritanceGeneratorTests.cs

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,303 @@ namespace Rocks.Tests.Generators;
44

55
public static class InheritanceGeneratorTests
66
{
7+
[Test]
8+
public static async Task GenerateWhenNewMethodIsIntroducedAsync()
9+
{
10+
var code =
11+
"""
12+
#nullable enable
13+
14+
using Rocks;
15+
using System;
16+
17+
[assembly: Rock(typeof(BindableReactiveProperty<>), BuildType.Create | BuildType.Make)]
18+
19+
public class ReactiveProperty<T>
20+
{
21+
public virtual T RetrieveValue() => default!;
22+
}
23+
24+
public class BindableReactiveProperty<T>
25+
: ReactiveProperty<T>
26+
{
27+
public new T RetrieveValue() => default!;
28+
}
29+
""";
30+
31+
var createGeneratedCode =
32+
""""
33+
// <auto-generated/>
34+
35+
#pragma warning disable CS8618
36+
#pragma warning disable CS8633
37+
#pragma warning disable CS8714
38+
#pragma warning disable CS8775
39+
40+
#nullable enable
41+
42+
using Rocks.Extensions;
43+
44+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
45+
internal sealed class BindableReactivePropertyCreateExpectations<T>
46+
: global::Rocks.Expectations
47+
{
48+
internal sealed class Handler0
49+
: global::Rocks.Handler<global::System.Func<object?, bool>, bool>
50+
{
51+
public global::Rocks.Argument<object?> @obj { get; set; }
52+
}
53+
private global::Rocks.Handlers<global::BindableReactivePropertyCreateExpectations<T>.Handler0>? @handlers0;
54+
internal sealed class Handler1
55+
: global::Rocks.Handler<global::System.Func<int>, int>
56+
{ }
57+
private global::Rocks.Handlers<global::BindableReactivePropertyCreateExpectations<T>.Handler1>? @handlers1;
58+
internal sealed class Handler2
59+
: global::Rocks.Handler<global::System.Func<string?>, string?>
60+
{ }
61+
private global::Rocks.Handlers<global::BindableReactivePropertyCreateExpectations<T>.Handler2>? @handlers2;
62+
63+
public override void Verify()
64+
{
65+
if (this.WasInstanceInvoked)
66+
{
67+
var failures = new global::System.Collections.Generic.List<string>();
68+
69+
if (this.handlers0 is not null) { failures.AddRange(this.Verify(this.handlers0, 0)); }
70+
if (this.handlers1 is not null) { failures.AddRange(this.Verify(this.handlers1, 1)); }
71+
if (this.handlers2 is not null) { failures.AddRange(this.Verify(this.handlers2, 2)); }
72+
73+
if (failures.Count > 0)
74+
{
75+
throw new global::Rocks.Exceptions.VerificationException(failures);
76+
}
77+
}
78+
}
79+
80+
private sealed class Mock
81+
: global::BindableReactiveProperty<T>
82+
{
83+
public Mock(global::BindableReactivePropertyCreateExpectations<T> @expectations)
84+
{
85+
this.Expectations = @expectations;
86+
}
87+
88+
[global::Rocks.MemberIdentifier(0)]
89+
public override bool Equals(object? @obj)
90+
{
91+
if (this.Expectations.handlers0 is not null)
92+
{
93+
foreach (var @handler in this.Expectations.handlers0)
94+
{
95+
if (@handler.@obj.IsValid(@obj!))
96+
{
97+
@handler.CallCount++;
98+
var @result = @handler.Callback is not null ?
99+
@handler.Callback(@obj!) : @handler.ReturnValue;
100+
return @result!;
101+
}
102+
}
103+
104+
throw new global::Rocks.Exceptions.ExpectationException(
105+
$"""
106+
No handlers match for {this.GetType().GetMemberDescription(0)}
107+
obj: {@obj.FormatValue()}
108+
""");
109+
}
110+
else
111+
{
112+
return base.Equals(@obj: @obj!);
113+
}
114+
}
115+
116+
[global::Rocks.MemberIdentifier(1)]
117+
public override int GetHashCode()
118+
{
119+
if (this.Expectations.handlers1 is not null)
120+
{
121+
var @handler = this.Expectations.handlers1.First;
122+
@handler.CallCount++;
123+
var @result = @handler.Callback is not null ?
124+
@handler.Callback() : @handler.ReturnValue;
125+
return @result!;
126+
}
127+
else
128+
{
129+
return base.GetHashCode();
130+
}
131+
}
132+
133+
[global::Rocks.MemberIdentifier(2)]
134+
public override string? ToString()
135+
{
136+
if (this.Expectations.handlers2 is not null)
137+
{
138+
var @handler = this.Expectations.handlers2.First;
139+
@handler.CallCount++;
140+
var @result = @handler.Callback is not null ?
141+
@handler.Callback() : @handler.ReturnValue;
142+
return @result!;
143+
}
144+
else
145+
{
146+
return base.ToString();
147+
}
148+
}
149+
150+
private global::BindableReactivePropertyCreateExpectations<T> Expectations { get; }
151+
}
152+
153+
internal sealed class MethodExpectations
154+
{
155+
internal MethodExpectations(global::BindableReactivePropertyCreateExpectations<T> expectations) =>
156+
this.Expectations = expectations;
157+
158+
internal global::BindableReactivePropertyCreateExpectations<T>.Adornments.AdornmentsForHandler0 Equals(global::Rocks.Argument<object?> @obj)
159+
{
160+
global::Rocks.Exceptions.ExpectationException.ThrowIf(this.Expectations.WasInstanceInvoked);
161+
global::System.ArgumentNullException.ThrowIfNull(@obj);
162+
163+
var @handler = new global::BindableReactivePropertyCreateExpectations<T>.Handler0
164+
{
165+
@obj = @obj,
166+
};
167+
168+
if (this.Expectations.handlers0 is null) { this.Expectations.handlers0 = new(@handler); }
169+
else { this.Expectations.handlers0.Add(@handler); }
170+
return new(@handler);
171+
}
172+
173+
internal new global::BindableReactivePropertyCreateExpectations<T>.Adornments.AdornmentsForHandler1 GetHashCode()
174+
{
175+
global::Rocks.Exceptions.ExpectationException.ThrowIf(this.Expectations.WasInstanceInvoked);
176+
var handler = new global::BindableReactivePropertyCreateExpectations<T>.Handler1();
177+
if (this.Expectations.handlers1 is null) { this.Expectations.handlers1 = new(handler); }
178+
else { this.Expectations.handlers1.Add(handler); }
179+
return new(handler);
180+
}
181+
182+
internal new global::BindableReactivePropertyCreateExpectations<T>.Adornments.AdornmentsForHandler2 ToString()
183+
{
184+
global::Rocks.Exceptions.ExpectationException.ThrowIf(this.Expectations.WasInstanceInvoked);
185+
var handler = new global::BindableReactivePropertyCreateExpectations<T>.Handler2();
186+
if (this.Expectations.handlers2 is null) { this.Expectations.handlers2 = new(handler); }
187+
else { this.Expectations.handlers2.Add(handler); }
188+
return new(handler);
189+
}
190+
191+
private global::BindableReactivePropertyCreateExpectations<T> Expectations { get; }
192+
}
193+
194+
internal global::BindableReactivePropertyCreateExpectations<T>.MethodExpectations Methods { get; }
195+
196+
internal BindableReactivePropertyCreateExpectations() =>
197+
(this.Methods) = (new(this));
198+
199+
internal global::BindableReactiveProperty<T> Instance()
200+
{
201+
if (!this.WasInstanceInvoked)
202+
{
203+
this.WasInstanceInvoked = true;
204+
var @mock = new Mock(this);
205+
this.MockType = @mock.GetType();
206+
return @mock;
207+
}
208+
else
209+
{
210+
throw new global::Rocks.Exceptions.NewMockInstanceException("Can only create a new mock once.");
211+
}
212+
}
213+
214+
internal static class Adornments
215+
{
216+
public interface IAdornmentsForBindableReactiveProperty<TAdornments>
217+
: global::Rocks.IAdornments<TAdornments>
218+
where TAdornments : IAdornmentsForBindableReactiveProperty<TAdornments>
219+
{ }
220+
221+
public sealed class AdornmentsForHandler0
222+
: global::Rocks.Adornments<AdornmentsForHandler0, global::BindableReactivePropertyCreateExpectations<T>.Handler0, global::System.Func<object?, bool>, bool>, IAdornmentsForBindableReactiveProperty<AdornmentsForHandler0>
223+
{
224+
public AdornmentsForHandler0(global::BindableReactivePropertyCreateExpectations<T>.Handler0 handler)
225+
: base(handler) { }
226+
}
227+
public sealed class AdornmentsForHandler1
228+
: global::Rocks.Adornments<AdornmentsForHandler1, global::BindableReactivePropertyCreateExpectations<T>.Handler1, global::System.Func<int>, int>, IAdornmentsForBindableReactiveProperty<AdornmentsForHandler1>
229+
{
230+
public AdornmentsForHandler1(global::BindableReactivePropertyCreateExpectations<T>.Handler1 handler)
231+
: base(handler) { }
232+
}
233+
public sealed class AdornmentsForHandler2
234+
: global::Rocks.Adornments<AdornmentsForHandler2, global::BindableReactivePropertyCreateExpectations<T>.Handler2, global::System.Func<string?>, string?>, IAdornmentsForBindableReactiveProperty<AdornmentsForHandler2>
235+
{
236+
public AdornmentsForHandler2(global::BindableReactivePropertyCreateExpectations<T>.Handler2 handler)
237+
: base(handler) { }
238+
}
239+
}
240+
}
241+
242+
#pragma warning restore CS8618
243+
#pragma warning restore CS8633
244+
#pragma warning restore CS8714
245+
#pragma warning restore CS8775
246+
"""";
247+
248+
var makeGeneratedCode =
249+
"""
250+
// <auto-generated/>
251+
252+
#pragma warning disable CS8618
253+
#pragma warning disable CS8633
254+
#pragma warning disable CS8714
255+
#pragma warning disable CS8775
256+
257+
#nullable enable
258+
259+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
260+
internal sealed class BindableReactivePropertyMakeExpectations<T>
261+
{
262+
internal global::BindableReactiveProperty<T> Instance()
263+
{
264+
return new Mock();
265+
}
266+
267+
private sealed class Mock
268+
: global::BindableReactiveProperty<T>
269+
{
270+
public Mock()
271+
{
272+
}
273+
274+
public override bool Equals(object? @obj)
275+
{
276+
return default!;
277+
}
278+
public override int GetHashCode()
279+
{
280+
return default!;
281+
}
282+
public override string? ToString()
283+
{
284+
return default!;
285+
}
286+
}
287+
}
288+
289+
#pragma warning restore CS8618
290+
#pragma warning restore CS8633
291+
#pragma warning restore CS8714
292+
#pragma warning restore CS8775
293+
294+
""";
295+
296+
await TestAssistants.RunGeneratorAsync<RockGenerator>(code,
297+
[
298+
("BindableReactivePropertyT_Rock_Create.g.cs", createGeneratedCode),
299+
("BindableReactivePropertyT_Rock_Make.g.cs", makeGeneratedCode)
300+
],
301+
[]);
302+
}
303+
7304
[Test]
8305
public static async Task GenerateWhenNewPropertyIsIntroducedAsync()
9306
{

src/Rocks/Discovery/MockableMethodDiscovery.cs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ private static MockableMethods GetMethodsForClass(ITypeSymbol mockType, IAssembl
5151

5252
foreach (var hierarchyMethod in hierarchyMethods)
5353
{
54-
if (hierarchyMethod.IsStatic && hierarchyMethod.CanBeSeenByContainingAssembly(containingAssemblyOfInvocationSymbol))
54+
var canBeSeen = hierarchyMethod.CanBeSeenByContainingAssembly(containingAssemblyOfInvocationSymbol);
55+
56+
if (canBeSeen)
5557
{
5658
// This is the case where a class does something like this:
5759
// `protected static new string ToString()`
@@ -67,27 +69,18 @@ private static MockableMethods GetMethodsForClass(ITypeSymbol mockType, IAssembl
6769
methods.Remove(methodToRemove);
6870
}
6971
}
70-
else if (!hierarchyMethod.IsStatic && (!mockType.IsRecord || hierarchyMethod.Name != nameof(Equals)))
72+
73+
if (!hierarchyMethod.IsStatic && (!mockType.IsRecord || hierarchyMethod.Name != nameof(Equals)))
7174
{
7275
if (hierarchyMethod.IsAbstract || hierarchyMethod.IsOverride || hierarchyMethod.IsVirtual)
7376
{
74-
var canBeSeen = hierarchyMethod.CanBeSeenByContainingAssembly(containingAssemblyOfInvocationSymbol);
75-
7677
if (!canBeSeen && hierarchyMethod.IsAbstract)
7778
{
7879
inaccessibleAbstractMembers = true;
7980
}
8081
else if (canBeSeen)
8182
{
82-
var methodToRemove = methods.SingleOrDefault(_ => !(_.Value.Match(hierarchyMethod) == MethodMatch.None));
83-
84-
if (methodToRemove is not null)
85-
{
86-
methods.Remove(methodToRemove);
87-
}
88-
89-
if ((methodToRemove is null || !methodToRemove.Value.ContainingType.Equals(hierarchyMethod.ContainingType)) &&
90-
!hierarchyMethod.IsSealed)
83+
if (!hierarchyMethod.IsSealed)
9184
{
9285
methods.Add(new(hierarchyMethod, mockType, RequiresExplicitInterfaceImplementation.No, RequiresOverride.Yes,
9386
objectMethods.Any(_ => hierarchyMethod.Name == _.Name && hierarchyMethod.Parameters.Length == 0) ?

src/Rocks/Discovery/MockablePropertyDiscovery.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ static bool AreParametersEqual(IPropertySymbol property1, IPropertySymbol proper
2727
var property1Parameter = property1.Parameters[i];
2828
var property2Parameter = property2.Parameters[i];
2929

30-
if (!property1Parameter.Type.Equals(property2Parameter.Type))
30+
if (!SymbolEqualityComparer.Default.Equals(property1Parameter.Type, property2Parameter.Type))
3131
{
3232
return false;
3333
}

0 commit comments

Comments
 (0)