Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Shane32 committed Nov 1, 2021
1 parent d54033e commit c487cd6
Show file tree
Hide file tree
Showing 4 changed files with 522 additions and 296 deletions.
138 changes: 11 additions & 127 deletions src/GraphQL.DI/DIObjectGraphType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,56 +233,6 @@ protected virtual IEnumerable<MethodInfo> GetMethodsToProcess()

}

/// <summary>
/// Returns a boolean indicating if the return value of a method is nullable.
/// </summary>
protected virtual bool GetNullability(MethodInfo method)
{
if (method.GetCustomAttribute<OptionalAttribute>() != null)
return true;
if (method.GetCustomAttribute<RequiredAttribute>() != null)
return false;
if (method.ReturnType.IsValueType)
return method.ReturnType.IsConstructedGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Nullable<>);

Nullability nullable = GetMethodDefaultNullability(method);

// now check the return type to see if there's a nullable attribute for it
var attribute = method.ReturnParameter.CustomAttributes.FirstOrDefault(x =>
x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute" &&
x.ConstructorArguments.Count == 1 &&
(x.ConstructorArguments[0].ArgumentType == typeof(byte) ||
x.ConstructorArguments[0].ArgumentType == typeof(byte[])));
if (attribute != null && attribute.ConstructorArguments[0].ArgumentType == typeof(byte)) {
nullable = (Nullability)(byte)attribute.ConstructorArguments[0].Value;
}

var nullabilityBytes = attribute?.ConstructorArguments[0].Value as IList<CustomAttributeTypedArgument>;
var index = 0;
nullable = Consider(method.ReturnType);
return nullable != Nullability.NonNullable;

Nullability Consider(Type t)
{
var g = t.IsGenericType ? t.GetGenericTypeDefinition() : null;
if (g == typeof(Nullable<>))
return Nullability.Nullable;
if (t.IsValueType)
return Nullability.NonNullable;
if ((nullabilityBytes != null && (byte)nullabilityBytes[index].Value == (byte)Nullability.Nullable) || (nullabilityBytes == null && nullable == Nullability.Nullable))
return Nullability.Nullable;
if (g == typeof(IDataLoaderResult<>) || g == typeof(Task<>)) {
index++;
return Consider(t.GenericTypeArguments[0]);
}
if (t == typeof(IDataLoaderResult))
return Nullability.Nullable;
if (nullabilityBytes != null)
return (Nullability)(byte)nullabilityBytes[index].Value;
return nullable;
}
}

private (Nullability, IList<CustomAttributeTypedArgument>?) GetMethodParameterNullability(ParameterInfo parameter)
{
Nullability nullableContext = GetMethodDefaultNullability(parameter.Member);
Expand All @@ -302,24 +252,13 @@ Nullability Consider(Type t)
return (nullableContext, null);
}

private static readonly Type[] _valueTupleTypes = new[] {
typeof(ValueTuple<>),
typeof(ValueTuple<,>),
typeof(ValueTuple<,,>),
typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>),
typeof(ValueTuple<,,,,,>),
typeof(ValueTuple<,,,,,,>),
typeof(ValueTuple<,,,,,,,>),
};

/// <summary>
/// Returns a boolean indicating if the return value of a method is nullable.
/// <para>
/// See <seealso href="https://github.com/dotnet/roslyn/blob/main/docs/features/nullable-metadata.md"/>.
/// </para>
/// </summary>
internal IEnumerable<(Type Type, Nullability Nullable)> GetNullability2(ParameterInfo parameter)
protected virtual IEnumerable<(Type Type, Nullability Nullable)> GetNullability(ParameterInfo parameter)
{
var (nullableContext, nullabilityBytes) = GetMethodParameterNullability(parameter);
var list = new List<(Type, Nullability)>();
Expand All @@ -338,15 +277,8 @@ void Consider(Type t, bool nullableValueType)
//do not increment index for Nullable<T>
//do not add Nullable<T> to the list but rather just add underlying type
Consider(t.GenericTypeArguments[0], true);
}
else if (_valueTupleTypes.Contains(g)) {
//do not increment index for value tuples
list.Add((t, nullableValueType ? Nullability.Nullable : Nullability.NonNullable));
for (int i = 0; i < t.GenericTypeArguments.Length; i++) {
Consider(t.GenericTypeArguments[i], false);
}
} else {
//generic structs that are not tuples or Nullable<T> will contain a 0 in the array
//generic structs that are not Nullable<T> will contain a 0 in the array
index++;
list.Add((t, nullableValueType ? Nullability.Nullable : Nullability.NonNullable));
for (int i = 0; i < t.GenericTypeArguments.Length; i++) {
Expand All @@ -370,24 +302,6 @@ void Consider(Type t, bool nullableValueType)
Consider(t.GenericTypeArguments[i], false);
}
}


