File: Helpers\KnownTypeSymbols.cs
Web Access
Project: src\runtime\src\libraries\System.Text.Json\gen\System.Text.Json.SourceGeneration.Roslyn4.4.csproj (System.Text.Json.SourceGeneration)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.DotnetRuntime.Extensions;

namespace System.Text.Json.SourceGeneration
{
    internal sealed class KnownTypeSymbols
    {
        public KnownTypeSymbols(Compilation compilation)
            => Compilation = compilation;

        public Compilation Compilation { get; }

        // Caches a set of types with built-in converter support. Populated by the Parser class.
        public HashSet<ITypeSymbol>? BuiltInSupportTypes { get; set; }

        public INamedTypeSymbol? IListOfTType => GetOrResolveType(typeof(IList<>), ref _IListOfTType);
        private Option<INamedTypeSymbol?> _IListOfTType;

        public INamedTypeSymbol? ICollectionOfTType => GetOrResolveType(typeof(ICollection<>), ref _ICollectionOfTType);
        private Option<INamedTypeSymbol?> _ICollectionOfTType;

        public INamedTypeSymbol? IEnumerableType => GetOrResolveType(typeof(IEnumerable), ref _IEnumerableType);
        private Option<INamedTypeSymbol?> _IEnumerableType;

        public INamedTypeSymbol? IEnumerableOfTType => GetOrResolveType(typeof(IEnumerable<>), ref _IEnumerableOfTType);
        private Option<INamedTypeSymbol?> _IEnumerableOfTType;

        public INamedTypeSymbol? ListOfTType => GetOrResolveType(typeof(List<>), ref _ListOfTType);
        private Option<INamedTypeSymbol?> _ListOfTType;

        public INamedTypeSymbol? DictionaryOfTKeyTValueType => GetOrResolveType(typeof(Dictionary<,>), ref _DictionaryOfTKeyTValueType);
        private Option<INamedTypeSymbol?> _DictionaryOfTKeyTValueType;

        public INamedTypeSymbol? IAsyncEnumerableOfTType => GetOrResolveType("System.Collections.Generic.IAsyncEnumerable`1", ref _AsyncEnumerableOfTType);
        private Option<INamedTypeSymbol?> _AsyncEnumerableOfTType;

        public INamedTypeSymbol? IDictionaryOfTKeyTValueType => GetOrResolveType(typeof(IDictionary<,>), ref _IDictionaryOfTKeyTValueType);
        private Option<INamedTypeSymbol?> _IDictionaryOfTKeyTValueType;

        public INamedTypeSymbol? IReadonlyDictionaryOfTKeyTValueType => GetOrResolveType(typeof(IReadOnlyDictionary<,>), ref _IReadonlyDictionaryOfTKeyTValueType);
        private Option<INamedTypeSymbol?> _IReadonlyDictionaryOfTKeyTValueType;

        public INamedTypeSymbol? ISetOfTType => GetOrResolveType(typeof(ISet<>), ref _ISetOfTType);
        private Option<INamedTypeSymbol?> _ISetOfTType;

        public INamedTypeSymbol? IReadOnlySetOfTType => GetOrResolveType("System.Collections.Generic.IReadOnlySet`1", ref _IReadOnlySetOfTType);
        private Option<INamedTypeSymbol?> _IReadOnlySetOfTType;

        public INamedTypeSymbol? StackOfTType => GetOrResolveType(typeof(Stack<>), ref _StackOfTType);
        private Option<INamedTypeSymbol?> _StackOfTType;

        public INamedTypeSymbol? QueueOfTType => GetOrResolveType(typeof(Queue<>), ref _QueueOfTType);
        private Option<INamedTypeSymbol?> _QueueOfTType;

        public INamedTypeSymbol? ConcurrentStackType => GetOrResolveType(typeof(ConcurrentStack<>), ref _ConcurrentStackType);
        private Option<INamedTypeSymbol?> _ConcurrentStackType;

        public INamedTypeSymbol? ConcurrentQueueType => GetOrResolveType(typeof(ConcurrentQueue<>), ref _ConcurrentQueueType);
        private Option<INamedTypeSymbol?> _ConcurrentQueueType;

