Skip to content

Commit 7158bc0

Browse files
committed
split implementations relevant to nullable metadata into extension class TypeGenericParameterExtensions
1 parent ea600ed commit 7158bc0

File tree

2 files changed

+81
-43
lines changed

2 files changed

+81
-43
lines changed

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

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -156,48 +156,6 @@ static bool HasUnmanagedConstraint(Type genericParameter)
156156
static attr => attr.AttributeType.FullName.Equals("System.Runtime.CompilerServices.IsUnmanagedAttribute", StringComparison.Ordinal)
157157
);
158158

159-
static bool IsNullableAttribute(CustomAttributeData attr)
160-
=> attr.AttributeType.FullName.Equals("System.Runtime.CompilerServices.NullableAttribute", StringComparison.Ordinal);
161-
162-
static bool IsNullableContextAttribute(CustomAttributeData attr)
163-
=> attr.AttributeType.FullName.Equals("System.Runtime.CompilerServices.NullableContextAttribute", StringComparison.Ordinal);
164-
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-
183-
// ref: https://github.com/dotnet/roslyn/blob/main/docs/features/nullable-metadata.md#type-parameters
184-
static bool HasNotNullConstraint(Type genericParameter)
185-
{
186-
var attrNullable = genericParameter.CustomAttributes.FirstOrDefault(IsNullableAttribute);
187-
var attrNullableContext =
188-
genericParameter.DeclaringMethod?.CustomAttributes?.FirstOrDefault(IsNullableContextAttribute) ??
189-
TryGetNullableContextAttributeFromDeclaringTypeOrOuterType(genericParameter);
190-
191-
const byte notAnnotated = 1;
192-
193-
if (attrNullableContext is not null && notAnnotated.Equals(attrNullableContext.ConstructorArguments[0].Value))
194-
// `#nullable enable` context
195-
return attrNullable is null;
196-
else
197-
// `#nullable disable` context
198-
return attrNullable is not null && notAnnotated.Equals(attrNullable.ConstructorArguments[0].Value);
199-
}
200-
201159
static bool IsValueType(Type t) => string.Equals(t.FullName, typeof(ValueType).FullName, StringComparison.Ordinal);
202160
static bool IsNotValueType(Type t) => !string.Equals(t.FullName, typeof(ValueType).FullName, StringComparison.Ordinal);
203161

@@ -215,7 +173,7 @@ bool typeWithNamespace
215173

216174
if (
217175
constraintAttrs == GenericParameterAttributes.None &&
218-
HasNotNullConstraint(genericParameter)
176+
genericParameter.HasGenericParameterNotNullConstraint()
219177
) {
220178
yield return "notnull";
221179
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// SPDX-FileCopyrightText: 2022 smdn <smdn@smdn.jp>
2+
// SPDX-License-Identifier: MIT
3+
using System;
4+
#if NULL_STATE_STATIC_ANALYSIS_ATTRIBUTES
5+
using System.Diagnostics.CodeAnalysis;
6+
#endif
7+
using System.Linq;
8+
using System.Reflection;
9+
10+
namespace Smdn.Reflection;
11+
12+
internal static class TypeGenericParameterExtensions {
13+
private static void ValidateGenericParameterArgument(Type param, string paramName)
14+
{
15+
if (param is null)
16+
throw new ArgumentNullException(paramName);
17+
if (!param.IsGenericParameter)
18+
throw new InvalidOperationException($"{paramName} must be a generic parameter or generic argument");
19+
}
20+
21+
// ref: https://github.com/dotnet/roslyn/blob/main/docs/features/nullable-metadata.md#type-parameters
22+
public static bool HasGenericParameterNotNullConstraint(this Type genericParameter)
23+
{
24+
ValidateGenericParameterArgument(genericParameter, nameof(genericParameter));
25+
26+
var attrNullable = genericParameter.CustomAttributes.FirstOrDefault(IsNullableAttribute);
27+
var attrNullableContext = FindNullableContextAttribute(genericParameter);
28+
29+
const byte notAnnotated = 1;
30+
31+
if (attrNullableContext is not null && notAnnotated.Equals(attrNullableContext.ConstructorArguments[0].Value))
32+
// `#nullable enable` context
33+
return attrNullable is null;
34+
else
35+
// `#nullable disable` context
36+
return attrNullable is not null && notAnnotated.Equals(attrNullable.ConstructorArguments[0].Value);
37+
}
38+
39+
private static bool IsNullableAttribute(CustomAttributeData attr)
40+
=> "System.Runtime.CompilerServices.NullableAttribute".Equals(attr.AttributeType.FullName, StringComparison.Ordinal);
41+
42+
private static bool IsNullableContextAttribute(CustomAttributeData attr)
43+
=> "System.Runtime.CompilerServices.NullableContextAttribute".Equals(attr.AttributeType.FullName, StringComparison.Ordinal);
44+
45+
private static bool TryGetNullableContextAttribute(
46+
MemberInfo member,
47+
#if NULL_STATE_STATIC_ANALYSIS_ATTRIBUTES
48+
[NotNullWhen(true)]
49+
#endif
50+
out CustomAttributeData? attrNullableContext
51+
)
52+
{
53+
attrNullableContext = member.CustomAttributes.FirstOrDefault(IsNullableContextAttribute);
54+
55+
return attrNullableContext is not null;
56+
}
57+
58+
private static CustomAttributeData? FindNullableContextAttribute(Type genericParameter)
59+
{
60+
if (
61+
genericParameter.IsGenericMethodParameter &&
62+
genericParameter.DeclaringMethod is not null &&
63+
TryGetNullableContextAttribute(genericParameter.DeclaringMethod, out var methodAttr)
64+
) {
65+
return methodAttr;
66+
}
67+
68+
Type? t = genericParameter;
69+
70+
for (; ; ) {
71+
if ((t = t!.DeclaringType) is null)
72+
return null;
73+
if (TryGetNullableContextAttribute(t, out var typeAttr))
74+
return typeAttr;
75+
76+
// retry against to the outer type
77+
continue;
78+
}
79+
}
80+
}

0 commit comments

Comments
 (0)