Skip to content

Commit d41d7c7

Browse files
committed
add support for showing new modifier on member declarations
1 parent a71ce8f commit d41d7c7

File tree

4 files changed

+144
-37
lines changed

4 files changed

+144
-37
lines changed

src/Smdn.Reflection.ReverseGenerating/Smdn.Reflection.ReverseGenerating/Generator.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ GeneratorOptions options
8383
string GetSingleLineGenericArgumentConstraintsDeclaration()
8484
=> genericArgumentConstraints.Count == 0 ? string.Empty : " " + string.Join(" ", genericArgumentConstraints);
8585

86-
var modifierNew = t.IsHidingInheritedType() ? "new " : null;
86+
var modifierNew = t.IsHidingInheritedType(nonPublic: true) ? "new " : null;
8787

8888
if (t.IsEnum) {
8989
yield return $"{modifierNew}{accessibilities}enum {typeName} : {t.GetEnumUnderlyingType().FormatTypeName()}";
@@ -845,7 +845,7 @@ method is not null &&
845845
if (!string.IsNullOrEmpty(methodReturnTypeAttributes))
846846
sb.Append(methodReturnTypeAttributes).Append(' ');
847847

848-
if (asDelegateDeclaration && m.GetDeclaringTypeOrThrow().IsHidingInheritedType())
848+
if (asDelegateDeclaration && m.GetDeclaringTypeOrThrow().IsHidingInheritedType(nonPublic: true))
849849
sb.Append("new ");
850850

851851
if (!isFinalizer) {
@@ -1193,8 +1193,8 @@ static void AppendMethodModifiers(StringBuilder sb, MethodBase? m)
11931193
sb.Append("unsafe ");
11941194
}
11951195

1196-
// TODO: append 'new' modifier before accessibility
1197-
// sb.Append("new ");
1196+
if (member.IsHidingInheritedMember(nonPublic: true))
1197+
sb.Append("new ");
11981198

11991199
SWITCH_MEMBER_TYPE:
12001200
switch (member) {
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// SPDX-FileCopyrightText: 2022 smdn <smdn@smdn.jp>
2+
// SPDX-License-Identifier: MIT
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Reflection;
7+
8+
namespace Smdn.Reflection;
9+
10+
internal static class MemberInfoInheritedMemberExtensions {
11+
public static bool IsHidingInheritedMember(this MemberInfo member, bool nonPublic)
12+
{
13+
if (member is null)
14+
throw new ArgumentNullException(nameof(member));
15+
16+
if (member is ConstructorInfo)
17+
return false; // constructors can not be 'new'
18+
19+
var bindingFlagsVisibility = BindingFlags.Public;
20+
21+
if (nonPublic)
22+
bindingFlagsVisibility |= BindingFlags.NonPublic;
23+
24+
if (member is Type t) {
25+
if (!t.IsNested)
26+
return false; // non-nested type never hides any types
27+
28+
// is hiding any nested type in type hierarchy?
29+
return EnumerateTypeHierarchy(t.DeclaringType!)
30+
.SelectMany(th => th.GetNestedTypes(bindingFlagsVisibility))
31+
.Any(ti => string.Equals(ti.Name, t.Name, StringComparison.Ordinal));
32+
33+
static IEnumerable<Type> EnumerateTypeHierarchy(Type t)
34+
{
35+
Type? _t = t;
36+
37+
for (; ; ) {
38+
if ((_t = _t?.BaseType) is not null)
39+
yield return _t;
40+
else
41+
break;
42+
}
43+
}
44+
}
45+
46+
if (member.DeclaringType is null)
47+
return false; // XXX: ???
48+
49+
var declaringType = member.DeclaringType;
50+
var baseTypeOrInterfaces = declaringType.IsInterface
51+
? declaringType.GetInterfaces()
52+
: declaringType.BaseType is null
53+
? Enumerable.Empty<Type>()
54+
: Enumerable.Repeat(declaringType.BaseType!, 1);
55+
var overriddenOrHiddenMembers = baseTypeOrInterfaces
56+
.SelectMany(t =>
57+
t.GetMember(
58+
member.Name,
59+
member.MemberType,
60+
bindingFlagsVisibility | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy
61+
)
62+
);
63+
64+
return member switch {
65+
FieldInfo f => overriddenOrHiddenMembers
66+
.OfType<FieldInfo>()
67+
.Any(), // fields cannot be overridden, always 'new'
68+
69+
MethodInfo m => overriddenOrHiddenMembers
70+
.OfType<MethodInfo>()
71+
.Any(mh => HasSameSignature(m, mh) && (declaringType.IsInterface || !m.IsOverridden())),
72+
73+
EventInfo ev =>
74+
overriddenOrHiddenMembers.Any() &&
75+
ev.GetMethods(nonPublic: nonPublic).All(accessor => IsHidingInheritedMember(accessor, nonPublic)),
76+
77+
PropertyInfo p =>
78+
overriddenOrHiddenMembers.Any() &&
79+
p.GetAccessors(nonPublic: nonPublic).All(accessor => IsHidingInheritedMember(accessor, nonPublic)),
80+
81+
_ => false,
82+
};
83+
84+
static bool HasSameSignature(MethodInfo x, MethodInfo y)
85+
{
86+
if (x.ReturnParameter.ParameterType != y.ReturnParameter.ParameterType)
87+
return false;
88+
89+
var paramsX = x.GetParameters();
90+
var paramsY = y.GetParameters();
91+
92+
if (paramsX.Length != paramsY.Length)
93+
return false;
94+
95+
for (var i = 0; i < paramsX.Length; i++) {
96+
if (paramsX[i].ParameterType != paramsY[i].ParameterType)
97+
return false;
98+
}
99+
100+
return true;
101+
}
102+
}
103+
}

src/Smdn.Reflection.ReverseGenerating/Smdn.Reflection/TypeInheritedTypeExtensions.cs

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,6 @@
88
namespace Smdn.Reflection;
99

1010
internal static class TypeInheritedTypeExtensions {
11-
public static bool IsHidingInheritedType(this Type t)
12-
{
13-
if (t is null)
14-
throw new ArgumentNullException(nameof(t));
15-
if (!t.IsNested)
16-
return false;
17-
18-
return EnumerateTypeHierarchy(t.DeclaringType!)
19-
.SelectMany(static th => th.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic))
20-
.Any(ti => ti.Name == t.Name);
21-
22-
static IEnumerable<Type> EnumerateTypeHierarchy(Type t)
23-
{
24-
Type? _t = t;
25-
26-
for (; ; ) {
27-
if ((_t = _t?.BaseType) is not null)
28-
yield return _t;
29-
else
30-
break;
31-
}
32-
}
33-
}
11+
public static bool IsHidingInheritedType(this Type t, bool nonPublic)
12+
=> MemberInfoInheritedMemberExtensions.IsHidingInheritedMember(t, nonPublic);
3413
}

tests/Smdn.Reflection.ReverseGenerating/Smdn.Reflection.ReverseGenerating/Generator.MemberDeclaration.cs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,13 @@ public class Modifiers {
189189
[MemberDeclarationTestCase("public static readonly int F8 = 0;")] public static readonly int F8;
190190
[MemberDeclarationTestCase("public static readonly int F9 = 0;")] static readonly public int F9;
191191
[MemberDeclarationTestCase("public static readonly int F10 = 0;")] readonly public static int F10;
192+
193+
protected int FProtected;
192194
}
193195

194196
public class ModifiersNew : Modifiers {
195-
[SkipTestCase("modifier `new` is not supported currently")]
196197
[MemberDeclarationTestCase("new public int F4;")] public new int F4;
198+
[MemberDeclarationTestCase("new public int FProtected;")] new public int FProtected;
197199
}
198200
#pragma warning restore CA2211
199201

@@ -500,20 +502,26 @@ public class Modifiers1 {
500502
public abstract class Modifiers_Abstract {
501503
[MemberDeclarationTestCase("public abstract int P1 { get; set; }")] public abstract int P1 { get; set; }
502504
[MemberDeclarationTestCase("public virtual int P2 { get; set; }")] public virtual int P2 { get; set; }
505+
506+
[MemberDeclarationTestCase("protected virtual int PProtectedVirtual { get; private set; }")] protected virtual int PProtectedVirtual { get; private set; }
503507
}
504508

505509
public abstract class Modifiers_Override : Modifiers_Abstract {
506510
[MemberDeclarationTestCase("public override int P2 { get; set; }")] public override int P2 { get; set; }
511+
512+
[MemberDeclarationTestCase("protected override int PProtectedVirtual { get; }")] protected override int PProtectedVirtual { get => 0; }
507513
}
508514

509515
public abstract class Modifiers_New : Modifiers_Abstract {
510-
[SkipTestCase("modifier `new` is not supported currently")]
511516
[MemberDeclarationTestCase("new public int P2 { get; set; }")] public new int P2 { get; set; }
517+
518+
[MemberDeclarationTestCase("new public int PProtectedVirtual { private get; set; }")] public new int PProtectedVirtual { private get; set; }
512519
}
513520

514521
public abstract class Modifiers_NewVirtual : Modifiers_Abstract {
515-
[SkipTestCase("modifier `new` is not supported currently")]
516522
[MemberDeclarationTestCase("new public virtual int P2 { get; set; }")] public new virtual int P2 { get; set; }
523+
524+
[MemberDeclarationTestCase("new protected virtual int PProtectedVirtual { private get; set; }")] new protected virtual int PProtectedVirtual { private get; set; }
517525
}
518526

519527
public class Indexers1 {
@@ -656,28 +664,37 @@ public abstract class Abstract {
656664

657665
public class Virtual {
658666
[MemberDeclarationTestCase("public virtual void M() {}")] public virtual void M() { }
667+
668+
[MemberDeclarationTestCase("protected virtual void MProtectedVirtual() {}")] protected virtual void MProtectedVirtual() { }
659669
}
660670

661671
public class Override : Virtual {
662672
[MemberDeclarationTestCase("public override void M() {}")] public override void M() { }
673+
674+
[MemberDeclarationTestCase("protected override void MProtectedVirtual() {}")] protected override void MProtectedVirtual() { }
663675
}
664676

665677
public class SealedOverride1 : Virtual {
666678
[MemberDeclarationTestCase("public sealed override void M() {}")] public sealed override void M() { }
679+
680+
[MemberDeclarationTestCase("protected sealed override void MProtectedVirtual() {}")] protected sealed override void MProtectedVirtual() { }
681+
667682
}
668683

669684
public class SealedOverride2 : Virtual {
670685
[MemberDeclarationTestCase("public sealed override void M() {}")] public override sealed void M() { }
671686
}
672687

673688
public abstract class New : Virtual {
674-
[SkipTestCase("modifier `new` is not supported currently")]
675689
[MemberDeclarationTestCase("new public void M() {}")] public new void M() { }
690+
691+
[MemberDeclarationTestCase("new protected void MProtectedVirtual() {}")] protected new void MProtectedVirtual() { }
676692
}
677693

678694
public abstract class NewVirtual : Virtual {
679-
[SkipTestCase("modifier `new` is not supported currently")]
680695
[MemberDeclarationTestCase("new public virtual void M() {}")] public new virtual void M() { }
696+
697+
[MemberDeclarationTestCase("new protected virtual void MProtectedVirtual() {}")] protected new virtual void MProtectedVirtual() { }
681698
}
682699

683700
public class Unsafe {
@@ -1166,11 +1183,22 @@ public class Modifiers {
11661183
[MemberDeclarationTestCase("public event System.EventHandler E1;")] public event EventHandler E1;
11671184
[MemberDeclarationTestCase("public static event System.EventHandler E2;")] public static event EventHandler E2;
11681185
[MemberDeclarationTestCase("public static event System.EventHandler E3;")] static public event EventHandler E3;
1186+
1187+
[MemberDeclarationTestCase("protected virtual event System.EventHandler EProtectedVirtual;")] protected virtual event EventHandler EProtectedVirtual;
1188+
}
1189+
1190+
public class ModifiersOverride : Modifiers {
1191+
[MemberDeclarationTestCase("protected override event System.EventHandler EProtectedVirtual;")] protected override event EventHandler EProtectedVirtual;
11691192
}
11701193

11711194
public class ModifiersNew : Modifiers {
1172-
[SkipTestCase("modifier `new` is not supported currently")]
11731195
[MemberDeclarationTestCase("new public event System.EventHandler E1;")] public new event EventHandler E1;
1196+
1197+
[MemberDeclarationTestCase("new public event System.EventHandler EProtectedVirtual;")] public new event EventHandler EProtectedVirtual;
1198+
}
1199+
1200+
public class ModifiersNewVirtual : Modifiers {
1201+
[MemberDeclarationTestCase("new public virtual event System.EventHandler EProtectedVirtual;")] public new virtual event EventHandler EProtectedVirtual;
11741202
}
11751203

11761204
public class Accessibilities {
@@ -1253,15 +1281,12 @@ public interface IBase {
12531281
}
12541282

12551283
public interface IEx : IBase {
1256-
[SkipTestCase("modifier `new` is not supported currently")]
12571284
[MemberDeclarationTestCase("new static virtual void MVirtual() {}")]
12581285
new static virtual void MVirtual() => throw null;
12591286

1260-
[SkipTestCase("modifier `new` is not supported currently")]
1261-
[MemberDeclarationTestCase("new static abstract void MAbstract() {}")]
1287+
[MemberDeclarationTestCase("new static abstract void MAbstract();")]
12621288
new static abstract void MAbstract();
12631289

1264-
[SkipTestCase("modifier `new` is not supported currently")]
12651290
[MemberDeclarationTestCase("new static abstract event System.EventHandler? EAbstract;")]
12661291
new static abstract event EventHandler? EAbstract;
12671292
}

0 commit comments

Comments
 (0)