        public INamedTypeSymbol? IDictionaryType => GetOrResolveType(typeof(IDictionary), ref _IDictionaryType);
        private Option<INamedTypeSymbol?> _IDictionaryType;

        public INamedTypeSymbol? IListType => GetOrResolveType(typeof(IList), ref _IListType);
        private Option<INamedTypeSymbol?> _IListType;

        public INamedTypeSymbol? StackType => GetOrResolveType(typeof(Stack), ref _StackType);
        private Option<INamedTypeSymbol?> _StackType;

        public INamedTypeSymbol? QueueType => GetOrResolveType(typeof(Queue), ref _QueueType);
        private Option<INamedTypeSymbol?> _QueueType;

        public INamedTypeSymbol? KeyValuePair => GetOrResolveType(typeof(KeyValuePair<,>), ref _KeyValuePair);
        private Option<INamedTypeSymbol?> _KeyValuePair;

        public INamedTypeSymbol? ImmutableArrayType => GetOrResolveType(typeof(ImmutableArray<>), ref _ImmutableArrayType);
        private Option<INamedTypeSymbol?> _ImmutableArrayType;

        public INamedTypeSymbol? ImmutableListType => GetOrResolveType(typeof(ImmutableList<>), ref _ImmutableListType);
        private Option<INamedTypeSymbol?> _ImmutableListType;

        public INamedTypeSymbol? IImmutableListType => GetOrResolveType(typeof(IImmutableList<>), ref _IImmutableListType);
        private Option<INamedTypeSymbol?> _IImmutableListType;

        public INamedTypeSymbol? ImmutableStackType => GetOrResolveType(typeof(ImmutableStack<>), ref _ImmutableStackType);
        private Option<INamedTypeSymbol?> _ImmutableStackType;

        public INamedTypeSymbol? IImmutableStackType => GetOrResolveType(typeof(IImmutableStack<>), ref _IImmutableStackType);
        private Option<INamedTypeSymbol?> _IImmutableStackType;

        public INamedTypeSymbol? ImmutableQueueType => GetOrResolveType(typeof(ImmutableQueue<>), ref _ImmutableQueueType);
        private Option<INamedTypeSymbol?> _ImmutableQueueType;

        public INamedTypeSymbol? IImmutableQueueType => GetOrResolveType(typeof(IImmutableQueue<>), ref _IImmutableQueueType);
        private Option<INamedTypeSymbol?> _IImmutableQueueType;

        public INamedTypeSymbol? ImmutableSortedType => GetOrResolveType(typeof(ImmutableSortedSet<>), ref _ImmutableSortedType);
        private Option<INamedTypeSymbol?> _ImmutableSortedType;

        public INamedTypeSymbol? ImmutableHashSetType => GetOrResolveType(typeof(ImmutableHashSet<>), ref _ImmutableHashSetType);
        private Option<INamedTypeSymbol?> _ImmutableHashSetType;

        public INamedTypeSymbol? IImmutableSetType => GetOrResolveType(typeof(IImmutableSet<>), ref _IImmutableSetType);
        private Option<INamedTypeSymbol?> _IImmutableSetType;

        public INamedTypeSymbol? ImmutableDictionaryType => GetOrResolveType(typeof(ImmutableDictionary<,>), ref _ImmutableDictionaryType);
        private Option<INamedTypeSymbol?> _ImmutableDictionaryType;

        public INamedTypeSymbol? ImmutableSortedDictionaryType => GetOrResolveType(typeof(ImmutableSortedDictionary<,>), ref _ImmutableSortedDictionaryType);
        private Option<INamedTypeSymbol?> _ImmutableSortedDictionaryType;

        public INamedTypeSymbol? IImmutableDictionaryType => GetOrResolveType(typeof(IImmutableDictionary<,>), ref _IImmutableDictionaryType);
        private Option<INamedTypeSymbol?> _IImmutableDictionaryType;

