Skip to content

Commit 7e88596

Browse files
committed
Fix general member equality in ApiCompat mappers
1 parent c5acf25 commit 7e88596

File tree

2 files changed

+120
-92
lines changed

2 files changed

+120
-92
lines changed

src/build/ArApiCompat/ArApiCompat.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="AsmResolver.DotNet" Version="6.0.0-development.271" />
13+
<PackageReference Include="AsmResolver.DotNet" Version="6.0.0-development.278" />
1414
</ItemGroup>
1515

1616
</Project>
Lines changed: 119 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,119 @@
1-
using System.Diagnostics.CodeAnalysis;
2-
using AsmResolver.DotNet;
3-
using AsmResolver.DotNet.Signatures;
4-
5-
namespace ArApiCompat.Utilities.AsmResolver;
6-
7-
// TODO upstream?
8-
9-
/// <summary>
10-
/// A <see cref="SignatureComparer"/> with added support for <see cref="IMemberDescriptor"/>, <see cref="PropertyDefinition"/> and <see cref="EventDefinition"/>.
11-
/// </summary>
12-
[SuppressMessage("Design", "CA1061:Do not hide base class methods", Justification = "Hidden base class methods eventually get called regardless.")]
13-
internal sealed class ExtendedSignatureComparer : SignatureComparer,
14-
IEqualityComparer<IMemberDescriptor>,
15-
IEqualityComparer<PropertyDefinition>,
16-
IEqualityComparer<EventDefinition>
17-
{
18-
public ExtendedSignatureComparer()
19-
{
20-
}
21-
22-
public ExtendedSignatureComparer(SignatureComparisonFlags flags) : base(flags)
23-
{
24-
}
25-
26-
public static new ExtendedSignatureComparer Default { get; } = new();
27-
public static ExtendedSignatureComparer VersionAgnostic { get; } = new(SignatureComparisonFlags.VersionAgnostic);
28-
29-
public bool Equals(IMemberDescriptor? x, IMemberDescriptor? y)
30-
{
31-
if (ReferenceEquals(x, y)) return true;
32-
if (x == null || y == null) return false;
33-
34-
return x switch
35-
{
36-
ITypeDescriptor type => base.Equals(type, y as ITypeDescriptor),
37-
IMethodDescriptor method => base.Equals(method, y as IMethodDescriptor),
38-
IFieldDescriptor field => base.Equals(field, y as IFieldDescriptor),
39-
PropertyDefinition property => Equals(property, y as PropertyDefinition),
40-
EventDefinition @event => Equals(@event, y as EventDefinition),
41-
_ => false,
42-
};
43-
}
44-
45-
public int GetHashCode(IMemberDescriptor obj)
46-
{
47-
return obj switch
48-
{
49-
ITypeDescriptor type => base.GetHashCode(type),
50-
IMethodDescriptor method => base.GetHashCode(method),
51-
IFieldDescriptor field => base.GetHashCode(field),
52-
PropertyDefinition property => GetHashCode(property),
53-
EventDefinition @event => GetHashCode(@event),
54-
_ => throw new ArgumentOutOfRangeException(nameof(obj)),
55-
};
56-
}
57-
58-
public bool Equals(PropertyDefinition? x, PropertyDefinition? y)
59-
{
60-
if (ReferenceEquals(x, y)) return true;
61-
if (x == null || y == null) return false;
62-
63-
return x.Name == y.Name && base.Equals(x.DeclaringType, y.DeclaringType);
64-
}
65-
66-
public int GetHashCode(PropertyDefinition obj)
67-
{
68-
return HashCode.Combine(
69-
obj.Name,
70-
obj.DeclaringType == null ? 0 : base.GetHashCode(obj.DeclaringType),
71-
obj.Signature == null ? 0 : base.GetHashCode(obj.Signature)
72-
);
73-
}
74-
75-
public bool Equals(EventDefinition? x, EventDefinition? y)
76-
{
77-
if (ReferenceEquals(x, y)) return true;
78-
if (x == null || y == null) return false;
79-
80-
return x.Name == y.Name && base.Equals(x.DeclaringType, y.DeclaringType);
81-
}
82-
83-
public int GetHashCode(EventDefinition obj)
84-
{
85-
return HashCode.Combine(
86-
obj.Name,
87-
obj.DeclaringType == null ? 0 : base.GetHashCode(obj.DeclaringType),
88-
obj.EventType == null ? 0 : base.GetHashCode(obj.EventType)
89-
);
90-
}
91-
}
1+
using AsmResolver.DotNet;
2+
using AsmResolver.DotNet.Signatures;
3+
using System.Diagnostics.CodeAnalysis;
4+
5+
namespace ArApiCompat.Utilities.AsmResolver;
6+
7+
// TODO upstream?
8+
9+
/// <summary>
10+
/// A <see cref="SignatureComparer"/> with added support for <see cref="IMemberDescriptor"/>, <see cref="PropertyDefinition"/> and <see cref="EventDefinition"/>.
11+
/// </summary>
12+
[SuppressMessage("Design", "CA1061:Do not hide base class methods", Justification = "Hidden base class methods eventually get called regardless.")]
13+
internal sealed class ExtendedSignatureComparer : SignatureComparer,
14+
IEqualityComparer<IMemberDescriptor>,
15+
IEqualityComparer<PropertyDefinition>,
16+
IEqualityComparer<EventDefinition>
17+
{
18+
public ExtendedSignatureComparer()
19+
{
20+
}
21+
22+
public ExtendedSignatureComparer(SignatureComparisonFlags flags) : base(flags)
23+
{
24+
}
25+
26+
public static new ExtendedSignatureComparer Default { get; } = new();
27+
public static ExtendedSignatureComparer VersionAgnostic { get; } = new(SignatureComparisonFlags.VersionAgnostic);
28+
29+
30+
public bool Equals(IMemberDescriptor? x, IMemberDescriptor? y)
31+
{
32+
if (ReferenceEquals(x, y)) return true;
33+
if (x == null || y == null) return false;
34+
35+
return x switch
36+
{
37+
ITypeDescriptor type => base.Equals(type, y as ITypeDescriptor),
38+
IMethodDescriptor method => base.Equals(method, y as IMethodDescriptor),
39+
IFieldDescriptor field => base.Equals(field, y as IFieldDescriptor),
40+
PropertyDefinition property => Equals(property, y as PropertyDefinition),
41+
EventDefinition @event => Equals(@event, y as EventDefinition),
42+
_ => false,
43+
};
44+
}
45+
46+
public int GetHashCode(IMemberDescriptor obj)
47+
{
48+
return obj switch
49+
{
50+
ITypeDescriptor type => base.GetHashCode(type),
51+
IMethodDescriptor method => base.GetHashCode(method),
52+
IFieldDescriptor field => base.GetHashCode(field),
53+
PropertyDefinition property => GetHashCode(property),
54+
EventDefinition @event => GetHashCode(@event),
55+
_ => throw new ArgumentOutOfRangeException(nameof(obj)),
56+
};
57+
}
58+
59+
public bool Equals(PropertyDefinition? x, PropertyDefinition? y)
60+
{
61+
if (ReferenceEquals(x, y)) return true;
62+
if (x == null || y == null) return false;
63+
64+
return x.Name == y.Name && Equals(x.DeclaringType, y.DeclaringType);
65+
}
66+
67+
public int GetHashCode(PropertyDefinition obj)
68+
{
69+
return HashCode.Combine(
70+
obj.Name,
71+
obj.DeclaringType == null ? 0 : base.GetHashCode(obj.DeclaringType),
72+
obj.Signature == null ? 0 : base.GetHashCode(obj.Signature)
73+
);
74+
}
75+
76+
public bool Equals(EventDefinition? x, EventDefinition? y)
77+
{
78+
if (ReferenceEquals(x, y)) return true;
79+
if (x == null || y == null) return false;
80+
81+
return x.Name == y.Name && base.Equals(x.DeclaringType, y.DeclaringType);
82+
}
83+
84+
public int GetHashCode(EventDefinition obj)
85+
{
86+
return HashCode.Combine(
87+
obj.Name,
88+
obj.DeclaringType == null ? 0 : base.GetHashCode(obj.DeclaringType),
89+
obj.EventType == null ? 0 : base.GetHashCode(obj.EventType)
90+
);
91+
}
92+
93+
protected override bool SimpleTypeEquals(ITypeDescriptor x, ITypeDescriptor y)
94+
{
95+
// Check the basic properties first.
96+
if (!x.IsTypeOf(y.Namespace, y.Name))
97+
return false;
98+
99+
// If scope matches, it is a perfect match.
100+
if (Equals(x.Scope, y.Scope))
101+
return true;
102+
103+
// It can still be an exported type, we need to resolve the type then and check if the definitions match.
104+
// For our purposes, we only actually care that the name matches
105+
return x.Resolve() is { } definition1
106+
&& y.Resolve() is { } definition2
107+
&& Equals(definition1.DeclaringType, definition2.DeclaringType);
108+
}
109+
110+
protected override int SimpleTypeHashCode(ITypeDescriptor obj)
111+
{
112+
return HashCode.Combine(
113+
obj.Namespace,
114+
obj.Name,
115+
obj.DeclaringType is not null ? GetHashCode(obj.DeclaringType) : 0
116+
);
117+
}
118+
119+
}

0 commit comments

Comments
 (0)