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

Allow overridden properties that inherit attributes #556

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 40 additions & 6 deletions src/CommandLineUtils/Internal/ReflectionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ namespace McMaster.Extensions.CommandLineUtils
{
internal static class ReflectionHelper
{
private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
private const BindingFlags InheritedLookup = BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Static;

private const BindingFlags DeclaredOnlyLookup = InheritedLookup | BindingFlags.DeclaredOnly;

public static SetPropertyDelegate GetPropertySetter(PropertyInfo prop)
{
Expand Down Expand Up @@ -158,19 +161,50 @@ public static bool IsSpecialTupleType(Type type, [NotNullWhen(true)] out Type? w
return result;
}

private class MethodMetadataEquality : IEqualityComparer<MethodInfo>
{
public bool Equals(MethodInfo? x, MethodInfo? y)
{
if (x == null && y == null)
{
return true;
}

return x != null && y != null && x.HasSameMetadataDefinitionAs(y);
}

public int GetHashCode(MethodInfo obj)
{
return obj.HasMetadataToken() ? obj.GetMetadataToken().GetHashCode() : 0;
}
}

private static IEnumerable<MemberInfo> GetAllMembers(Type type)
{
while (type != null)
// Keep track of the base definitions of property getters we see so we can skip ones we've seen already
var baseGetters = new HashSet<MethodInfo>(new MethodMetadataEquality());

var currentType = type;
while (currentType != null)
{
var members = type.GetMembers(DeclaredOnlyLookup);
var members = currentType.GetMembers(InheritedLookup);
foreach (var member in members)
{
if (member is PropertyInfo property)
{
var getter = property.GetGetMethod(true)?.GetBaseDefinition();

// If we have a getter, try to add it to our set. If it _wasn't_ a new element, don't yield it.
if (getter != null && !baseGetters.Add(getter))
{
continue;
}
}

yield return member;
}

#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
type = type.BaseType;
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
currentType = currentType.BaseType;
}
}
}
Expand Down