Skip to content

Commit c4edb93

Browse files
committed
fix estimating the notnull constraints with optimized nullable metadata of nested types
1 parent e0d83af commit c4edb93

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,31 @@ static bool IsNullableAttribute(CustomAttributeData attr)
162162
static bool IsNullableContextAttribute(CustomAttributeData attr)
163163
=> attr.AttributeType.FullName.Equals("System.Runtime.CompilerServices.NullableContextAttribute", StringComparison.Ordinal);
164164

165+
static CustomAttributeData? TryGetNullableContextAttributeFromDeclaringTypeOrOuterType(Type type)
166+
{
167+
Type? t = type;
168+
169+
for (; ; ) {
170+
if ((t = t!.DeclaringType) is null)
171+
return null;
172+
173+
var attr = t!.CustomAttributes.FirstOrDefault(IsNullableContextAttribute);
174+
175+
if (attr is not null)
176+
return attr;
177+
178+
// retry against to the outer type
179+
continue;
180+
}
181+
}
182+
165183
// ref: https://github.com/dotnet/roslyn/blob/main/docs/features/nullable-metadata.md#type-parameters
166184
static bool HasNotNullConstraint(Type genericParameter)
167185
{
168186
var attrNullable = genericParameter.CustomAttributes.FirstOrDefault(IsNullableAttribute);
169187
var attrNullableContext =
170188
genericParameter.DeclaringMethod?.CustomAttributes?.FirstOrDefault(IsNullableContextAttribute) ??
171-
genericParameter.DeclaringType.CustomAttributes.FirstOrDefault(IsNullableContextAttribute);
189+
TryGetNullableContextAttributeFromDeclaringTypeOrOuterType(genericParameter);
172190

173191
const byte notAnnotated = 1;
174192

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,30 @@ public class CBase { }
11241124
[MemberDeclarationTestCase("public T M15_1<T>(T x) where T : unmanaged, System.IDisposable {}")] public T M15_1<T>(T x) where T : unmanaged, IDisposable => throw new NotImplementedException();
11251125
}
11261126

1127+
public class ConstraintsNotNullWithNullableMetadata {
1128+
#nullable enable annotations
1129+
class ClassWithOptimizedNullableMetadata {
1130+
// these members are required to reproduce optimized nullable metadata
1131+
public void RequiredToBeOptimized0(string p) { }
1132+
public void RequiredToBeOptimized1(string? p) { }
1133+
1134+
// this class will have no NullableContextAttribute after optimization
1135+
class NotNullConstraints {
1136+
[MemberDeclarationTestCase($"public void {nameof(NotNullConstraint)}<TNotNull>(TNotNull p) where TNotNull : notnull {{}}")]
1137+
public void NotNullConstraint<TNotNull>(TNotNull p) where TNotNull : notnull { }
1138+
1139+
// this member is required to reproduce optimized nullable metadata
1140+
#if SYSTEM_REFLECTION_NULLABILITYINFO
1141+
[MemberDeclarationTestCase($"public void {nameof(NotNullConstraint_Nullable)}<TNotNull>(TNotNull? p) where TNotNull : notnull {{}}")]
1142+
#else
1143+
[MemberDeclarationTestCase($"public void {nameof(NotNullConstraint_Nullable)}<TNotNull>(TNotNull p) where TNotNull : notnull {{}}")]
1144+
#endif
1145+
public void NotNullConstraint_Nullable<TNotNull>(TNotNull? p) where TNotNull : notnull { }
1146+
}
1147+
}
1148+
#nullable restore annotations
1149+
}
1150+
11271151
class Constraints2 {
11281152
[MemberDeclarationTestCase("public T1 M1<T1, T2>(T2 x) where T1 : new() where T2 : new() {}")]
11291153
public T1 M1<T1, T2>(T2 x)

0 commit comments

Comments
 (0)