        public INamedTypeSymbol? KeyedCollectionType => GetOrResolveType(typeof(KeyedCollection<,>), ref _KeyedCollectionType);
        private Option<INamedTypeSymbol?> _KeyedCollectionType;

        public INamedTypeSymbol ObjectType => _ObjectType ??= Compilation.GetSpecialType(SpecialType.System_Object);
        private INamedTypeSymbol? _ObjectType;

        public INamedTypeSymbol StringType => _StringType ??= Compilation.GetSpecialType(SpecialType.System_String);
        private INamedTypeSymbol? _StringType;

        public INamedTypeSymbol? DateTimeOffsetType => GetOrResolveType(typeof(DateTimeOffset), ref _DateTimeOffsetType);
        private Option<INamedTypeSymbol?> _DateTimeOffsetType;

        public INamedTypeSymbol? TimeSpanType => GetOrResolveType(typeof(TimeSpan), ref _TimeSpanType);
        private Option<INamedTypeSymbol?> _TimeSpanType;

        public INamedTypeSymbol? DateOnlyType => GetOrResolveType("System.DateOnly", ref _DateOnlyType);
        private Option<INamedTypeSymbol?> _DateOnlyType;

        public INamedTypeSymbol? TimeOnlyType => GetOrResolveType("System.TimeOnly", ref _TimeOnlyType);
        private Option<INamedTypeSymbol?> _TimeOnlyType;

        public INamedTypeSymbol? Int128Type => GetOrResolveType("System.Int128", ref _Int128Type);
        private Option<INamedTypeSymbol?> _Int128Type;

        public INamedTypeSymbol? UInt128Type => GetOrResolveType("System.UInt128", ref _UInt128Type);
        private Option<INamedTypeSymbol?> _UInt128Type;

        public INamedTypeSymbol? HalfType => GetOrResolveType("System.Half", ref _HalfType);
        private Option<INamedTypeSymbol?> _HalfType;

        public IArrayTypeSymbol? ByteArrayType => _ByteArrayType.HasValue
            ? _ByteArrayType.Value
            : (_ByteArrayType = new(Compilation.CreateArrayTypeSymbol(Compilation.GetSpecialType(SpecialType.System_Byte), rank: 1))).Value;

        private Option<IArrayTypeSymbol?> _ByteArrayType;

        public INamedTypeSymbol? MemoryByteType => _MemoryByteType.HasValue
            ? _MemoryByteType.Value
            : (_MemoryByteType = new(MemoryType?.Construct(Compilation.GetSpecialType(SpecialType.System_Byte)))).Value;

        private Option<INamedTypeSymbol?> _MemoryByteType;

        public INamedTypeSymbol? ReadOnlyMemoryByteType => _ReadOnlyMemoryByteType.HasValue
            ? _ReadOnlyMemoryByteType.Value
            : (_ReadOnlyMemoryByteType = new(ReadOnlyMemoryType?.Construct(Compilation.GetSpecialType(SpecialType.System_Byte)))).Value;

        private Option<INamedTypeSymbol?> _ReadOnlyMemoryByteType;

        public INamedTypeSymbol? GuidType => GetOrResolveType(typeof(Guid), ref _GuidType);
        private Option<INamedTypeSymbol?> _GuidType;

        public INamedTypeSymbol? UriType => GetOrResolveType(typeof(Uri), ref _UriType);
        private Option<INamedTypeSymbol?> _UriType;

        public INamedTypeSymbol? VersionType => GetOrResolveType(typeof(Version), ref _VersionType);
        private Option<INamedTypeSymbol?> _VersionType;

        // System.Text.Json types
        public INamedTypeSymbol? JsonConverterType => GetOrResolveType("System.Text.Json.Serialization.JsonConverter", ref _JsonConverterType);
        private Option<INamedTypeSymbol?> _JsonConverterType;

        public INamedTypeSymbol? JsonTypeClassifierFactoryType => GetOrResolveType("System.Text.Json.Serialization.JsonTypeClassifierFactory", ref _JsonTypeClassifierFactoryType);
        private Option<INamedTypeSymbol?> _JsonTypeClassifierFactoryType;