//var g = t.IsGenericType ? t.GetGenericTypeDefinition() : null;
//if (g == typeof(Nullable<>))
// return Nullability.Nullable;
//if (t.IsValueType)
// return Nullability.NonNullable;
//if ((nullabilityBytes != null && (byte)nullabilityBytes[index].Value == (byte)Nullability.Nullable) || (nullabilityBytes == null && nullableContext == Nullability.Nullable))
// return Nullability.Nullable;
//if (g == typeof(IDataLoaderResult<>) || g == typeof(Task<>)) {
// index++;
// return Consider(t.GenericTypeArguments[0]);
//}
//if (t == typeof(IDataLoaderResult))
// return Nullability.Nullable;
//if (nullabilityBytes != null)
// return (Nullability)(byte)nullabilityBytes[index].Value;
//return nullableContext;
}
}

Expand Down Expand Up @@ -423,42 +337,6 @@ void CheckDeclaringType(Type parentType)
}
}

/// <summary>
/// Returns a boolean indicating if the parameter value is nullable
/// </summary>
protected virtual bool GetNullability(MethodInfo method, ParameterInfo parameter)
{
if (parameter.GetCustomAttribute<OptionalAttribute>() != null)
return true;
if (parameter.GetCustomAttribute<RequiredAttribute>() != null)
return false;
if (parameter.GetCustomAttribute<System.ComponentModel.DataAnnotations.RequiredAttribute>() != null)
return false;
if (parameter.IsOptional)
return true;
if (parameter.ParameterType.IsValueType)
return parameter.ParameterType.IsConstructedGenericType && parameter.ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>);

Nullability nullable = GetMethodDefaultNullability(method);

// now check the parameter to see if there's a nullable attribute for it
var attribute = parameter.CustomAttributes.FirstOrDefault(x =>
x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute" &&
x.ConstructorArguments.Count == 1 &&
(x.ConstructorArguments[0].ArgumentType == typeof(byte) ||
x.ConstructorArguments[0].ArgumentType == typeof(byte[])));
if (attribute != null && attribute.ConstructorArguments[0].ArgumentType == typeof(byte)) {
nullable = (Nullability)(byte)attribute.ConstructorArguments[0].Value;
}

var nullabilityBytes = attribute?.ConstructorArguments[0].Value as IList<CustomAttributeTypedArgument>;
if ((nullabilityBytes != null && (byte)nullabilityBytes[0].Value == (byte)Nullability.Nullable) || (nullabilityBytes == null && nullable == Nullability.Nullable))
return true;
if (nullabilityBytes != null)
return (Nullability)(byte)nullabilityBytes[0].Value != Nullability.NonNullable;
return nullable != Nullability.NonNullable;
}

/// <summary>
/// Apply <see cref="RequiredAttribute"/>, <see cref="OptionalAttribute"/>, <see cref="RequiredListAttribute"/>,
/// <see cref="OptionalListAttribute"/> and <see cref="IdAttribute"/> over the supplied <see cref="TypeInformation"/>.
Expand Down Expand Up @@ -499,6 +377,7 @@ protected virtual TypeInformation ApplyAttributes(TypeInformation typeInformatio
typeof(IReadOnlyCollection<>),
typeof(IReadOnlyList<>),
typeof(HashSet<>),
typeof(ISet<>),
};

/// <summary>
Expand All @@ -510,27 +389,32 @@ protected virtual TypeInformation GetTypeInformation(ParameterInfo parameterInfo
var isOptionalParameter = parameterInfo.IsOptional;
var isList = false;
var isNullableList = false;
var typeTree = GetNullability2(parameterInfo);
var typeTree = GetNullability(parameterInfo);
foreach (var type in typeTree) {
if (type.Type == typeof(IDataLoaderResult))
//assume type is nullable object
break;
if (type.Type.IsArray) {
//unwrap type and mark as list
isList = true;
isNullableList = type.Nullable != Nullability.NonNullable;
continue;
}
if (type.Type.IsGenericType) {
var g = type.Type.GetGenericTypeDefinition();
if (g == typeof(IDataLoaderResult<>) || g == typeof(Task<>)) {
//unwrap type
continue;
}
if (_listTypes.Contains(g)) {
//unwrap type and mark as list
isList = true;
isNullableList = type.Nullable != Nullability.NonNullable;
continue;
}
}
if (type.Type == typeof(IEnumerable)) {
if (type.Type == typeof(IEnumerable) || type.Type == typeof(ICollection)) {
//assume list of nullable object
isList = true;
isNullableList = type.Nullable != Nullability.NonNullable;
break;
Expand All @@ -545,7 +429,7 @@ protected virtual TypeInformation GetTypeInformation(ParameterInfo parameterInfo
}
return new TypeInformation(parameterInfo, isInputArgument, type.Type, nullable, isList, isNullableList, null);
}
//unknown
//unknown type
if (isOptionalParameter && isList)
isNullableList = true;
return new TypeInformation(parameterInfo, isInputArgument, typeof(object), true, isList, isNullableList, null);
Expand Down
2 changes: 1 addition & 1 deletion src/GraphQL.DI/Nullability.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace GraphQL.DI
{
internal enum Nullability : byte
public enum Nullability : byte
{
Unknown = 0,
NonNullable = 1,
Expand Down
Loading

0 comments on commit c487cd6

Please sign in to comment.