Skip to content

Commit

Permalink
Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
aalmada committed Sep 20, 2023
1 parent ba65c87 commit 0cb1913
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 63 deletions.
48 changes: 24 additions & 24 deletions NetFabric.CSharp.UnitTests/TestData/IndexableDataSets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,30 @@ public class IndexableTestData
public static TheoryData<Type, IndexableTestData> Indexables =>
new()
{
// {
// typeof(IndexableWithCount<int>),
// new IndexableTestData
// {
// IndexerDeclaringType = typeof(IndexableWithCount<int>),
// CountOrLengthDeclaringType = typeof(IndexableWithCount<int>),
// }
// },
// {
// typeof(IndexableWithLength<int>),
// new IndexableTestData
// {
// IndexerDeclaringType = typeof(IndexableWithLength<int>),
// CountOrLengthDeclaringType = typeof(IndexableWithLength<int>),
// }
// },
// {
// typeof(IIndexable<int>),
// new IndexableTestData
// {
// IndexerDeclaringType = typeof(IIndexable<int>),
// CountOrLengthDeclaringType = typeof(IIndexable<int>),
// }
// },
{
typeof(IndexableWithCount<int>),
new IndexableTestData
{
IndexerDeclaringType = typeof(IndexableWithCount<int>),
CountOrLengthDeclaringType = typeof(IndexableWithCount<int>),
}
},
{
typeof(IndexableWithLength<int>),
new IndexableTestData
{
IndexerDeclaringType = typeof(IndexableWithLength<int>),
CountOrLengthDeclaringType = typeof(IndexableWithLength<int>),
}
},
{
typeof(IIndexable<int>),
new IndexableTestData
{
IndexerDeclaringType = typeof(IIndexable<int>),
CountOrLengthDeclaringType = typeof(IIndexable<int>),
}
},
{
typeof(IDerivedIndexable<int>),
new IndexableTestData
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ static bool HandleGetAsyncEnumerator(IMethodSymbol getEnumerator,
}

var moveNext = enumeratorType.GetPublicMethod(NameOf.MoveNextAsync);
if (moveNext is null) // TODO: || moveNext.ReturnType.MetadataName != "System.Boolean")
if (moveNext is null ||
moveNext.ReturnType is not INamedTypeSymbol namedReturnType ||
namedReturnType.ToDisplayString() != "System.Threading.Tasks.ValueTask<bool>")
{
enumerableSymbols = default;
error = IsAsyncEnumerableError.MissingMoveNextAsync;
Expand Down
37 changes: 37 additions & 0 deletions NetFabric.CodeAnalysis/ITypeSymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -281,4 +281,41 @@ public static bool IsIntegerType(this ITypeSymbol typeSymbol, Compilation compil

return false;
}

/// <summary>
/// Determines whether the specified <see cref="ITypeSymbol"/> represents a floating-point numeric type within the context of the provided <see cref="Compilation"/>.
/// </summary>
/// <param name="typeSymbol">The <see cref="ITypeSymbol"/> to check for a floating-point numeric type.</param>
/// <param name="compilation">The <see cref="Compilation"/> containing the semantic information about the code.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="ITypeSymbol"/> represents a floating-point numeric type; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// Starting from .NET 7, this method checks if the <see cref="ITypeSymbol"/> represents a floating-point numeric type within the context of the provided <see cref="Compilation"/>.
/// It also compares the <see cref="ITypeSymbol"/> to the <see cref="System.Numerics.IFloatingPoint{T}"/> interface,
/// which indicates support for floating-point numeric operations.
/// </remarks>
public static bool IsFloatingPointType(this ITypeSymbol typeSymbol, Compilation compilation)
{
if (typeSymbol.MetadataName == "System.Half" ||
typeSymbol.MetadataName == "System.Float" ||
typeSymbol.MetadataName == "System.Double" ||
typeSymbol.MetadataName == "System.Decimal")
{
return true;
}

// supported starting from .NET 7
var floatingPointType = compilation.GetTypeByMetadataName("System.Numerics.IFloatingPoint`1")!;
if (floatingPointType is not null &&
typeSymbol.ImplementsInterface(floatingPointType, out var arguments) &&
arguments.Length == 1 &&
arguments[0].MetadataName == typeSymbol.MetadataName)
{
return true;
}

return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;

namespace NetFabric.Reflection;

Expand Down Expand Up @@ -48,7 +49,7 @@ public static bool IsAsyncEnumerable(this Type type,
}

var moveNextAsync = enumeratorType.GetMethod(PublicInstance, NameOf.MoveNextAsync, Type.EmptyTypes);
if (moveNextAsync is null)
if (moveNextAsync is null || moveNextAsync.ReturnType != typeof(ValueTask<bool>))
{
enumerableInfo = default;
error = IsAsyncEnumerableError.MissingMoveNextAsync;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public static bool IsDisposable(this Type type, [NotNullWhen(true)] out MethodIn
=> type.IsDisposable(out dispose, out _);

internal static bool IsDisposable(this Type type, [NotNullWhen(true)] out MethodInfo? dispose, out bool isByRefLike)
{
isByRefLike = type.IsByRefLike();
{
isByRefLike = type.IsByRefLike;
if (isByRefLike)
dispose = type.GetMethod(PublicInstanceDeclaredOnly, NameOf.Dispose, Type.EmptyTypes);
else if (type.ImplementsInterface(typeof(IDisposable), out _))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public static bool IsIndexable(this Type type,
{
var countOrLength = type.GetReadProperty(PublicInstance, NameOf.Count);
countOrLength ??= type.GetReadProperty(PublicInstance, NameOf.Length);
if (countOrLength is null)
if (countOrLength is null || !countOrLength.PropertyType.IsIntegerType())
{
indexableInfo = default;
error = IsIndexableError.MissingCountOrLength;
Expand Down
146 changes: 112 additions & 34 deletions NetFabric.Reflection/Reflection/TypeExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Reflection;

// ReSharper disable LoopCanBeConvertedToQuery
Expand All @@ -9,14 +10,19 @@ namespace NetFabric.Reflection;

public static partial class TypeExtensions
{

/// <summary>
/// Gets a value indicating whether <see cref="System.Type"/> implements the given interface <see cref="System.Type"/>.
/// Determines whether the specified <see cref="Type"/> implements a specified interface and, if so, provides information about generic type arguments.
/// </summary>
/// <param name="type">The <see cref="System.Type"/> to test.</param>
/// <param name="interfaceType">The interface <see cref="System.Type"/> to test.</param>
/// <param name="genericArguments">If methods returns <c>true</c> and interface <see cref="System.Type"/> is generic, contains the generic arguments of the implemented interface.</param>
/// <returns><c>true</c> if <see cref="System.Type"/> implements interface <see cref="System.Type"/>; otherwise, <c>false</c>.</returns>
/// <param name="type">The <see cref="Type"/> to check for interface implementation.</param>
/// <param name="interfaceType">The <see cref="Type"/> of the interface to check for implementation.</param>
/// <param name="genericArguments">
/// When the method returns <c>true</c>, this parameter contains an array of <see cref="Type"/> objects representing the generic type arguments
/// that make the implementation of the specified interface. If the type does not implement the interface or if the interface is non-generic,
/// this parameter is set to <c>null</c>.
/// </param>
/// <returns>
/// <c>true</c> if the specified <see cref="Type"/> implements the specified interface; otherwise, <c>false</c>.
/// </returns>
public static bool ImplementsInterface(this Type type, Type interfaceType, [NotNullWhen(true)] out Type[]? genericArguments)
{
if (!interfaceType.IsGenericType)
Expand All @@ -31,30 +37,16 @@ public static bool ImplementsInterface(this Type type, Type interfaceType, [NotN
return true;
}

foreach (var @interface in type.GetAllInterfaces())
foreach (var @interface in type.GetInterfaces())
{
if (@interface.IsGenericType && @interface.GetGenericTypeDefinition() == interfaceType.GetGenericTypeDefinition())
{
genericArguments = @interface.GetGenericArguments();
if (ImplementsInterface(@interface, interfaceType, out genericArguments))
return true;
}
}

genericArguments = default;
return false;
}

static IEnumerable<Type> GetAllInterfaces(this Type type)
{
foreach (var @interface in type.GetInterfaces())
{
yield return @interface;

foreach (var baseInterface in @interface.GetAllInterfaces())
yield return baseInterface;
}
}

internal const BindingFlags PublicInstance =
BindingFlags.Public | BindingFlags.Instance;

Expand All @@ -72,22 +64,61 @@ static IEnumerable<Type> GetAllInterfaces(this Type type)
}
}

if (type.IsInterface && !bindingAttr.HasFlag(BindingFlags.DeclaredOnly))
{
foreach(var @interface in type.GetInterfaces())
{
var indexer = GetReadIndexer(@interface, bindingAttr, parameterTypes);
if (indexer is not null)
return indexer;
}
}

return default;
}

internal static PropertyInfo? GetReadProperty(this Type type, BindingFlags bindingAttr, string name)
{
foreach (var property in type.GetProperties(bindingAttr))
{
if (property.Name == name && property.GetGetMethod() is not null)
if (property.Name == name &&
property.GetGetMethod() is not null)
{
return property;
}
}

if (type.IsInterface && !bindingAttr.HasFlag(BindingFlags.DeclaredOnly))
{
foreach(var @interface in type.GetInterfaces())
{
var indexer = GetReadProperty(@interface, bindingAttr, name);
if (indexer is not null)
return indexer;
}
}

return default;
}

internal static MethodInfo? GetMethod(this Type type, BindingFlags bindingAttr, string name, params Type[] types)
=> type.GetMethod(name, bindingAttr, null, types, null);
{
var method = type.GetMethod(name, bindingAttr, types);
if (method is not null)
return method;

if (type.IsInterface && !bindingAttr.HasFlag(BindingFlags.DeclaredOnly))
{
foreach(var @interface in type.GetInterfaces())
{
method = GetMethod(@interface, bindingAttr, name);
if (method is not null)
return method;
}
}

return default;
}

static bool SequenceEqual(ParameterInfo[] parameters, Type[] types)
{
Expand All @@ -104,9 +135,14 @@ static bool SequenceEqual(ParameterInfo[] parameters, Type[] types)
return true;
}

static bool IsByRefLike(this Type type)
=> type.IsByRefLike;

/// <summary>
/// Determines whether the specified <see cref="Type"/> represents a <see cref="Span{T}"/> or <see cref="ReadOnlySpan{T}"/>.
/// </summary>
/// <param name="type">The <see cref="Type"/> to check for <see cref="Span{T}"/> or <see cref="ReadOnlySpan{T}"/>.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="Type"/> represents a <see cref="Span{T}"/> or <see cref="ReadOnlySpan{T}"/>;
/// otherwise, <c>false</c>.
/// </returns>
public static bool IsSpanOrReadOnlySpan(this Type type)
{
if (type.IsGenericType)
Expand All @@ -131,19 +167,61 @@ public static bool IsSpanOrReadOnlySpan(this Type type)
/// </remarks>
public static bool IsIntegerType(this Type type)
{
#if NET7_0_OR_GREATER
return (type.ImplementsInterface(typeof(System.Numerics.IBinaryInteger<>), out var arguments) &&
arguments.Length == 1 &&
arguments[0].GetType() == type);
#else
return type == typeof(SByte) ||
if (type == typeof(SByte) ||
type == typeof(Byte) ||
type == typeof(Int16) ||
type == typeof(UInt16) ||
type == typeof(Int32) ||
type == typeof(UInt32) ||
type == typeof(Int64) ||
type == typeof(UInt64);
type == typeof(UInt64))
{
return true;
}

#if NET7_0_OR_GREATER
if (type.ImplementsInterface(typeof(IBinaryInteger<>), out var arguments) &&
arguments.Length == 1 &&
arguments[0] == type)
{
return true;
}
#endif

return false;
}

/// <summary>
/// Determines whether the specified <see cref="Type"/> represents a floating-point numeric type.
/// </summary>
/// <param name="type">The <see cref="Type"/> to check for a floating-point numeric type.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="Type"/> represents a floating-point numeric type; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// Starting from .NET 7, this method compares the <see cref="Type"/> to the <see cref="System.Numerics.IFloatingPoint{T}"/> interface,
/// which indicates support for floating-point numeric operations.
/// </remarks>
public static bool IsFloatingPointType(this Type type)
{
if (type == typeof(Half) ||
type == typeof(float) ||
type == typeof(double) ||
type == typeof(decimal))
{
return true;
}

#if NET7_0_OR_GREATER
if (type.ImplementsInterface(typeof(IFloatingPoint<>), out var arguments) &&
arguments.Length == 1 &&
arguments[0] == type)
{
return true;
}
#endif

return false;
}

}

0 comments on commit 0cb1913

Please sign in to comment.