        public INamedTypeSymbol? JsonSerializerContextType => GetOrResolveType("System.Text.Json.Serialization.JsonSerializerContext", ref _JsonSerializerContextType);
        private Option<INamedTypeSymbol?> _JsonSerializerContextType;

        public INamedTypeSymbol? JsonSerializableAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonSerializableAttribute", ref _JsonSerializableAttributeType);
        private Option<INamedTypeSymbol?> _JsonSerializableAttributeType;

        public INamedTypeSymbol? JsonDocumentType => GetOrResolveType("System.Text.Json.JsonDocument", ref _JsonDocumentType);
        private Option<INamedTypeSymbol?> _JsonDocumentType;

        public INamedTypeSymbol? JsonElementType => GetOrResolveType("System.Text.Json.JsonElement", ref _JsonElementType);
        private Option<INamedTypeSymbol?> _JsonElementType;

        public INamedTypeSymbol? StringObjectDictionaryType => _StringObjectDictionaryType.HasValue
            ? _StringObjectDictionaryType.Value
            : (_StringObjectDictionaryType = new(DictionaryOfTKeyTValueType?.Construct(StringType, ObjectType))).Value;
        private Option<INamedTypeSymbol?> _StringObjectDictionaryType;

        public INamedTypeSymbol? StringJsonElementDictionaryType => _StringJsonElementDictionaryType.HasValue
            ? _StringJsonElementDictionaryType.Value
            : (_StringJsonElementDictionaryType = new(DictionaryOfTKeyTValueType is { } dictType && JsonElementType is { } jsonElemType
                ? dictType.Construct(StringType, jsonElemType)
                : null)).Value;
        private Option<INamedTypeSymbol?> _StringJsonElementDictionaryType;

        public INamedTypeSymbol? JsonNodeType => GetOrResolveType("System.Text.Json.Nodes.JsonNode", ref _JsonNodeType);
        private Option<INamedTypeSymbol?> _JsonNodeType;

        public INamedTypeSymbol? JsonValueType => GetOrResolveType("System.Text.Json.Nodes.JsonValue", ref _JsonValueType);
        private Option<INamedTypeSymbol?> _JsonValueType;

        public INamedTypeSymbol? JsonObjectType => GetOrResolveType("System.Text.Json.Nodes.JsonObject", ref _JsonObjectType);
        private Option<INamedTypeSymbol?> _JsonObjectType;

        public INamedTypeSymbol? JsonArrayType => GetOrResolveType("System.Text.Json.Nodes.JsonArray", ref _JsonArrayType);
        private Option<INamedTypeSymbol?> _JsonArrayType;

        // System.Text.Json attributes
        public INamedTypeSymbol? JsonConverterAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonConverterAttribute", ref _JsonConverterAttributeType);
        private Option<INamedTypeSymbol?> _JsonConverterAttributeType;

        public INamedTypeSymbol? JsonDerivedTypeAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonDerivedTypeAttribute", ref _JsonDerivedTypeAttributeType);
        private Option<INamedTypeSymbol?> _JsonDerivedTypeAttributeType;

        public INamedTypeSymbol? JsonIgnoreAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonIgnoreAttribute", ref _JsonIgnoreAttributeType);
        private Option<INamedTypeSymbol?> _JsonIgnoreAttributeType;

        public INamedTypeSymbol? JsonPolymorphicAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonPolymorphicAttribute", ref _JsonPolymorphicAttributeType);
        private Option<INamedTypeSymbol?> _JsonPolymorphicAttributeType;

        public INamedTypeSymbol? JsonUnionAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonUnionAttribute", ref _JsonUnionAttributeType);
        private Option<INamedTypeSymbol?> _JsonUnionAttributeType;

        public INamedTypeSymbol? JsonNumberHandlingAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonNumberHandlingAttribute", ref _JsonNumberHandlingAttributeType);
        private Option<INamedTypeSymbol?> _JsonNumberHandlingAttributeType;

        public INamedTypeSymbol? JsonNamingPolicyAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonNamingPolicyAttribute", ref _JsonNamingPolicyAttributeType);
        private Option<INamedTypeSymbol?> _JsonNamingPolicyAttributeType;

