KnownTypeSymbols.cs 19.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
// 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.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.DotnetRuntime.Extensions;

namespace System.Text.Json.SourceGeneration
{
14
    internal sealed class KnownTypeSymbols
15
    {
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
        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? 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 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;

138 139 140 141 142 143 144 145 146
        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;

147 148 149 150 151 152
        public IArrayTypeSymbol? ByteArrayType => _ByteArrayType.HasValue
            ? _ByteArrayType.Value
            : (_ByteArrayType = new(Compilation.CreateArrayTypeSymbol(Compilation.GetSpecialType(SpecialType.System_Byte), rank: 1))).Value;

        private Option<IArrayTypeSymbol?> _ByteArrayType;

153 154 155 156 157 158 159 160 161 162 163 164
        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;

165 166 167 168 169 170 171 172
        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;
173 174

        // System.Text.Json types
175 176 177 178 179 180 181 182
        public INamedTypeSymbol? JsonConverterType => GetOrResolveType("System.Text.Json.Serialization.JsonConverter", ref _JsonConverterType);
        private Option<INamedTypeSymbol?> _JsonConverterType;

        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;
183

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

187 188 189 190 191 192 193 194 195 196 197 198 199 200
        public INamedTypeSymbol? JsonElementType => GetOrResolveType("System.Text.Json.JsonElement", ref _JsonElementType);
        private Option<INamedTypeSymbol?> _JsonElementType;

        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;
201 202

        // System.Text.Json attributes
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
        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? JsonNumberHandlingAttributeType => GetOrResolveType("System.Text.Json.Serialization.JsonNumberHandlingAttribute", ref _JsonNumberHandlingAttributeType);
        private Option<INamedTypeSymbol?> _JsonNumberHandlingAttributeType;

        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;
220

221 222 223 224 225 226
        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;

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

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

233 234 235 236 237 238
        public INamedTypeSymbol? IJsonOnSerializingType => GetOrResolveType(JsonConstants.IJsonOnSerializingFullName, ref _IJsonOnSerializingType);
        private Option<INamedTypeSymbol?> _IJsonOnSerializingType;

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

239
        // Unsupported types
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
        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;

255 256 257 258 259
        public INamedTypeSymbol? MemoryType => GetOrResolveType(typeof(Memory<>), ref _MemoryType);
        private Option<INamedTypeSymbol?> _MemoryType;

        public INamedTypeSymbol? ReadOnlyMemoryType => GetOrResolveType(typeof(ReadOnlyMemory<>), ref _ReadOnlyMemoryType);
        private Option<INamedTypeSymbol?> _ReadOnlyMemoryType;
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339

        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;
        }
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366

        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;
            }
        }
367 368
    }
}