File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\SymbolKey\SymbolKey.TupleTypeSymbolKey.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Linq;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.PooledObjects;
using System.Runtime.CompilerServices;
 
namespace Microsoft.CodeAnalysis;
 
internal partial struct SymbolKey
{
    private sealed class TupleTypeSymbolKey : AbstractSymbolKey<INamedTypeSymbol>
    {
        public static readonly TupleTypeSymbolKey Instance = new();
 
        public sealed override void Create(INamedTypeSymbol symbol, SymbolKeyWriter visitor)
        {
            Debug.Assert(symbol.IsTupleType);
 
            var isError = symbol.TupleUnderlyingType!.TypeKind == TypeKind.Error;
 
            using var _1 = ArrayBuilder<string?>.GetInstance(out var friendlyNames);
            using var _2 = ArrayBuilder<Location>.GetInstance(out var locations);
 
            foreach (var element in symbol.TupleElements)
            {
                friendlyNames.Add(element.IsImplicitlyDeclared ? null : element.Name);
                locations.Add(element.Locations.FirstOrDefault() ?? Location.None);
            }
 
            visitor.WriteBoolean(isError);
            visitor.WriteStringArray(friendlyNames.ToImmutable());
            visitor.WriteLocationArray(locations.ToImmutable());
 
            if (isError)
            {
                using var _3 = ArrayBuilder<ISymbol>.GetInstance(out var elementTypes);
 
                foreach (var element in symbol.TupleElements)
                    elementTypes.Add(element.Type);
 
                visitor.WriteSymbolKeyArray(elementTypes.ToImmutable());
            }
            else
            {
                visitor.WriteSymbolKey(symbol.TupleUnderlyingType);
            }
        }
 
        protected sealed override SymbolKeyResolution Resolve(
            SymbolKeyReader reader, INamedTypeSymbol? contextualSymbol, out string? failureReason)
        {
            contextualSymbol = contextualSymbol is { IsTupleType: true } ? contextualSymbol : null;
            var isError = reader.ReadBoolean();
 
            return isError
                ? ResolveErrorTuple(reader, contextualSymbol, out failureReason)
                : ResolveNormalTuple(reader, contextualSymbol, out failureReason);
        }
 
        private static SymbolKeyResolution ResolveNormalTuple(
            SymbolKeyReader reader, INamedTypeSymbol? contextualSymbol, out string? failureReason)
        {
            using var elementNames = reader.ReadStringArray();
            var elementLocations = ReadElementLocations(reader, out var elementLocationsFailureReason);
            var underlyingTypeResolution = reader.ReadSymbolKey(
                contextualSymbol?.TupleUnderlyingType,
                out var underlyingTypeFailureReason);
 
            if (underlyingTypeFailureReason != null)
            {
                failureReason = $"({nameof(TupleTypeSymbolKey)} {nameof(underlyingTypeResolution)} failed -> {underlyingTypeFailureReason})";
                return default;
            }
 
            using var result = PooledArrayBuilder<INamedTypeSymbol>.GetInstance();
 
            var elementNamesArray = elementNames.ToImmutable();
            foreach (var namedType in underlyingTypeResolution.OfType<INamedTypeSymbol>())
            {
                // Suppression on elementLocations due to https://github.com/dotnet/roslyn/issues/46527
                result.AddIfNotNull(reader.Compilation.CreateTupleTypeSymbol(
                    namedType, elementNamesArray, elementLocations!));
            }
 
            return CreateResolution(result, $"({nameof(TupleTypeSymbolKey)} failed)", out failureReason);
        }
 
        private static SymbolKeyResolution ResolveErrorTuple(
            SymbolKeyReader reader, INamedTypeSymbol? contextualType, out string? failureReason)
        {
            using var elementNames = reader.ReadStringArray();
            var elementLocations = ReadElementLocations(reader, out var elementLocationsFailureReason);
            using var elementTypes = reader.ReadSymbolKeyArray<INamedTypeSymbol, ITypeSymbol>(
                contextualType,
                static (contextualType, i) => SafeGet(contextualType.TupleElements, i)?.Type,
                out var elementTypesFailureReason);
 
            if (elementLocationsFailureReason != null)
            {
                failureReason = $"({nameof(TupleTypeSymbolKey)} {nameof(elementLocations)} failed -> {elementLocationsFailureReason})";
                return default;
            }
 
            if (elementTypesFailureReason != null)
            {
                failureReason = $"({nameof(TupleTypeSymbolKey)} {nameof(elementTypes)} failed -> {elementTypesFailureReason})";
                return default;
            }
 
            if (elementTypes.IsDefault)
            {
                failureReason = $"({nameof(TupleTypeSymbolKey)} {nameof(elementTypes)} failed)";
                return default;
            }
 
            // Suppression on elementLocations due to https://github.com/dotnet/roslyn/issues/46527
            var result = reader.Compilation.CreateTupleTypeSymbol(
                elementTypes.ToImmutable(), elementNames.ToImmutable(), elementLocations!);
            failureReason = null;
            return new SymbolKeyResolution(result);
        }
 
        private static ImmutableArray<Location> ReadElementLocations(SymbolKeyReader reader, out string? failureReason)
        {
            using var elementLocations = reader.ReadLocationArray(out failureReason);
            if (failureReason != null)
                return default;
 
            // Compiler API requires that all the locations are non-null, or that there is a default
            // immutable array passed in.
            if (elementLocations.Builder.All(loc => loc == null))
                return default;
 
            return elementLocations.ToImmutable()!;
        }
    }
}