        public INamedTypeSymbol? JsonObjectCreationHandlingAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonObjectCreationHandlingAttribute", ref _JsonObjectCreationHandlingAttributeType);
        private Option<INamedTypeSymbol?> _JsonObjectCreationHandlingAttributeType;

        public INamedTypeSymbol? JsonSourceGenerationOptionsAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonSourceGenerationOptionsAttribute", ref _JsonSourceGenerationOptionsAttributeType);
        private Option<INamedTypeSymbol?> _JsonSourceGenerationOptionsAttributeType;

        public INamedTypeSymbol? JsonUnmappedMemberHandlingAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonUnmappedMemberHandlingAttribute", ref _JsonUnmappedMemberHandlingAttributeType);
        private Option<INamedTypeSymbol?> _JsonUnmappedMemberHandlingAttributeType;

        public INamedTypeSymbol? JsonConstructorAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonConstructorAttribute", ref _JsonConstructorAttributeType);
        private Option<INamedTypeSymbol?> _JsonConstructorAttributeType;

        public INamedTypeSymbol? SetsRequiredMembersAttributeType => GetOrResolveType("System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute", ref _SetsRequiredMembersAttributeType);
        private Option<INamedTypeSymbol?> _SetsRequiredMembersAttributeType;

        public INamedTypeSymbol? UnsafeAccessorAttributeType => GetOrResolveType("System.Runtime.CompilerServices.UnsafeAccessorAttribute", ref _UnsafeAccessorAttributeType);
        private Option<INamedTypeSymbol?> _UnsafeAccessorAttributeType;

        // OverloadResolutionPriorityAttribute was added in .NET 9; its presence indicates
        // the runtime also supports generic type parameters in UnsafeAccessor.
        public bool SupportsGenericUnsafeAccessors => UnsafeAccessorAttributeType is not null
            && GetOrResolveType("System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute", ref _OverloadResolutionPriorityAttributeType) is not null;
        private Option<INamedTypeSymbol?> _OverloadResolutionPriorityAttributeType;

        public INamedTypeSymbol? JsonStringEnumConverterType => GetOrResolveType("System.Text.Json.Serialization.JsonStringEnumConverter", ref _JsonStringEnumConverterType);
        private Option<INamedTypeSymbol?> _JsonStringEnumConverterType;

        public INamedTypeSymbol? JsonStringEnumConverterOfTType => GetOrResolveType("System.Text.Json.Serialization.JsonStringEnumConverter`1", ref _JsonStringEnumConverterOfTType);
        private Option<INamedTypeSymbol?> _JsonStringEnumConverterOfTType;

        public INamedTypeSymbol? IJsonOnSerializingType => GetOrResolveType(JsonConstants.IJsonOnSerializingFullName, ref _IJsonOnSerializingType);
        private Option<INamedTypeSymbol?> _IJsonOnSerializingType;

        public INamedTypeSymbol? IJsonOnSerializedType => GetOrResolveType(JsonConstants.IJsonOnSerializedFullName, ref _IJsonOnSerializedType);
        private Option<INamedTypeSymbol?> _IJsonOnSerializedType;

        // Unsupported types
        public INamedTypeSymbol? DelegateType => _DelegateType ??= Compilation.GetSpecialType(SpecialType.System_Delegate);
        private INamedTypeSymbol? _DelegateType;

        public INamedTypeSymbol? MemberInfoType => GetOrResolveType(typeof(MemberInfo), ref _MemberInfoType);
        private Option<INamedTypeSymbol?> _MemberInfoType;

        public INamedTypeSymbol? SerializationInfoType => GetOrResolveType(typeof(Runtime.Serialization.SerializationInfo), ref _SerializationInfoType);
        private Option<INamedTypeSymbol?> _SerializationInfoType;

        public INamedTypeSymbol? IntPtrType => GetOrResolveType(typeof(IntPtr), ref _IntPtrType);
        private Option<INamedTypeSymbol?> _IntPtrType;

