Resolve hidden inspector members to the most-derived declaration

A keyed type that hides a base member with `new` offered two candidates
for one name. The inspector now keeps the most-derived declaration, after
dropping [IgnoreMember] ones, as the registry's own-member scan does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-14 13:59:15 +02:00
commit 5de49ebc60
2 changed files with 24 additions and 1 deletions

View file

@ -520,7 +520,10 @@ namespace GameCult.Caching
var candidates = type.GetMembers(Public)
.Where(member => member is FieldInfo field && !field.IsLiteral ||
member is PropertyInfo property && property.GetGetMethod() != null && property.GetIndexParameters().Length == 0)
.Where(member => !member.IsDefined(typeof(IgnoreMemberAttribute), true));
.Where(member => !member.IsDefined(typeof(IgnoreMemberAttribute), true))
// A member hidden with `new` is its most-derived declaration, as the registry's own-member scan reads it.
.GroupBy(member => member.Name)
.Select(hides => hides.OrderByDescending(member => Ancestry(member.DeclaringType)).First());
IEnumerable<(MemberInfo Member, int Slot)> slotted;
if (type.IsDefined(typeof(CultDocumentAttribute), false))
{
@ -575,6 +578,8 @@ namespace GameCult.Caching
private static CultInspectorShape Unsupported(Type type, string reason) =>
new CultInspectorShape(type, CultInspectorValueKind.Unsupported) { Reason = reason };
private static int Ancestry(Type? type) => type == null ? 0 : 1 + Ancestry(type.BaseType);
private static Type TypeOf(MemberInfo member) => member is FieldInfo field ? field.FieldType : ((PropertyInfo)member).PropertyType;
private static bool Assignable(MemberInfo member, bool allowPrivate)

View file

@ -39,6 +39,10 @@ namespace GameCult.Caching.Tests
var id = Model().MembersOf(typeof(InspectFixed)).Single();
Assert.That(id.IsAssignable, Is.False, "a get-only member its constructor fills cannot be assigned");
Assert.That(id.IsReadOnly, Is.True);
var hidden = Model().MembersOf(typeof(InspectHider)).Single();
Assert.That(hidden.Member.DeclaringType, Is.EqualTo(typeof(InspectHider)), "a `new` member is its most-derived declaration");
Assert.That(hidden.ValueType, Is.EqualTo(typeof(string)));
}
[Test]
@ -242,6 +246,20 @@ namespace GameCult.Caching.Tests
public float Side;
}
[MessagePackObject]
public class InspectHidden
{
[Key(0)]
public int Value;
}
[MessagePackObject]
public sealed class InspectHider : InspectHidden
{
[Key(0)]
public new string Value = string.Empty;
}
public readonly record struct InspectPair(float x, float y);
public interface IFakeDrawer