< Summary

Line coverage
27%
Covered lines: 118
Uncovered lines: 308
Coverable lines: 426
Total lines: 976
Line coverage: 27.6%
Branch coverage
16%
Covered branches: 53
Total branches: 326
Branch coverage: 16.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: GetCompatibleGenericBaseClass(...)70%101090.47%
File 1: GetCompatibleGenericInterface(...)66.66%121277.77%
File 1: IsImmutableDictionaryType(...)0%880%
File 1: IsImmutableEnumerableType(...)0%30300%
File 1: GetImmutableDictionaryConstructingTypeName(...)0%440%
File 1: GetImmutableEnumerableConstructingTypeName(...)0%26260%
File 1: GetBaseNameFromGenericType(...)100%110%
File 1: IsVirtual(...)66.66%66100%
File 1: IsKeyValuePair(...)50%22100%
File 1: TryGetDeserializationConstructor(...)61.53%262659.52%
File 1: GetDefaultValue(...)25%121247.05%
File 1: GetSortedTypeHierarchy(...)75%4483.33%
File 2: IsNullableOfT(...)100%22100%
File 2: IsNullableType(...)100%22100%
File 2: IsAssignableFromInternal(...)50%4466.66%
File 2: IsInSubtypeRelationshipWith(...)50%22100%
File 2: HasJsonConstructorAttribute(...)100%11100%
File 2: HasRequiredMemberAttribute(...)100%11100%
File 2: HasSetsRequiredMembersAttribute(...)100%11100%
File 2: HasCustomAttributeWithName(...)25%4444.44%
File 2: GetUniqueCustomAttribute(...)75%4490%
File 2: CreateInstanceNoWrapExceptions(...)100%11100%
File 2: GetMatchingGenericBaseTypes()0%14140%
File 2: TryUnifyWith(...)0%32320%
File 2: IsAtLeastAsVisibleAs(...)100%110%
File 2: IsAsRestrictive(System.Type,System.Type)0%82820%
File 2: IsAccessibleViaInheritance(System.Type,System.Type)0%10100%
File 2: HasInternalAccessTo(System.Reflection.Assembly,System.Reflection.Assembly)0%14140%
File 2: OriginalDefinition(System.Type)0%440%
File 2: GetDeclaredAccessibility(System.Type)0%12120%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/Common/ReflectionExtensions.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Reflection;
 6using System.Collections.Generic;
 7#if !BUILDING_SOURCE_GENERATOR
 8using System.Diagnostics.CodeAnalysis;
 9#endif
 10
 11namespace System.Text.Json.Reflection
 12{
 13    internal static partial class ReflectionExtensions
 14    {
 15        // Immutable collection types.
 16        private const string ImmutableArrayGenericTypeName = "System.Collections.Immutable.ImmutableArray`1";
 17        private const string ImmutableListGenericTypeName = "System.Collections.Immutable.ImmutableList`1";
 18        private const string ImmutableListGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableList`1";
 19        private const string ImmutableStackGenericTypeName = "System.Collections.Immutable.ImmutableStack`1";
 20        private const string ImmutableStackGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableStack`1";
 21        private const string ImmutableQueueGenericTypeName = "System.Collections.Immutable.ImmutableQueue`1";
 22        private const string ImmutableQueueGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableQueue`1";
 23        private const string ImmutableSortedSetGenericTypeName = "System.Collections.Immutable.ImmutableSortedSet`1";
 24        private const string ImmutableHashSetGenericTypeName = "System.Collections.Immutable.ImmutableHashSet`1";
 25        private const string ImmutableSetGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableSet`1";
 26        private const string ImmutableDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableDictionary`2";
 27        private const string ImmutableDictionaryGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableDicti
 28        private const string ImmutableSortedDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableSortedDic
 29
 30        // Immutable collection builder types.
 31        private const string ImmutableArrayTypeName = "System.Collections.Immutable.ImmutableArray";
 32        private const string ImmutableListTypeName = "System.Collections.Immutable.ImmutableList";
 33        private const string ImmutableStackTypeName = "System.Collections.Immutable.ImmutableStack";
 34        private const string ImmutableQueueTypeName = "System.Collections.Immutable.ImmutableQueue";
 35        private const string ImmutableSortedSetTypeName = "System.Collections.Immutable.ImmutableSortedSet";
 36        private const string ImmutableHashSetTypeName = "System.Collections.Immutable.ImmutableHashSet";
 37        private const string ImmutableDictionaryTypeName = "System.Collections.Immutable.ImmutableDictionary";
 38        private const string ImmutableSortedDictionaryTypeName = "System.Collections.Immutable.ImmutableSortedDictionary
 39
 40        public const string CreateRangeMethodName = "CreateRange";
 41
 42        public static Type? GetCompatibleGenericBaseClass(this Type type, Type? baseType)
 74043        {
 74044            if (baseType is null)
 045            {
 046                return null;
 47            }
 48
 74049            Debug.Assert(baseType.IsGenericType);
 74050            Debug.Assert(!baseType.IsInterface);
 74051            Debug.Assert(baseType == baseType.GetGenericTypeDefinition());
 52
 74053            Type? baseTypeToCheck = type;
 54
 101155            while (baseTypeToCheck != null && baseTypeToCheck != typeof(object))
 74056            {
 74057                if (baseTypeToCheck.IsGenericType)
 74058                {
 74059                    Type genericTypeToCheck = baseTypeToCheck.GetGenericTypeDefinition();
 74060                    if (genericTypeToCheck == baseType)
 46961                    {
 46962                        return baseTypeToCheck;
 63                    }
 27164                }
 65
 27166                baseTypeToCheck = baseTypeToCheck.BaseType;
 27167            }
 68
 27169            return null;
 74070        }
 71
 72#if !BUILDING_SOURCE_GENERATOR
 73        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
 74            Justification = "The 'interfaceType' must exist and so trimmer kept it. In which case " +
 75                "It also kept it on any type which implements it. The below call to GetInterfaces " +
 76                "may return fewer results when trimmed but it will return the 'interfaceType' " +
 77                "if the type implemented it, even after trimming.")]
 78#endif
 79        public static Type? GetCompatibleGenericInterface(this Type type, Type? interfaceType)
 96580        {
 96581            if (interfaceType is null)
 082            {
 083                return null;
 84            }
 85
 96586            Debug.Assert(interfaceType.IsGenericType);
 96587            Debug.Assert(interfaceType.IsInterface);
 96588            Debug.Assert(interfaceType == interfaceType.GetGenericTypeDefinition());
 89
 96590            Type interfaceToCheck = type;
 91
 96592            if (interfaceToCheck.IsGenericType)
 96593            {
 96594                interfaceToCheck = interfaceToCheck.GetGenericTypeDefinition();
 96595            }
 96
 96597            if (interfaceToCheck == interfaceType)
 098            {
 099                return type;
 100            }
 101
 12149102            foreach (Type typeToCheck in type.GetInterfaces())
 4627103            {
 4627104                if (typeToCheck.IsGenericType)
 2678105                {
 2678106                    Type genericInterfaceToCheck = typeToCheck.GetGenericTypeDefinition();
 2678107                    if (genericInterfaceToCheck == interfaceType)
 0108                    {
 0109                        return typeToCheck;
 110                    }
 2678111                }
 4627112            }
 113
 965114            return null;
 965115        }
 116
 117        public static bool IsImmutableDictionaryType(this Type type)
 0118        {
 0119            if (!type.IsGenericType || !type.Assembly.FullName!.StartsWith("System.Collections.Immutable", StringCompari
 0120            {
 0121                return false;
 122            }
 123
 0124            switch (GetBaseNameFromGenericType(type))
 125            {
 126                case ImmutableDictionaryGenericTypeName:
 127                case ImmutableDictionaryGenericInterfaceTypeName:
 128                case ImmutableSortedDictionaryGenericTypeName:
 0129                    return true;
 130                default:
 0131                    return false;
 132            }
 0133        }
 134
 135        public static bool IsImmutableEnumerableType(this Type type)
 0136        {
 0137            if (!type.IsGenericType || !type.Assembly.FullName!.StartsWith("System.Collections.Immutable", StringCompari
 0138            {
 0139                return false;
 140            }
 141
 0142            switch (GetBaseNameFromGenericType(type))
 143            {
 144                case ImmutableArrayGenericTypeName:
 145                case ImmutableListGenericTypeName:
 146                case ImmutableListGenericInterfaceTypeName:
 147                case ImmutableStackGenericTypeName:
 148                case ImmutableStackGenericInterfaceTypeName:
 149                case ImmutableQueueGenericTypeName:
 150                case ImmutableQueueGenericInterfaceTypeName:
 151                case ImmutableSortedSetGenericTypeName:
 152                case ImmutableHashSetGenericTypeName:
 153                case ImmutableSetGenericInterfaceTypeName:
 0154                    return true;
 155                default:
 0156                    return false;
 157            }
 0158        }
 159
 160        public static string? GetImmutableDictionaryConstructingTypeName(this Type type)
 0161        {
 0162            Debug.Assert(type.IsImmutableDictionaryType());
 163
 164            // Use the generic type definition of the immutable collection to determine
 165            // an appropriate constructing type, i.e. a type that we can invoke the
 166            // `CreateRange<T>` method on, which returns the desired immutable collection.
 0167            switch (GetBaseNameFromGenericType(type))
 168            {
 169                case ImmutableDictionaryGenericTypeName:
 170                case ImmutableDictionaryGenericInterfaceTypeName:
 0171                    return ImmutableDictionaryTypeName;
 172                case ImmutableSortedDictionaryGenericTypeName:
 0173                    return ImmutableSortedDictionaryTypeName;
 174                default:
 175                    // We verified that the type is an immutable collection, so the
 176                    // generic definition is one of the above.
 0177                    return null;
 178            }
 0179        }
 180
 181        public static string? GetImmutableEnumerableConstructingTypeName(this Type type)
 0182        {
 0183            Debug.Assert(type.IsImmutableEnumerableType());
 184
 185            // Use the generic type definition of the immutable collection to determine
 186            // an appropriate constructing type, i.e. a type that we can invoke the
 187            // `CreateRange<T>` method on, which returns the desired immutable collection.
 0188            switch (GetBaseNameFromGenericType(type))
 189            {
 190                case ImmutableArrayGenericTypeName:
 0191                    return ImmutableArrayTypeName;
 192                case ImmutableListGenericTypeName:
 193                case ImmutableListGenericInterfaceTypeName:
 0194                    return ImmutableListTypeName;
 195                case ImmutableStackGenericTypeName:
 196                case ImmutableStackGenericInterfaceTypeName:
 0197                    return ImmutableStackTypeName;
 198                case ImmutableQueueGenericTypeName:
 199                case ImmutableQueueGenericInterfaceTypeName:
 0200                    return ImmutableQueueTypeName;
 201                case ImmutableSortedSetGenericTypeName:
 0202                    return ImmutableSortedSetTypeName;
 203                case ImmutableHashSetGenericTypeName:
 204                case ImmutableSetGenericInterfaceTypeName:
 0205                    return ImmutableHashSetTypeName;
 206                default:
 207                    // We verified that the type is an immutable collection, so the
 208                    // generic definition is one of the above.
 0209                    return null;
 210            }
 0211        }
 212
 213        private static string GetBaseNameFromGenericType(Type genericType)
 0214        {
 0215            Type genericTypeDef = genericType.GetGenericTypeDefinition();
 0216            return genericTypeDef.FullName!;
 0217        }
 218
 219        public static bool IsVirtual(this PropertyInfo propertyInfo)
 6285220        {
 6285221            return propertyInfo.GetMethod?.IsVirtual == true || propertyInfo.SetMethod?.IsVirtual == true;
 6285222        }
 223
 224        public static bool IsKeyValuePair(this Type type)
 496225            => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>);
 226
 227        public static bool TryGetDeserializationConstructor(
 228            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTyp
 229            this Type type,
 230            bool useDefaultCtorInAnnotatedStructs,
 231            out ConstructorInfo? deserializationCtor)
 496232        {
 496233            ConstructorInfo? ctorWithAttribute = null;
 496234            ConstructorInfo? publicParameterlessCtor = null;
 496235            ConstructorInfo? lonePublicCtor = null;
 236
 496237            ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
 238
 496239            if (constructors.Length == 1)
 496240            {
 496241                lonePublicCtor = constructors[0];
 496242            }
 243
 2480244            foreach (ConstructorInfo constructor in constructors)
 496245            {
 496246                if (HasJsonConstructorAttribute(constructor))
 0247                {
 0248                    if (ctorWithAttribute != null)
 0249                    {
 0250                        deserializationCtor = null;
 0251                        return false;
 252                    }
 253
 0254                    ctorWithAttribute = constructor;
 0255                }
 496256                else if (constructor.GetParameters().Length == 0)
 163257                {
 163258                    publicParameterlessCtor = constructor;
 163259                }
 496260            }
 261
 262            // Search for non-public ctors with [JsonConstructor].
 2154263            foreach (ConstructorInfo constructor in type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)
 333264            {
 333265                if (HasJsonConstructorAttribute(constructor))
 0266                {
 0267                    if (ctorWithAttribute != null)
 0268                    {
 0269                        deserializationCtor = null;
 0270                        return false;
 271                    }
 272
 0273                    ctorWithAttribute = constructor;
 0274                }
 333275            }
 276
 277            // Structs will use default constructor if attribute isn't used.
 496278            if (useDefaultCtorInAnnotatedStructs && type.IsValueType && ctorWithAttribute == null)
 0279            {
 0280                deserializationCtor = null;
 0281                return true;
 282            }
 283
 496284            deserializationCtor = ctorWithAttribute ?? publicParameterlessCtor ?? lonePublicCtor;
 496285            return true;
 496286        }
 287
 288        public static object? GetDefaultValue(this ParameterInfo parameterInfo)
 1998289        {
 1998290            Type parameterType = parameterInfo.ParameterType;
 1998291            object? defaultValue = parameterInfo.DefaultValue;
 292
 1998293            if (defaultValue is null)
 0294            {
 0295                return null;
 296            }
 297
 298            // DBNull.Value is sometimes used as the default value (returned by reflection) of nullable params in place 
 1998299            if (defaultValue == DBNull.Value && parameterType != typeof(DBNull))
 1998300            {
 1998301                return null;
 302            }
 303
 304            // Default values of enums or nullable enums are represented using the underlying type and need to be cast e
 305            // cf. https://github.com/dotnet/runtime/issues/68647
 0306            if (parameterType.IsEnum)
 0307            {
 0308                return Enum.ToObject(parameterType, defaultValue);
 309            }
 310
 0311            if (Nullable.GetUnderlyingType(parameterType) is Type underlyingType && underlyingType.IsEnum)
 0312            {
 0313                return Enum.ToObject(underlyingType, defaultValue);
 314            }
 315
 0316            return defaultValue;
 1998317        }
 318
 319        /// <summary>
 320        /// Returns the type hierarchy for the given type, starting from the current type up to the base type(s) in the 
 321        /// Interface hierarchies with multiple inheritance will return results using topological sorting.
 322        /// </summary>
 323        [RequiresUnreferencedCode("Should only be used by the reflection-based serializer.")]
 324        public static Type[] GetSortedTypeHierarchy(this Type type)
 496325        {
 496326            if (!type.IsInterface)
 496327            {
 328                // Non-interface hierarchies are linear, just walk up to the earliest ancestor.
 329
 496330                var results = new List<Type>();
 2976331                for (Type? current = type; current != null; current = current.BaseType)
 992332                {
 992333                    results.Add(current);
 992334                }
 335
 496336                return results.ToArray();
 337            }
 338            else
 0339            {
 340                // Interface hierarchies support multiple inheritance.
 341                // For consistency with class hierarchy resolution order,
 342                // sort topologically from most derived to least derived.
 0343                return JsonHelpers.TraverseGraphWithTopologicalSort(type, static t => t.GetInterfaces());
 344            }
 496345        }
 346    }
 347}
 348

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Reflection;
 8using System.Runtime.CompilerServices;
 9using System.Runtime.ExceptionServices;
 10using System.Text.Json.Serialization;
 11
 12namespace System.Text.Json.Reflection
 13{
 14    internal static partial class ReflectionExtensions
 15    {
 16        /// <summary>
 17        /// Returns <see langword="true" /> when the given type is of type <see cref="Nullable{T}"/>.
 18        /// </summary>
 19        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 20        public static bool IsNullableOfT(this Type type) =>
 1148821            type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
 22
 199823        public static bool IsNullableType(this Type type) => !type.IsValueType || IsNullableOfT(type);
 24
 25        /// <summary>
 26        /// Returns <see langword="true" /> when the given type is assignable from <paramref name="from"/> including sup
 27        /// when <paramref name="from"/> is <see cref="Nullable{T}"/> by using the {T} generic parameter for <paramref n
 28        /// </summary>
 29        public static bool IsAssignableFromInternal(this Type type, Type from)
 414730        {
 414731            if (IsNullableOfT(from) && type.IsInterface)
 032            {
 033                return type.IsAssignableFrom(from.GetGenericArguments()[0]);
 34            }
 35
 414736            return type.IsAssignableFrom(from);
 414737        }
 38
 39        /// <summary>
 40        /// Returns <see langword="true" /> when either type is assignable to the other.
 41        /// </summary>
 42        public static bool IsInSubtypeRelationshipWith(this Type type, Type other) =>
 414743            type.IsAssignableFromInternal(other) || other.IsAssignableFromInternal(type);
 44
 45        private static bool HasJsonConstructorAttribute(ConstructorInfo constructorInfo)
 82946            => constructorInfo.IsDefined(typeof(JsonConstructorAttribute), inherit: false);
 47
 48        public static bool HasRequiredMemberAttribute(this MemberInfo memberInfo)
 49649        {
 50            // For compiler related attributes we should only look at full type name rather than trying to do something 
 51            // I.e. library is targeting netstandard2.0 with polyfilled attributes and is being consumed by an app targe
 49652            return memberInfo.HasCustomAttributeWithName("System.Runtime.CompilerServices.RequiredMemberAttribute", inhe
 49653        }
 54
 55        public static bool HasSetsRequiredMembersAttribute(this MemberInfo memberInfo)
 49656        {
 57            // See comment for HasRequiredMemberAttribute for why we need to always only look at full name
 49658            return memberInfo.HasCustomAttributeWithName("System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute",
 49659        }
 60
 61        private static bool HasCustomAttributeWithName(this MemberInfo memberInfo, string fullName, bool inherit)
 99262        {
 297663            foreach (object attribute in memberInfo.GetCustomAttributes(inherit))
 064            {
 065                if (attribute.GetType().FullName == fullName)
 066                {
 067                    return true;
 68                }
 069            }
 70
 99271            return false;
 99272        }
 73
 74        public static TAttribute? GetUniqueCustomAttribute<TAttribute>(this MemberInfo memberInfo, bool inherit)
 75            where TAttribute : Attribute
 2055676        {
 2055677            object[] attributes = memberInfo.GetCustomAttributes(typeof(TAttribute), inherit);
 78
 2055679            if (attributes.Length == 0)
 2047080            {
 2047081                return null;
 82            }
 83
 8684            if (attributes.Length == 1)
 8685            {
 8686                return (TAttribute)attributes[0];
 87            }
 88
 089            ThrowHelper.ThrowInvalidOperationException_SerializationDuplicateAttribute(typeof(TAttribute), memberInfo);
 90            return null;
 2055691        }
 92
 93        /// <summary>
 94        /// Polyfill for BindingFlags.DoNotWrapExceptions
 95        /// </summary>
 96        public static object? CreateInstanceNoWrapExceptions(
 97            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTyp
 98            Type[] parameterTypes,
 99            object?[] parameters)
 681100        {
 681101            ConstructorInfo ctorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.I
 102#if NET
 681103            return ctorInfo.Invoke(BindingFlags.DoNotWrapExceptions, null, parameters, null);
 104#else
 105            try
 106            {
 107                return ctorInfo.Invoke(parameters);
 108            }
 109            catch (TargetInvocationException ex) when (ex.InnerException is not null)
 110            {
 111                ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
 112                throw; // unreachable
 113            }
 114#endif
 681115        }
 116
 117        /// <summary>
 118        /// Invokes <paramref name="methodInfo"/> without wrapping any exception thrown by the
 119        /// target method in a <see cref="TargetInvocationException"/>. This matches the behavior of
 120        /// the Reflection.Emit-based accessor, which emits direct calls into user code.
 121        /// </summary>
 122        public static object? InvokeNoWrapExceptions(this MethodInfo methodInfo, object? obj, object?[]? parameters)
 123        {
 124#if NET
 125            return methodInfo.Invoke(obj, BindingFlags.DoNotWrapExceptions, binder: null, parameters, culture: null);
 126#else
 127            try
 128            {
 129                return methodInfo.Invoke(obj, parameters);
 130            }
 131            catch (TargetInvocationException ex) when (ex.InnerException is not null)
 132            {
 133                ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
 134                throw; // unreachable
 135            }
 136#endif
 137        }
 138
 139        /// <summary>
 140        /// Invokes <paramref name="constructorInfo"/> without wrapping any exception thrown by the
 141        /// constructor in a <see cref="TargetInvocationException"/>. This matches the behavior of
 142        /// the Reflection.Emit-based accessor, which emits direct calls into user code.
 143        /// </summary>
 144        public static object InvokeNoWrapExceptions(this ConstructorInfo constructorInfo, object?[]? parameters)
 145        {
 146#if NET
 147            return constructorInfo.Invoke(BindingFlags.DoNotWrapExceptions, binder: null, parameters, culture: null);
 148#else
 149            try
 150            {
 151                return constructorInfo.Invoke(parameters);
 152            }
 153            catch (TargetInvocationException ex) when (ex.InnerException is not null)
 154            {
 155                ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
 156                throw; // unreachable
 157            }
 158#endif
 159        }
 160
 161        /// <summary>
 162        /// Enumerates every ancestor of <paramref name="type"/> whose generic type definition
 163        /// matches <paramref name="baseTypeDefinition"/>. For interface bases this yields every
 164        /// implementing instantiation (a type can implement the same interface definition with
 165        /// different type arguments); for class bases it yields at most the first match found
 166        /// while walking the base-type chain (only one such instantiation is reachable).
 167        ///
 168        /// IMPORTANT: This implementation mirrors
 169        /// <c>System.Text.Json.SourceGeneration.RoslynExtensions.GetCompatibleGenericBaseTypes</c>
 170        /// in gen/Helpers/RoslynExtensions.cs. Any change to the enumeration order or matching
 171        /// rules MUST be applied on both sides to keep reflection and source-gen behaviour in sync.
 172        /// </summary>
 173        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
 174            Justification = "The derived type was supplied via [JsonDerivedType] by the user, so its interface " +
 175                            "metadata is rooted at the attribute usage site and survives trimming. Callers are " +
 176                            "additionally annotated [RequiresUnreferencedCode] to flow this requirement outward.")]
 177        public static IEnumerable<Type> GetMatchingGenericBaseTypes(this Type type, Type baseTypeDefinition)
 0178        {
 0179            Debug.Assert(baseTypeDefinition.IsGenericTypeDefinition);
 180
 0181            if (baseTypeDefinition.IsInterface)
 0182            {
 0183                foreach (Type iface in type.GetInterfaces())
 0184                {
 0185                    if (iface.IsGenericType && iface.GetGenericTypeDefinition() == baseTypeDefinition)
 0186                    {
 0187                        yield return iface;
 0188                    }
 0189                }
 190
 191                // Note: do NOT yield break here. Type.GetInterfaces() does not include `type`
 192                // itself, so when `type` IS the interface we're looking for, the fall-through
 193                // to the BaseType walk below picks it up via the self-check on the first
 194                // iteration (Type.BaseType returns null for interfaces, so the loop
 195                // terminates immediately).
 0196            }
 197
 0198            for (Type? current = type; current is not null; current = current.BaseType)
 0199            {
 0200                if (current.IsGenericType && current.GetGenericTypeDefinition() == baseTypeDefinition)
 0201                {
 0202                    yield return current;
 0203                    yield break;
 204                }
 0205            }
 0206        }
 207
 208        /// <summary>
 209        /// Attempts to unify a <paramref name="pattern"/> type (which may contain generic
 210        /// parameter references) with a <paramref name="target"/> type, recording bindings in
 211        /// <paramref name="substitution"/>. Returns <see langword="true"/> if the pattern matches
 212        /// the target under some extension of the current substitution.
 213        ///
 214        /// IMPORTANT: This implementation MIRRORS
 215        /// <c>System.Text.Json.SourceGeneration.RoslynExtensions.TryUnifyWith</c> in
 216        /// gen/Helpers/RoslynExtensions.cs. Any structural change (e.g. new type-kind handling,
 217        /// refined array/pointer rules) MUST be applied on both sides to keep reflection and
 218        /// source-gen behaviour in sync. The two implementations are exercised by:
 219        ///   * tests/.../PolymorphicTests.CustomTypeHierarchies.cs (reflection)
 220        ///   * tests/.../JsonSourceGeneratorDiagnosticsTests.cs (source-gen)
 221        ///
 222        /// Known intentional asymmetries with the source-gen mirror:
 223        ///   * This implementation distinguishes SZ arrays (<c>T[]</c>) from rank-1
 224        ///     multi-dimensional arrays (<c>T[*]</c>) via <c>Type.IsSZArray</c> on .NET. Roslyn
 225        ///     surfaces both as <c>IArrayTypeSymbol</c> with <c>Rank == 1</c>, and C#
 226        ///     <c>typeof()</c> attribute syntax only produces SZ arrays, so the source-gen
 227        ///     mirror has no equivalent check.
 228        ///   * This implementation has a branch for <c>Type.IsByRef</c>; Roslyn never surfaces
 229        ///     ref types as generic arguments, so the source-gen mirror has no equivalent.
 230        /// </summary>
 231        public static bool TryUnifyWith(this Type pattern, Type target, IDictionary<Type, Type> substitution)
 0232        {
 0233            if (pattern.IsGenericParameter)
 0234            {
 0235                if (substitution.TryGetValue(pattern, out Type? existing))
 0236                {
 0237                    return existing == target;
 238                }
 239
 0240                substitution[pattern] = target;
 0241                return true;
 242            }
 243
 0244            if (pattern.IsArray)
 0245            {
 0246                if (!target.IsArray)
 0247                {
 0248                    return false;
 249                }
 250
 0251                if (pattern.GetArrayRank() != target.GetArrayRank())
 0252                {
 0253                    return false;
 254                }
 255
 256                // Distinguish single-dim zero-based arrays (T[]) from non-SZ rank-1 arrays (T[*]).
 257#if NET
 0258                if (pattern.IsSZArray != target.IsSZArray)
 0259                {
 0260                    return false;
 261                }
 262#endif
 263
 0264                return pattern.GetElementType()!.TryUnifyWith(target.GetElementType()!, substitution);
 265            }
 266
 0267            if (pattern.IsPointer)
 0268            {
 0269                if (!target.IsPointer)
 0270                {
 0271                    return false;
 272                }
 273
 0274                return pattern.GetElementType()!.TryUnifyWith(target.GetElementType()!, substitution);
 275            }
 276
 0277            if (pattern.IsByRef)
 0278            {
 0279                if (!target.IsByRef)
 0280                {
 0281                    return false;
 282                }
 283
 0284                return pattern.GetElementType()!.TryUnifyWith(target.GetElementType()!, substitution);
 285            }
 286
 0287            if (pattern.IsGenericType)
 0288            {
 0289                if (!target.IsGenericType || pattern.GetGenericTypeDefinition() != target.GetGenericTypeDefinition())
 0290                {
 0291                    return false;
 292                }
 293
 0294                Type[] patternArgs = pattern.GetGenericArguments();
 0295                Type[] targetArgs = target.GetGenericArguments();
 0296                if (patternArgs.Length != targetArgs.Length)
 0297                {
 0298                    return false;
 299                }
 300
 0301                for (int i = 0; i < patternArgs.Length; i++)
 0302                {
 0303                    if (!patternArgs[i].TryUnifyWith(targetArgs[i], substitution))
 0304                    {
 0305                        return false;
 306                    }
 0307                }
 308
 0309                return true;
 310            }
 311
 0312            return pattern == target;
 0313        }
 314
 315        // The following is a line-by-line port of the Roslyn C# compiler's own accessibility comparison â€” the
 316        // logic it runs to report the "inconsistent accessibility" diagnostics (CS0050/CS0060 and friends). An
 317        // inferred closed-hierarchy derived type is kept only when it is at least as visible as the base it is
 318        // registered under; otherwise there are call sites that can reference the base but not the derived type.
 319        // Rather than invent an accessibility metric (which would risk drifting from the language as new
 320        // modifiers are added), we reproduce the compiler's algorithm verbatim, using its terminology, so it
 321        // tracks C# accessibility as it evolves. The reflection resolver has no Roslyn Compilation, so the
 322        // algorithm is reproduced here over System.Type; gen/Helpers/RoslynExtensions.cs mirrors it over ISymbol.
 323        //
 324        // Ported from dotnet/roslyn @ 121e7dc868d26be12b9c3fb52b7b9d2ae41a1ac2:
 325        //   IsAtLeastAsVisibleAs / FindTypeLessVisibleThan / IsAsRestrictive:
 326        //     https://github.com/dotnet/roslyn/blob/121e7dc868d26be12b9c3fb52b7b9d2ae41a1ac2/src/Compilers/CSharp/Porta
 327        //   IsAccessibleViaInheritance:
 328        //     https://github.com/dotnet/roslyn/blob/121e7dc868d26be12b9c3fb52b7b9d2ae41a1ac2/src/Compilers/CSharp/Porta
 329        //   HasInternalAccessTo:
 330        //     https://github.com/dotnet/roslyn/blob/121e7dc868d26be12b9c3fb52b7b9d2ae41a1ac2/src/Compilers/CSharp/Porta
 331
 332        /// <summary>
 333        /// Determines whether <paramref name="type"/> is at least as visible as <paramref name="sym"/>. Port of
 334        /// Roslyn's <c>TypeSymbolExtensions.IsAtLeastAsVisibleAs</c>; because a closed hierarchy relates two
 335        /// named types, the compound-type traversal (<c>FindTypeLessVisibleThan</c>/<c>Symbol.VisitType</c>)
 336        /// reduces to the single <c>IsAsRestrictive</c> check.
 337        /// </summary>
 338        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 339        public static bool IsAtLeastAsVisibleAs(this Type type, Type sym)
 0340        {
 0341            return IsAsRestrictive(type, sym);
 342
 343            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 344            static bool IsAsRestrictive(Type s1, Type sym2)
 0345            {
 0346                Accessibility acc1 = GetDeclaredAccessibility(s1);
 347
 0348                if (acc1 == Accessibility.Public)
 0349                {
 0350                    return true;
 351                }
 352
 0353                for (Type? s2 = sym2; s2 is not null; s2 = s2.DeclaringType)
 0354                {
 0355                    Accessibility acc2 = GetDeclaredAccessibility(s2);
 356
 0357                    switch (acc1)
 358                    {
 359                        case Accessibility.Internal:
 360                            // If s2 is private or internal, and is in an assembly that gives s1's assembly internal
 361                            // access, then this is at least as restrictive as s1's internal.
 0362                            if (acc2 is Accessibility.Private or Accessibility.Internal or Accessibility.ProtectedAndInt
 0363                                HasInternalAccessTo(s2.Assembly, s1.Assembly))
 0364                            {
 0365                                return true;
 366                            }
 367
 0368                            break;
 369
 370                        case Accessibility.ProtectedAndInternal:
 371                            // Since s1 is private protected, s2 must be more restrictive than both internal and
 372                            // protected. Do the "internal" test first (as above); if it passes, fall through to the
 373                            // "protected" test.
 0374                            if (acc2 is Accessibility.Private or Accessibility.Internal or Accessibility.ProtectedAndInt
 0375                                HasInternalAccessTo(s2.Assembly, s1.Assembly))
 0376                            {
 0377                                goto case Accessibility.Protected;
 378                            }
 379
 0380                            break;
 381
 382                        case Accessibility.Protected:
 0383                        {
 0384                            Type? parent1 = s1.DeclaringType;
 385
 0386                            if (parent1 is null)
 0387                            {
 388                                // not helpful
 0389                            }
 0390                            else if (acc2 == Accessibility.Private)
 0391                            {
 392                                // if s2 is private and within s1's parent or within a subclass of s1's parent,
 393                                // then this is at least as restrictive as s1's protected.
 0394                                for (Type? parent2 = s2.DeclaringType; parent2 is not null; parent2 = parent2.DeclaringT
 0395                                {
 0396                                    if (IsAccessibleViaInheritance(parent1, parent2))
 0397                                    {
 0398                                        return true;
 399                                    }
 0400                                }
 0401                            }
 0402                            else if (acc2 is Accessibility.Protected or Accessibility.ProtectedAndInternal)
 0403                            {
 404                                // if s2 is protected, and its parent is a subclass of (or the same as) s1's
 405                                // parent, then this is at least as restrictive as s1's protected.
 0406                                Type? parent2 = s2.DeclaringType;
 0407                                if (parent2 is not null && IsAccessibleViaInheritance(parent1, parent2))
 0408                                {
 0409                                    return true;
 410                                }
 0411                            }
 412
 0413                            break;
 414                        }
 415
 416                        case Accessibility.ProtectedOrInternal:
 0417                        {
 0418                            Type? parent1 = s1.DeclaringType;
 419
 0420                            if (parent1 is null)
 0421                            {
 0422                                break;
 423                            }
 424
 0425                            switch (acc2)
 426                            {
 427                                case Accessibility.Private:
 428                                    // if s2 is private and within a subclass of s1's parent, or within the same
 429                                    // assembly as s1, then this is at least as restrictive as s1's protected internal.
 0430                                    if (HasInternalAccessTo(s2.Assembly, s1.Assembly))
 0431                                    {
 0432                                        return true;
 433                                    }
 434
 0435                                    for (Type? parent2 = s2.DeclaringType; parent2 is not null; parent2 = parent2.Declar
 0436                                    {
 0437                                        if (IsAccessibleViaInheritance(parent1, parent2))
 0438                                        {
 0439                                            return true;
 440                                        }
 0441                                    }
 442
 0443                                    break;
 444
 445                                case Accessibility.Internal:
 446                                    // If s2 is in an assembly that gives s1's assembly internal access, then this
 447                                    // is more restrictive than s1's protected internal.
 0448                                    if (HasInternalAccessTo(s2.Assembly, s1.Assembly))
 0449                                    {
 0450                                        return true;
 451                                    }
 452
 0453                                    break;
 454
 455                                case Accessibility.Protected:
 456                                    // if s2 is protected, and its parent is a subclass of (or the same as) s1's
 457                                    // parent, then this is at least as restrictive as s1's protected internal.
 0458                                    if (s2.DeclaringType is Type protectedParent2 && IsAccessibleViaInheritance(parent1,
 0459                                    {
 0460                                        return true;
 461                                    }
 462
 0463                                    break;
 464
 465                                case Accessibility.ProtectedAndInternal:
 466                                    // if s2 is private protected, and its parent is a subclass of (or the same as)
 467                                    // s1's parent, or it is in the same assembly as s1, then this is at least as
 468                                    // restrictive as s1's protected internal.
 0469                                    if (HasInternalAccessTo(s2.Assembly, s1.Assembly) ||
 0470                                        (s2.DeclaringType is Type privateProtectedParent2 && IsAccessibleViaInheritance(
 0471                                    {
 0472                                        return true;
 473                                    }
 474
 0475                                    break;
 476
 477                                case Accessibility.ProtectedOrInternal:
 478                                    // if s2 is protected internal, and its parent is a subclass of (or the same as)
 479                                    // s1's parent, and it is in the same assembly as s1, then this is at least as
 480                                    // restrictive as s1's protected internal.
 0481                                    if (HasInternalAccessTo(s2.Assembly, s1.Assembly) &&
 0482                                        s2.DeclaringType is Type protectedOrInternalParent2 && IsAccessibleViaInheritanc
 0483                                    {
 0484                                        return true;
 485                                    }
 486
 0487                                    break;
 488                            }
 489
 0490                            break;
 491                        }
 492
 493                        case Accessibility.Private:
 0494                            if (acc2 == Accessibility.Private)
 0495                            {
 496                                // if s2 is private, and it is within s1's parent, then this is at least as
 497                                // restrictive as s1's private.
 0498                                Type? parent1 = s1.DeclaringType;
 499
 0500                                if (parent1 is null)
 0501                                {
 0502                                    break;
 503                                }
 504
 0505                                Type parent1OriginalDefinition = OriginalDefinition(parent1);
 0506                                for (Type? parent2 = s2.DeclaringType; parent2 is not null; parent2 = parent2.DeclaringT
 0507                                {
 0508                                    if (ReferenceEquals(OriginalDefinition(parent2), parent1OriginalDefinition))
 0509                                    {
 0510                                        return true;
 511                                    }
 0512                                }
 0513                            }
 514
 0515                            break;
 516                    }
 0517                }
 518
 0519                return false;
 0520            }
 521
 522            [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 523            static bool IsAccessibleViaInheritance(Type superType, Type subType)
 0524            {
 0525                Type originalSuperType = OriginalDefinition(superType);
 0526                for (Type? current = subType; current is not null; current = current.BaseType)
 0527                {
 0528                    if (ReferenceEquals(OriginalDefinition(current), originalSuperType))
 0529                    {
 0530                        return true;
 531                    }
 0532                }
 533
 0534                if (originalSuperType.IsInterface)
 0535                {
 0536                    foreach (Type current in subType.GetInterfaces())
 0537                    {
 0538                        if (ReferenceEquals(OriginalDefinition(current), originalSuperType))
 0539                        {
 0540                            return true;
 541                        }
 0542                    }
 0543                }
 544
 0545                return false;
 0546            }
 547
 548            static bool HasInternalAccessTo(Assembly fromAssembly, Assembly toAssembly)
 0549            {
 0550                if (fromAssembly == toAssembly)
 0551                {
 0552                    return true;
 553                }
 554
 555                // Reflection analog of Roslyn's AreInternalsVisibleToThisAssembly. Closed hierarchies are compiled
 556                // into a single assembly, so this branch only matters for the general faithfulness of the port; the
 557                // friend assembly is matched by simple name (the runtime already enforced strong-name identity when
 558                // it loaded the types).
 0559                string? fromName = fromAssembly.GetName().Name;
 0560                foreach (CustomAttributeData attribute in toAssembly.GetCustomAttributesData())
 0561                {
 0562                    if (attribute.AttributeType == typeof(InternalsVisibleToAttribute) &&
 0563                        attribute.ConstructorArguments.Count > 0 &&
 0564                        attribute.ConstructorArguments[0].Value is string friendName)
 0565                    {
 0566                        int comma = friendName.IndexOf(',');
 0567                        string friendSimpleName = comma < 0 ? friendName : friendName.Substring(0, comma);
 0568                        if (string.Equals(friendSimpleName, fromName, StringComparison.OrdinalIgnoreCase))
 0569                        {
 0570                            return true;
 571                        }
 0572                    }
 0573                }
 574
 0575                return false;
 0576            }
 577
 578            static Type OriginalDefinition(Type type) =>
 0579                type.IsGenericType && !type.IsGenericTypeDefinition ? type.GetGenericTypeDefinition() : type;
 580
 581            static Accessibility GetDeclaredAccessibility(Type type)
 0582            {
 0583                if (type.IsPublic || type.IsNestedPublic)
 0584                {
 0585                    return Accessibility.Public;
 586                }
 587
 0588                if (type.IsNestedFamORAssem)
 0589                {
 0590                    return Accessibility.ProtectedOrInternal; // protected internal
 591                }
 592
 0593                if (type.IsNestedFamily)
 0594                {
 0595                    return Accessibility.Protected;
 596                }
 597
 0598                if (type.IsNestedFamANDAssem)
 0599                {
 0600                    return Accessibility.ProtectedAndInternal; // private protected
 601                }
 602
 0603                if (type.IsNestedPrivate)
 0604                {
 0605                    return Accessibility.Private;
 606                }
 607
 608                // Top-level non-public (IsNotPublic) and nested assembly (IsNestedAssembly) are both 'internal'.
 0609                return Accessibility.Internal;
 0610            }
 0611        }
 612
 613        /// <summary>
 614        /// Mirrors the members of Roslyn's <see cref="T:Microsoft.CodeAnalysis.Accessibility"/> that a C# type
 615        /// declaration can have, so the ported accessibility comparison uses the compiler's terminology.
 616        /// </summary>
 617        private enum Accessibility
 618        {
 619            Private,
 620            ProtectedAndInternal, // private protected
 621            Protected,
 622            Internal,
 623            ProtectedOrInternal, // protected internal
 624            Public,
 625        }
 626    }
 627}
 628

Methods/Properties

GetCompatibleGenericBaseClass(System.Type,System.Type)
GetCompatibleGenericInterface(System.Type,System.Type)
IsImmutableDictionaryType(System.Type)
IsImmutableEnumerableType(System.Type)
GetImmutableDictionaryConstructingTypeName(System.Type)
GetImmutableEnumerableConstructingTypeName(System.Type)
GetBaseNameFromGenericType(System.Type)
IsVirtual(System.Reflection.PropertyInfo)
IsKeyValuePair(System.Type)
TryGetDeserializationConstructor(System.Type,System.Boolean,System.Reflection.ConstructorInfo&)
GetDefaultValue(System.Reflection.ParameterInfo)
GetSortedTypeHierarchy(System.Type)
IsNullableOfT(System.Type)
IsNullableType(System.Type)
IsAssignableFromInternal(System.Type,System.Type)
IsInSubtypeRelationshipWith(System.Type,System.Type)
HasJsonConstructorAttribute(System.Reflection.ConstructorInfo)
HasRequiredMemberAttribute(System.Reflection.MemberInfo)
HasSetsRequiredMembersAttribute(System.Reflection.MemberInfo)
HasCustomAttributeWithName(System.Reflection.MemberInfo,System.String,System.Boolean)
GetUniqueCustomAttribute(System.Reflection.MemberInfo,System.Boolean)
CreateInstanceNoWrapExceptions(System.Type,System.Type[],System.Object[])
GetMatchingGenericBaseTypes()
TryUnifyWith(System.Type,System.Type,System.Collections.Generic.IDictionary`2<System.Type,System.Type>)
IsAtLeastAsVisibleAs(System.Type,System.Type)
IsAsRestrictive(System.Type,System.Type)
IsAccessibleViaInheritance(System.Type,System.Type)
HasInternalAccessTo(System.Reflection.Assembly,System.Reflection.Assembly)
OriginalDefinition(System.Type)
GetDeclaredAccessibility(System.Type)