        public INamedTypeSymbol? UIntPtrType => GetOrResolveType(typeof(UIntPtr), ref _UIntPtrType);
        private Option<INamedTypeSymbol?> _UIntPtrType;

        public INamedTypeSymbol? MemoryType => GetOrResolveType(typeof(Memory<>), ref _MemoryType);
        private Option<INamedTypeSymbol?> _MemoryType;

        public INamedTypeSymbol? ReadOnlyMemoryType => GetOrResolveType(typeof(ReadOnlyMemory<>), ref _ReadOnlyMemoryType);
        private Option<INamedTypeSymbol?> _ReadOnlyMemoryType;

        public bool IsImmutableEnumerableType(ITypeSymbol type, out string? factoryTypeFullName)
        {
            if (type is not INamedTypeSymbol { IsGenericType: true, ConstructedFrom: INamedTypeSymbol genericTypeDef })
            {
                factoryTypeFullName = null;
                return false;
            }

            SymbolEqualityComparer cmp = SymbolEqualityComparer.Default;
            if (cmp.Equals(genericTypeDef, ImmutableArrayType))
            {
                factoryTypeFullName = typeof(ImmutableArray).FullName;
                return true;
            }

            if (cmp.Equals(genericTypeDef, ImmutableListType) ||
                cmp.Equals(genericTypeDef, IImmutableListType))
            {
                factoryTypeFullName = typeof(ImmutableList).FullName;
                return true;
            }

            if (cmp.Equals(genericTypeDef, ImmutableStackType) ||
                cmp.Equals(genericTypeDef, IImmutableStackType))
            {
                factoryTypeFullName = typeof(ImmutableStack).FullName;
                return true;
            }

            if (cmp.Equals(genericTypeDef, ImmutableQueueType) ||
                cmp.Equals(genericTypeDef, IImmutableQueueType))
            {
                factoryTypeFullName = typeof(ImmutableQueue).FullName;
                return true;
            }

            if (cmp.Equals(genericTypeDef, ImmutableHashSetType) ||
                cmp.Equals(genericTypeDef, IImmutableSetType))
            {
                factoryTypeFullName = typeof(ImmutableHashSet).FullName;
                return true;
            }

            if (cmp.Equals(genericTypeDef, ImmutableSortedType))
            {
                factoryTypeFullName = typeof(ImmutableSortedSet).FullName;
                return true;
            }

            factoryTypeFullName = null;
            return false;
        }

        public bool IsImmutableDictionaryType(ITypeSymbol type, out string? factoryTypeFullName)
        {
            if (type is not INamedTypeSymbol { IsGenericType: true, ConstructedFrom: INamedTypeSymbol genericTypeDef })
            {
                factoryTypeFullName = null;
                return false;
            }

            SymbolEqualityComparer cmp = SymbolEqualityComparer.Default;

            if (cmp.Equals(genericTypeDef, ImmutableDictionaryType) ||
                cmp.Equals(genericTypeDef, IImmutableDictionaryType))
            {
                factoryTypeFullName = typeof(ImmutableDictionary).FullName;
                return true;
            }

            if (cmp.Equals(genericTypeDef, ImmutableSortedDictionaryType))
            {
                factoryTypeFullName = typeof(ImmutableSortedDictionary).FullName;
                return true;
            }

            factoryTypeFullName = null;
            return false;
        }

        private INamedTypeSymbol? GetOrResolveType(Type type, ref Option<INamedTypeSymbol?> field)
            => GetOrResolveType(type.FullName!, ref field);

        private INamedTypeSymbol? GetOrResolveType(string fullyQualifiedName, ref Option<INamedTypeSymbol?> field)
        {
            if (field.HasValue)
            {
                return field.Value;
            }

            INamedTypeSymbol? type = Compilation.GetBestTypeByMetadataName(fullyQualifiedName);
            field = new(type);
            return type;
        }

        private readonly struct Option<T>
        {
            public readonly bool HasValue;
            public readonly T Value;

            public Option(T value)
            {
                HasValue = true;
                Value = value;
            }
        }
    }
}